在现代微服务架构中,一个应用往往由多个相互协作的容器组成。手动管理这些容器的生命周期、网络连接和数据卷挂载不仅繁琐,而且容易出错。Docker Compose作为Docker官方推出的多容器编排工具,通过声明式的YAML配置文件,让我们能够以代码的方式定义和管理整个应用栈,实现”一键部署”和”环境一致性”。
本文将从基础概念入手,深入解析Docker Compose的核心语法,通过三个完整的实战案例展示其强大功能,并分享生产环境中的最佳实践。无论你是刚接触容器化的新手,还是希望提升部署效率的运维工程师,都能从本文中获得有价值的知识。
一、Docker Compose 基础
1.1 什么是Docker Compose
Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用YAML文件来配置应用程序的服务,然后通过单个命令创建并启动所有服务。
Compose的核心优势在于:
- 声明式配置:用YAML文件描述应用架构,比命令行参数更易读、易维护
- 一键部署:单个
docker compose up命令即可启动整个应用栈 - 环境一致性:开发、测试、生产环境使用相同的配置文件
- 服务依赖管理:自动处理服务之间的启动顺序和依赖关系
- 隔离环境:每个项目使用独立的网络命名空间,避免端口冲突
适用场景Docker Compose特别适合单主机的开发、测试环境部署。对于跨主机的生产环境集群,建议使用Kubernetes等更强大的编排工具。
1.2 Docker Compose 安装
Docker Compose现在已经集成在Docker Desktop中,Windows和Mac用户安装Docker Desktop后即可直接使用。对于Linux用户,需要单独安装:
# 下载Docker Compose v2.27.0(最新稳定版)sudo curl -L "https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 添加执行权限sudo chmod +x /usr/local/bin/docker-compose
# 创建符号链接(可选,用于兼容旧命令)sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 验证安装docker-compose --version版本选择建议从Docker Compose V2开始,官方推荐使用
docker compose(空格分隔)代替docker-compose(连字符分隔)命令。两者功能完全一致,本文将统一使用docker compose格式,更加符合Docker CLI的设计理念。
1.3 Docker Compose 工作流程
Docker Compose的工作流程非常简单:
- 编写Dockerfile:为每个服务定义构建规则
- 编写docker-compose.yml:定义应用的服务、网络和数据卷
- 运行docker compose up:启动整个应用栈
Compose会自动完成以下工作:
- 创建一个以项目目录名为前缀的隔离网络
- 构建或拉取所需的镜像
- 创建并启动所有定义的服务
- 按照依赖关系顺序启动服务
- 将服务连接到共享网络
二、docker-compose.yml 核心语法详解
docker-compose.yml是Docker Compose的核心配置文件,它使用YAML语法定义应用的各个组件。一个完整的docker-compose.yml文件通常包含以下顶级元素:
version: '3.8' # Compose文件格式版本
services: # 定义应用的服务 service1: # 服务1的配置 service2: # 服务2的配置
networks: # 定义自定义网络 network1: # 网络配置
volumes: # 定义数据卷 volume1: # 数据卷配置2.1 版本声明
Compose文件格式有多个版本,不同版本支持的功能不同。目前最常用的是3.x系列,最新稳定版是3.8。
| 版本 | 兼容性 | 主要特性 |
|---|---|---|
| 1.x | 旧版 | 不支持网络和数据卷的顶级声明 |
| 2.x | Docker Engine 1.10+ | 引入网络和数据卷的顶级声明 |
| 3.x | Docker Engine 1.13+ | 支持Swarm模式,增加了部署、健康检查等功能 |
| 3.8 | Docker Engine 19.03.0+ | 最新稳定版,支持所有现代特性 |
版本兼容性版本声明是必须的,且必须是文件的第一行。不同版本的语法有差异,使用错误的版本会导致配置失效。建议统一使用3.8版本以获得最佳特性支持。
2.2 services 配置
services是Compose文件中最重要的部分,它定义了应用的各个服务。每个服务都是一个容器的实例。下面是一个完整的服务配置示例:
services: web: image: nginx:alpine container_name: my-nginx ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - html:/usr/share/nginx/html environment: - TZ=Asia/Shanghai networks: - frontend depends_on: - db restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 30s timeout: 10s retries: 3下面详细解释每个常用指令:
2.2.1 image
指定服务使用的镜像。可以是镜像名、镜像ID,或者带有标签的镜像名。
services: # 使用官方nginx镜像,标签为alpine web: image: nginx:alpine
# 使用完整的镜像仓库地址 app: image: registry.example.com/my-app:v1.0.0
# 使用镜像ID db: image: sha256:abcdef1234562.2.2 build
如果服务需要从Dockerfile构建镜像,可以使用build指令。它可以是一个包含构建上下文的字符串,或者一个详细的配置对象。
services: app: # 简单形式:指定构建上下文为当前目录 build: .
backend: # 详细形式 build: context: ./app # 构建上下文路径 dockerfile: Dockerfile.prod # Dockerfile文件名 args: # 构建参数 - NODE_ENV=production - API_VERSION=v2 target: production # 多阶段构建的目标阶段 cache_from: # 缓存来源 - myapp:latest labels: # 镜像标签 - "com.example.version=1.0"构建缓存优化使用
cache_from可以显著加速构建过程,特别是在CI/CD环境中。配合多阶段构建可以进一步减小镜像体积。
2.2.3 ports
映射容器端口到主机端口。格式为主机端口:容器端口,或者只指定容器端口(主机端口随机分配)。
services: web: image: nginx ports: - "80:80" # 映射主机80端口到容器80端口 - "443:443" # 映射主机443端口到容器443端口 - "8080:8080/tcp" # 指定协议为TCP - "53:53/udp" # 指定协议为UDP - "3000" # 随机分配主机端口到容器3000端口 - "127.0.0.1:5000:5000" # 仅绑定到本地回环地址端口格式说明端口映射必须用引号括起来,否则YAML会将
53:53解析为时间格式,导致错误。如需限制访问来源,可以指定绑定的IP地址。
2.2.4 volumes
挂载数据卷到容器。可以是命名卷、主机路径挂载,或者临时文件系统。
services: db: image: mysql:8.0 volumes: # 命名卷挂载 - mysql_data:/var/lib/mysql
# 主机路径挂载(相对路径) - ./conf:/etc/mysql/conf.d
# 主机路径挂载(绝对路径) - /data/mysql/logs:/var/log/mysql
# 只读挂载 - ./my.cnf:/etc/mysql/my.cnf:ro
# 临时文件系统(数据不持久化) - /tmp
# 高级配置 - type: volume source: mysql_data target: /var/lib/mysql volume: nocopy: true
volumes: mysql_data: # 声明命名卷 driver: local2.2.5 environment
设置容器的环境变量。可以是列表形式或字典形式。
services: db: image: mysql:8.0
# 列表形式 environment: - MYSQL_ROOT_PASSWORD=root123 - MYSQL_DATABASE=myapp - MYSQL_USER=user - MYSQL_PASSWORD=pass123
app: image: myapp # 字典形式(推荐,更清晰) environment: NODE_ENV: production API_KEY: ${API_KEY} # 从.env文件读取 DB_HOST: db DB_PORT: "3306"2.2.6 depends_on
指定服务之间的依赖关系,决定服务的启动顺序。
services: web: image: nginx depends_on: - app - db
app: build: ./app depends_on: db: condition: service_healthy # 等待db健康检查通过
db: image: mysql:8.0 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5启动顺序与就绪状态
depends_on只保证容器的启动顺序,不保证容器内的应用完全就绪。例如,MySQL容器启动后,数据库服务可能还需要几秒钟才能接受连接。推荐配合健康检查使用,或在应用中实现连接重试逻辑。
2.2.7 restart
指定容器的重启策略。
services: web: image: nginx restart: unless-stopped # 除非手动停止,否则总是重启(推荐生产环境)
app: image: myapp restart: always # 总是重启
worker: image: worker restart: on-failure # 仅在失败时重启
test: image: test restart: "no" # 不重启(默认,注意要加引号)2.2.8 container_name
指定容器的名称。默认情况下,Compose会使用项目名_服务名_序号的格式命名容器。
services: web: image: nginx container_name: my-nginx扩展限制如果指定了container_name,那么该服务将无法进行扩展(scale),因为容器名称必须是唯一的。如果需要扩展服务,请勿设置container_name。
2.2.9 networks
指定服务连接的网络。
services: web: image: nginx networks: frontend: ipv4_address: 172.20.0.2 # 指定静态IP backend: aliases: # 网络别名 - web-server
app: build: ./app networks: - backend
db: image: mysql:8.0 networks: - backend
networks: frontend: driver: bridge backend: driver: bridge internal: true # 内部网络,隔离外部访问2.3 networks 配置
networks顶级元素用于定义自定义网络。默认情况下,Compose会为每个项目创建一个桥接网络,所有服务都会连接到这个网络。
networks: # 默认桥接网络 frontend:
# 指定驱动为bridge backend: driver: bridge
# 自定义子网和网关 custom: driver: bridge ipam: driver: default config: - subnet: 172.20.0.0/16 gateway: 172.20.0.1 ip_range: 172.20.5.0/24
# 使用已存在的外部网络 external_network: external: true name: my-pre-existing-network
# overlay网络(用于Swarm模式) overlay: driver: overlay attachable: true2.4 volumes 配置
volumes顶级元素用于定义命名卷。命名卷可以在多个服务之间共享,并且在容器删除后仍然保留数据。
volumes: # 默认驱动的命名卷 mysql_data:
# 指定驱动为local postgres_data: driver: local driver_opts: type: none o: bind device: /data/postgres
# 使用已存在的外部卷 external_volume: external: true name: my-external-volume
# NFS挂载 nfs_data: driver: local driver_opts: type: nfs o: addr=10.0.0.1,rw device: ":/exported/path"三、Docker Compose 常用命令
Docker Compose提供了丰富的命令来管理应用的整个生命周期。下面是最常用的命令:
3.1 启动应用
# 启动所有服务(前台运行,可以看到实时日志)docker compose up
# 启动所有服务(后台运行)docker compose up -d
# 构建镜像并启动服务docker compose up --build
# 强制重新创建容器docker compose up --force-recreate
# 只启动指定服务及其依赖docker compose up web
# 启动时移除孤立容器docker compose up --remove-orphans3.2 查看状态
# 查看所有服务的状态docker compose ps
# 查看所有服务(包括已停止的)docker compose ps -a
# 查看服务的日志docker compose logs
# 实时查看服务的日志docker compose logs -f
# 查看指定服务的日志docker compose logs web
# 查看最后100行日志docker compose logs --tail=100
# 显示时间戳docker compose logs -t3.3 停止和删除
# 停止所有服务(不删除容器和数据卷)docker compose stop
# 停止指定服务docker compose stop web
# 启动已停止的服务docker compose start
# 重启所有服务docker compose restart
# 删除所有容器和网络docker compose down
# 删除所有容器、网络和数据卷docker compose down -v
# 删除所有容器、网络和镜像docker compose down --rmi all
# 暂停服务docker compose pause
# 恢复暂停的服务docker compose unpause3.4 执行命令
# 在运行中的容器中执行命令docker compose exec web bash
# 以root用户执行docker compose exec -u root web bash
# 在新容器中执行命令(不会影响原容器)docker compose run web bash
# 执行一次性命令docker compose run --rm web python manage.py migrate3.5 扩展服务
# 将web服务扩展到3个实例docker compose up -d --scale web=3
# 同时扩展多个服务docker compose up -d --scale web=3 --scale worker=5
# 查看扩展后的服务docker compose ps3.6 其他常用命令
# 验证docker-compose.yml语法docker compose config
# 显示合并后的配置docker compose config --services
# 查看镜像docker compose images
# 查看服务端口docker compose port web 80
# 查看服务进程docker compose top
# 查看服务事件docker compose events四、实战案例1:LNMP环境部署
LNMP(Linux + Nginx + MySQL + PHP)是最常用的Web服务器环境之一。下面我们使用Docker Compose来部署一个完整的LNMP环境。
4.1 项目结构
lnmp/├── docker-compose.yml├── nginx/│ ├── nginx.conf│ └── logs/├── php/│ ├── Dockerfile│ └── php.ini├── mysql/│ ├── conf/│ │ └── my.cnf│ └── logs/└── www/ └── index.php4.2 docker-compose.yml 配置
version: '3.8'
services: nginx: image: nginx:alpine container_name: lnmp-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./www:/var/www/html - ./nginx/logs:/var/log/nginx depends_on: - php networks: - lnmp-network restart: unless-stopped healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost"] interval: 30s timeout: 10s retries: 3
php: build: ./php container_name: lnmp-php volumes: - ./www:/var/www/html - ./php/php.ini:/usr/local/etc/php/php.ini:ro networks: - lnmp-network restart: unless-stopped environment: - TZ=Asia/Shanghai
mysql: image: mysql:8.0 container_name: lnmp-mysql ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d:ro - ./mysql/logs:/var/log/mysql environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123456} MYSQL_DATABASE: ${MYSQL_DATABASE:-lnmp} MYSQL_USER: ${MYSQL_USER:-lnmp} MYSQL_PASSWORD: ${MYSQL_PASSWORD:-lnmp123456} TZ: Asia/Shanghai networks: - lnmp-network restart: unless-stopped healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 5 command: --default-authentication-plugin=mysql_native_password
networks: lnmp-network: driver: bridge
volumes: mysql_data: driver: local4.3 PHP Dockerfile
FROM php:8.2-fpm-alpine
# 安装系统依赖RUN apk add --no-cache \ libpng-dev \ libjpeg-turbo-dev \ freetype-dev \ zip \ libzip-dev
# 安装PHP扩展RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) \ pdo_mysql \ mysqli \ gd \ zip \ opcache
# 安装Redis扩展RUN pecl install redis \ && docker-php-ext-enable redis
# 设置时区ENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 设置工作目录WORKDIR /var/www/html
# 优化PHP-FPM配置RUN echo "pm.max_children = 50" >> /usr/local/etc/php-fpm.d/www.conf \ && echo "pm.start_servers = 10" >> /usr/local/etc/php-fpm.d/www.conf \ && echo "pm.min_spare_servers = 5" >> /usr/local/etc/php-fpm.d/www.conf \ && echo "pm.max_spare_servers = 20" >> /usr/local/etc/php-fpm.d/www.conf4.4 Nginx 配置
user nginx;worker_processes auto;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;
events { worker_connections 2048; use epoll; multi_accept on;}
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; client_max_body_size 20M;
# Gzip压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
server { listen 80; server_name localhost; root /var/www/html; index index.php index.html index.htm;
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ \.php$ { fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;
# FastCGI优化 fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; }
location ~ /\.ht { deny all; }
# 静态文件缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 7d; add_header Cache-Control "public, immutable"; } }}4.5 测试页面
<?php// 显示PHP信息phpinfo();
// 测试MySQL连接try { $pdo = new PDO( 'mysql:host=mysql;dbname=lnmp', 'lnmp', 'lnmp123456' ); echo "<h2>MySQL连接成功!</h2>"; echo "<p>MySQL版本:" . $pdo->getAttribute(PDO::ATTR_SERVER_VERSION) . "</p>";} catch (PDOException $e) { echo "<h2>MySQL连接失败!</h2>"; echo "<p>错误信息:" . $e->getMessage() . "</p>";}?>4.6 启动服务
# 进入项目目录cd lnmp
# 创建必要的目录mkdir -p nginx/logs mysql/logs
# 启动服务docker compose up -d
# 查看服务状态docker compose ps
# 查看日志docker compose logs -f访问 http://localhost ,你应该能看到PHP信息页面和MySQL连接成功的提示。
生产环境建议生产环境部署时,建议将敏感信息(如数据库密码)存放在
.env文件中,并在.gitignore中排除该文件。同时启用HTTPS,配置防火墙规则,限制MySQL端口仅允许内部访问。
五、实战案例2:WordPress + MySQL 部署
WordPress是最流行的内容管理系统之一。下面我们使用Docker Compose来部署一个WordPress网站。
5.1 项目结构
wordpress/├── docker-compose.yml├── .env└── .gitignore5.2 docker-compose.yml 配置
version: '3.8'
services: wordpress: image: wordpress:latest container_name: wordpress ports: - "80:80" volumes: - wordpress_data:/var/www/html - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_NAME: ${MYSQL_DATABASE} WORDPRESS_DB_USER: ${MYSQL_USER} WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD} WORDPRESS_TABLE_PREFIX: wp_ WORDPRESS_DEBUG: ${WORDPRESS_DEBUG:-false} depends_on: db: condition: service_healthy networks: - wordpress-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 30s timeout: 10s retries: 3
db: image: mysql:8.0 container_name: wordpress-db volumes: - db_data:/var/lib/mysql - ./mysql-init:/docker-entrypoint-initdb.d:ro environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} TZ: Asia/Shanghai networks: - wordpress-network restart: unless-stopped healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 5 start_period: 30s command: - --default-authentication-plugin=mysql_native_password - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci
# 可选:添加Redis缓存 redis: image: redis:alpine container_name: wordpress-redis networks: - wordpress-network restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3
networks: wordpress-network: driver: bridge
volumes: wordpress_data: driver: local db_data: driver: local5.3 环境变量文件
# MySQL配置MYSQL_ROOT_PASSWORD=StrongRootPassword123!MYSQL_DATABASE=wordpressMYSQL_USER=wpuserMYSQL_PASSWORD=WpUserPassword123!
# WordPress配置WORDPRESS_DEBUG=false5.4 PHP上传限制配置
file_uploads = Onmemory_limit = 256Mupload_max_filesize = 64Mpost_max_size = 64Mmax_execution_time = 3005.5 .gitignore 配置
.env*.log.DS_Store5.6 启动服务
# 进入项目目录cd wordpress
# 启动服务docker compose up -d
# 查看启动日志docker compose logs -f
# 等待所有服务健康docker compose ps访问 http://localhost ,按照提示完成WordPress的安装。
数据备份建议定期备份WordPress数据和MySQL数据库。可以使用以下命令:
Terminal window # 备份WordPress文件docker compose exec wordpress tar czf /tmp/wp-backup.tar.gz /var/www/html# 备份MySQL数据库docker compose exec db mysqldump -uroot -p$MYSQL_ROOT_PASSWORD wordpress > backup.sql
六、实战案例3:微服务架构示例
下面我们使用Docker Compose来部署一个简单的微服务架构,包含前端、后端API、数据库和缓存服务。
6.1 项目结构
microservices/├── docker-compose.yml├── docker-compose.prod.yml├── .env├── frontend/│ ├── Dockerfile│ └── ...├── backend/│ ├── Dockerfile│ └── ...└── nginx/ └── nginx.conf6.2 docker-compose.yml 配置
version: '3.8'
services: # Nginx反向代理 nginx: image: nginx:alpine container_name: ms-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/nginx/ssl:ro depends_on: - frontend - backend networks: - frontend-network restart: unless-stopped
# 前端服务 frontend: build: ./frontend container_name: ms-frontend environment: - API_URL=http://nginx/api - NODE_ENV=production networks: - frontend-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3
# 后端API服务 backend: build: context: ./backend args: NODE_ENV: production container_name: ms-backend environment: NODE_ENV: production DB_HOST: db DB_PORT: 5432 DB_NAME: ${POSTGRES_DB} DB_USER: ${POSTGRES_USER} DB_PASSWORD: ${POSTGRES_PASSWORD} REDIS_HOST: redis REDIS_PORT: 6379 JWT_SECRET: ${JWT_SECRET} depends_on: db: condition: service_healthy redis: condition: service_healthy networks: - backend-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: cpus: '1' memory: 1G reservations: cpus: '0.5' memory: 512M
# PostgreSQL数据库 db: image: postgres:15-alpine container_name: ms-db volumes: - postgres_data:/var/lib/postgresql/data - ./db/init:/docker-entrypoint-initdb.d:ro environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} PGDATA: /var/lib/postgresql/data/pgdata TZ: Asia/Shanghai networks: - backend-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5 start_period: 10s shm_size: 256mb
# Redis缓存 redis: image: redis:alpine container_name: ms-redis command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data networks: - backend-network restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 10s timeout: 3s retries: 5
networks: frontend-network: driver: bridge backend-network: driver: bridge internal: true # 内部网络,增强安全性
volumes: postgres_data: driver: local redis_data: driver: local6.3 生产环境覆盖配置
version: '3.8'
services: nginx: volumes: - ./nginx/nginx.prod.conf:/etc/nginx/nginx.conf:ro logging: driver: "json-file" options: max-size: "10m" max-file: "3"
frontend: build: context: ./frontend dockerfile: Dockerfile.prod logging: driver: "json-file" options: max-size: "10m" max-file: "3"
backend: build: context: ./backend dockerfile: Dockerfile.prod deploy: replicas: 3 resources: limits: cpus: '2' memory: 2G logging: driver: "json-file" options: max-size: "10m" max-file: "3"
db: volumes: - /data/postgres:/var/lib/postgresql/data deploy: resources: limits: cpus: '2' memory: 4G logging: driver: "json-file" options: max-size: "10m" max-file: "5"
redis: deploy: resources: limits: cpus: '1' memory: 1G logging: driver: "json-file" options: max-size: "5m" max-file: "3"6.4 环境变量文件
# PostgreSQL配置POSTGRES_DB=microservicesPOSTGRES_USER=apiPOSTGRES_PASSWORD=SecurePostgresPassword123!
# Redis配置REDIS_PASSWORD=SecureRedisPassword123!
# JWT密钥JWT_SECRET=YourSuperSecretJWTKey123456!6.5 启动服务
# 开发环境cd microservicesdocker compose up -d
# 生产环境docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 查看服务状态docker compose ps
# 查看日志docker compose logs -f backend
# 扩展后端服务docker compose up -d --scale backend=5七、Docker Compose 高级技巧
7.1 环境变量使用
Docker Compose支持多种方式使用环境变量:
7.1.1 .env 文件
Compose会自动加载项目目录下的.env文件中的环境变量。
# 镜像版本TAG=v1.0.0NODE_VERSION=18-alpine
# 数据库配置DB_HOST=dbDB_PORT=5432DB_PASSWORD=SecurePassword123!
# 应用配置APP_PORT=3000LOG_LEVEL=infoservices: app: image: myapp:${TAG:-latest} build: context: . args: NODE_VERSION: ${NODE_VERSION} ports: - "${APP_PORT}:3000" environment: DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_PASSWORD: ${DB_PASSWORD} LOG_LEVEL: ${LOG_LEVEL:-warn}默认值语法使用
${VAR:-default}语法可以设置默认值,如果环境变量未定义则使用默认值。这在处理可选配置时非常有用。
7.1.2 env_file 指令
services: app: image: myapp env_file: - .env # 基础配置 - .env.local # 本地覆盖 - .env.secret # 敏感信息(不提交到Git)7.2 配置覆盖
Docker Compose支持多个配置文件,允许你覆盖基础配置。这是实现多环境部署的最佳实践。
version: '3.8'
services: app: build: target: development volumes: - .:/app - /app/node_modules environment: NODE_ENV: development DEBUG: "app:*" command: npm run dev# 开发环境(自动加载docker-compose.override.yml)docker compose up -d
# 测试环境docker compose -f docker-compose.yml -f docker-compose.test.yml up -d
# 生产环境docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 查看合并后的配置docker compose -f docker-compose.yml -f docker-compose.prod.yml config7.3 健康检查详解
健康检查可以确保容器内的应用正常运行,Compose会根据健康检查结果决定服务是否就绪。
services: # HTTP服务健康检查 web: image: nginx healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s # 检查间隔 timeout: 10s # 超时时间 retries: 3 # 失败重试次数 start_period: 40s # 启动等待时间
# 数据库健康检查 db: image: postgres:15 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5
# Redis健康检查 redis: image: redis:alpine healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 3
# 自定义脚本健康检查 app: build: . healthcheck: test: ["CMD", "/app/healthcheck.sh"] interval: 30s timeout: 10s retries: 37.4 服务扩展策略
# 扩展web服务到5个实例docker compose up -d --scale web=5
# 同时扩展多个服务docker compose up -d --scale web=5 --scale worker=10
# 动态调整规模docker compose up -d --scale web=3 # 缩减到3个
# 查看扩展后的服务docker compose psservices: web: build: ./web # 不要设置container_name ports: - "8080-8089:80" # 使用端口范围 networks: - webnet deploy: replicas: 3 resources: limits: cpus: '0.5' memory: 512M7.5 配置文件验证和调试
# 验证docker-compose.yml语法docker compose config
# 查看服务列表docker compose config --services
# 查看数据卷列表docker compose config --volumes
# 显示合并后的完整配置docker compose config --resolve-image-digests
# 静默模式(只在有错误时输出)docker compose config --quiet
# 检查特定服务配置docker compose config --services web7.6 日志管理
services: app: image: myapp logging: driver: "json-file" options: max-size: "10m" # 单个日志文件最大大小 max-file: "3" # 保留的日志文件数量 compress: "true" # 压缩旧日志
db: image: postgres logging: driver: "syslog" options: syslog-address: "tcp://192.168.0.42:123" tag: "{{.Name}}"# 查看所有服务日志docker compose logs
# 实时跟踪日志docker compose logs -f
# 查看最后100行docker compose logs --tail=100
# 显示时间戳docker compose logs -t
# 只看指定服务docker compose logs web db
# 从某个时间点开始docker compose logs --since 2024-01-01T00:00:00
# 到某个时间点结束docker compose logs --until 2024-01-02T00:00:00八、生产环境部署最佳实践
8.1 服务编排策略
在生产环境中部署Docker Compose应用时,需要考虑服务的启动顺序、依赖关系和故障恢复。
services: # 负载均衡器 lb: image: nginx:alpine depends_on: app: condition: service_healthy restart: always deploy: resources: limits: cpus: '0.5' memory: 256M
# 应用服务 app: build: ./app depends_on: db: condition: service_healthy redis: condition: service_started restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s
# 数据库(带初始化等待) db: image: postgres:15 restart: always healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 start_period: 30s启动顺序最佳实践
- 优先启动无依赖的基础服务(数据库、缓存)
- 使用健康检查确保服务真正可用
- 设置合理的
start_period给服务充足的初始化时间- 应用层实现连接重试机制作为额外保障
8.2 安全性配置
services: app: image: myapp # 限制容器权限 cap_drop: - ALL cap_add: - NET_BIND_SERVICE
# 以非root用户运行 user: "1000:1000"
# 只读根文件系统 read_only: true tmpfs: - /tmp - /var/run
# 安全选项 security_opt: - no-new-privileges:true
# 限制系统调用 sysctls: net.ipv4.ip_unprivileged_port_start: 0
# 资源限制 deploy: resources: limits: cpus: '2' memory: 2G pids: 100 reservations: cpus: '1' memory: 1G
# 数据库安全配置 db: image: postgres:15 environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password secrets: - db_password networks: backend: aliases: - database
secrets: db_password: file: ./secrets/db_password.txt
networks: backend: internal: true # 内部网络,不对外暴露8.3 监控和告警
services: # Prometheus监控 prometheus: image: prom/prometheus container_name: prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--storage.tsdb.retention.time=30d' ports: - "9090:9090" networks: - monitoring restart: unless-stopped
# Grafana可视化 grafana: image: grafana/grafana container_name: grafana volumes: - grafana_data:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning:ro environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD} GF_INSTALL_PLUGINS: grafana-clock-panel ports: - "3000:3000" networks: - monitoring restart: unless-stopped depends_on: - prometheus
# Node Exporter(主机监控) node-exporter: image: prom/node-exporter container_name: node-exporter command: - '--path.rootfs=/host' volumes: - '/:/host:ro,rslave' networks: - monitoring restart: unless-stopped
# cAdvisor(容器监控) cadvisor: image: gcr.io/cadvisor/cadvisor container_name: cadvisor volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro ports: - "8080:8080" networks: - monitoring restart: unless-stopped
volumes: prometheus_data: grafana_data:
networks: monitoring: driver: bridge8.4 备份和恢复策略
#!/bin/bash# backup.sh - Docker Compose数据备份脚本
BACKUP_DIR="/backups"DATE=$(date +%Y%m%d_%H%M%S)PROJECT_NAME="myapp"
# 创建备份目录mkdir -p ${BACKUP_DIR}/${DATE}
# 备份数据卷echo "备份数据卷..."docker run --rm \ -v ${PROJECT_NAME}_postgres_data:/data:ro \ -v ${BACKUP_DIR}/${DATE}:/backup \ alpine tar czf /backup/postgres_data.tar.gz -C /data .
docker run --rm \ -v ${PROJECT_NAME}_redis_data:/data:ro \ -v ${BACKUP_DIR}/${DATE}:/backup \ alpine tar czf /backup/redis_data.tar.gz -C /data .
# 备份PostgreSQL数据库echo "备份PostgreSQL数据库..."docker compose exec -T db pg_dumpall -U postgres | gzip > ${BACKUP_DIR}/${DATE}/db_dump.sql.gz
# 备份配置文件echo "备份配置文件..."tar czf ${BACKUP_DIR}/${DATE}/configs.tar.gz \ docker-compose.yml \ docker-compose.prod.yml \ .env \ nginx/
# 清理旧备份(保留最近7天)find ${BACKUP_DIR} -type d -mtime +7 -exec rm -rf {} +
echo "备份完成: ${BACKUP_DIR}/${DATE}"#!/bin/bash# restore.sh - Docker Compose数据恢复脚本
BACKUP_DATE=$1BACKUP_DIR="/backups/${BACKUP_DATE}"
if [ ! -d "${BACKUP_DIR}" ]; then echo "备份目录不存在: ${BACKUP_DIR}" exit 1fi
# 停止服务echo "停止服务..."docker compose down
# 恢复数据卷echo "恢复数据卷..."docker run --rm \ -v myapp_postgres_data:/data \ -v ${BACKUP_DIR}:/backup \ alpine tar xzf /backup/postgres_data.tar.gz -C /data
docker run --rm \ -v myapp_redis_data:/data \ -v ${BACKUP_DIR}:/backup \ alpine tar xzf /backup/redis_data.tar.gz -C /data
# 恢复配置文件echo "恢复配置文件..."tar xzf ${BACKUP_DIR}/configs.tar.gz
# 启动服务echo "启动服务..."docker compose up -d
echo "恢复完成"8.5 性能优化
services: # Web服务优化 web: image: nginx:alpine volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - static:/usr/share/nginx/html:ro deploy: resources: limits: cpus: '2' memory: 1G reservations: cpus: '1' memory: 512M # 启用内核参数优化 sysctls: net.core.somaxconn: 1024 net.ipv4.tcp_syncookies: 1
# 应用服务优化 app: build: context: ./app cache_from: - myregistry/myapp:latest environment: NODE_ENV: production NODE_OPTIONS: "--max-old-space-size=2048" # 使用tmpfs提升临时文件性能 tmpfs: - /app/tmp:size=100M - /app/cache:size=500M # CPU亲和性 cpuset: "0-3"
# 数据库性能优化 db: image: postgres:15 command: - postgres - -c max_connections=200 - -c shared_buffers=256MB - -c effective_cache_size=1GB - -c maintenance_work_mem=64MB - -c checkpoint_completion_target=0.9 - -c wal_buffers=16MB - -c default_statistics_target=100 shm_size: 512mb volumes: - postgres_data:/var/lib/postgresql/data - type: tmpfs target: /dev/shm tmpfs: size: 512M
# Redis优化 redis: image: redis:alpine command: > redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru --save "" --appendonly yes --appendfsync everysec volumes: - redis_data:/data性能调优建议
- 资源限制:合理设置CPU和内存限制,避免单个容器占用过多资源
- tmpfs使用:将临时文件和缓存放在内存文件系统中
- 网络优化:调整内核参数提升网络吞吐
- 数据库调优:根据实际负载调整数据库参数
- 镜像缓存:使用
cache_from加速构建过程
九、故障排查和调试
9.1 常见问题诊断
# 查看容器状态docker compose ps -a
# 查看容器资源使用docker compose stats
# 查看详细的容器信息docker compose inspect web
# 查看服务事件docker compose events
# 查看网络信息docker network lsdocker network inspect myapp_default
# 查看数据卷docker volume lsdocker volume inspect myapp_postgres_data
# 进入容器调试docker compose exec web shdocker compose exec -u root web bash
# 查看容器进程docker compose top
# 查看端口映射docker compose port web 809.2 日志分析
# 查看错误日志docker compose logs | grep -i error
# 分析慢查询docker compose logs db | grep "duration:"
# 统计错误频率docker compose logs --since 1h | grep -c "ERROR"
# 导出日志到文件docker compose logs --no-color > app.log
# 实时过滤特定服务日志docker compose logs -f web | grep "status=500"
# 查看启动失败的容器日志docker compose logs $(docker compose ps -q --filter "status=exited")9.3 性能分析
# 查看容器资源实时使用docker compose stats
# 查看容器进程docker compose top web
# 进入容器执行性能分析docker compose exec web topdocker compose exec web htopdocker compose exec web iotop
# 查看网络连接docker compose exec web netstat -tunlp
# 数据库性能分析docker compose exec db psql -U postgres -c "SELECT * FROM pg_stat_activity;"
# 查看容器磁盘使用docker compose exec web df -h十、常见问题与解决方案
10.1 端口冲突
问题:启动时提示端口被占用
Error: Bind for 0.0.0.0:80 failed: port is already allocated解决方案:
services: web: image: nginx ports: - "8080:80" # 将主机8080端口映射到容器80端口或者查找并停止占用端口的进程:
# Linux/Macsudo lsof -i :80sudo kill -9 <PID>
# Windowsnetstat -ano | findstr :80taskkill /PID <PID> /F10.2 服务启动顺序问题
问题:应用启动时数据库还未就绪
解决方案:使用健康检查和重试机制
services: app: build: ./app depends_on: db: condition: service_healthy restart: on-failure environment: DB_RETRY_ATTEMPTS: 10 DB_RETRY_INTERVAL: 5
db: image: postgres:15 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 start_period: 30s应用层实现重试(Node.js示例):
const { Client } = require('pg');
async function connectWithRetry(maxRetries = 10, interval = 5000) { for (let i = 0; i < maxRetries; i++) { try { const client = new Client({ host: process.env.DB_HOST, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, }); await client.connect(); console.log('Database connected successfully'); return client; } catch (error) { console.log(`Connection attempt ${i + 1} failed: ${error.message}`); if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, interval)); } } } throw new Error('Failed to connect to database after retries');}10.3 数据持久化问题
问题:容器删除后数据丢失
解决方案:正确使用命名卷
services: db: image: mysql:8.0 volumes: # 正确:使用命名卷 - mysql_data:/var/lib/mysql # 错误:直接使用容器路径 # - /var/lib/mysql
volumes: mysql_data: driver: local # 可选:指定宿主机路径 driver_opts: type: none o: bind device: /data/mysql数据卷注意事项
- 命名卷在
docker compose down -v时才会被删除- 匿名卷会在容器删除时一同删除
- 主机路径挂载受宿主机文件权限影响
- 生产环境建议使用命名卷并定期备份
10.4 网络连接问题
问题:服务间无法互相访问
解决方案:
services: web: image: nginx networks: - frontend # 确保服务在同一网络中
app: image: myapp networks: - frontend - backend
db: image: postgres networks: - backend
networks: frontend: driver: bridge backend: driver: bridge# 查看网络配置docker network inspect myapp_frontend
# 测试服务间连通性docker compose exec web ping appdocker compose exec app nc -zv db 5432
# 查看DNS解析docker compose exec web nslookup db10.5 镜像构建缓存问题
问题:代码修改后镜像未更新
解决方案:
# 清除构建缓存并重新构建docker compose build --no-cache
# 删除所有镜像重新构建docker compose down --rmi alldocker compose up -d --build
# 清理Docker系统缓存docker system prune -a十一、CI/CD集成
11.1 GitLab CI配置
stages: - build - test - deploy
variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "/certs"
before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build: stage: build script: - docker compose build - docker compose push only: - main - develop
test: stage: test script: - docker compose up -d - docker compose exec -T app npm test - docker compose down -v only: - main - develop
deploy_staging: stage: deploy script: - scp docker-compose.yml $STAGING_SERVER:/app/ - ssh $STAGING_SERVER "cd /app && docker compose pull && docker compose up -d" only: - develop environment: name: staging
deploy_production: stage: deploy script: - scp docker-compose.yml docker-compose.prod.yml $PROD_SERVER:/app/ - ssh $PROD_SERVER "cd /app && docker compose -f docker-compose.yml -f docker-compose.prod.yml pull" - ssh $PROD_SERVER "cd /app && docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d" only: - main environment: name: production when: manual11.2 GitHub Actions配置
name: Deploy to Production
on: push: branches: [main] pull_request: branches: [main]
jobs: build-and-test: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v3
- name: Set up Docker Buildx uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build images run: docker compose build
- name: Start services run: docker compose up -d
- name: Run tests run: docker compose exec -T app npm test
- name: Stop services run: docker compose down -v
deploy: needs: build-and-test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main'
steps: - uses: actions/checkout@v3
- name: Deploy to production uses: appleboy/ssh-action@master with: host: ${{ secrets.PROD_HOST }} username: ${{ secrets.PROD_USER }} key: ${{ secrets.PROD_SSH_KEY }} script: | cd /app git pull origin main docker compose -f docker-compose.yml -f docker-compose.prod.yml pull docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d docker system prune -f十二、总结
Docker Compose是多容器应用部署的强大工具,它通过声明式的YAML配置文件,让我们能够轻松定义和管理整个应用栈。本文从基础概念入手,详细解析了Docker Compose的核心语法和常用命令,并通过三个完整的实战案例展示了其在实际项目中的应用。
通过本文的学习,你应该已经掌握了:
✅ 基础知识:Docker Compose的工作原理、安装方法和基本概念 ✅ 核心语法:docker-compose.yml文件的完整配置项和使用技巧 ✅ 实战能力:LNMP、WordPress和微服务架构的完整部署方案 ✅ 高级技巧:环境变量管理、配置覆盖、健康检查和服务扩展 ✅ 生产实践:安全配置、性能优化、监控告警和备份恢复策略 ✅ 问题解决:常见故障的诊断方法和解决方案 ✅ CI/CD集成:自动化部署流水线配置
在实际工作中,Docker Compose主要用于单主机的开发和测试环境,以及小规模的生产环境部署。对于大规模分布式系统,建议使用Kubernetes等更强大的容器编排平台。但无论如何,Docker Compose都是容器化技术学习和应用的重要一步,它的设计理念和最佳实践对于理解整个容器生态系统都有重要意义。
下一步学习建议
- 深入学习Docker网络:理解bridge、host、overlay等网络模式
- 掌握Docker Swarm:学习Docker原生的集群编排工具
- 学习Kubernetes:向大规模容器编排平台进阶
- 研究服务网格:了解Istio、Linkerd等微服务治理方案
- 实践DevOps:将容器化集成到完整的CI/CD流水线中