第09章:Docker Compose 编排
本章目标:掌握 Docker Compose 的语法和使用,学会编排多容器应用,实现一键部署和管理。
9.1 Docker Compose 是什么
9.1.1 定义与价值
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件,你可以配置应用的所有服务、网络和存储卷,然后用一个命令创建并启动所有服务。
传统方式(多个终端分别操作):
终端1: docker run -d --name db mysql
终端2: docker run -d --name redis redis
终端3: docker run -d --name app myapp
终端4: docker network connect ...
终端5: docker run -d --name nginx nginx
Docker Compose 方式(一个文件,一条命令):
docker compose up -d
9.1.2 Compose 的优势
| 优势 | 说明 |
|---|---|
| 声明式配置 | 一个 YAML 文件描述整个应用架构 |
| 一键启停 | docker compose up/down 管理所有服务 |
| 环境隔离 | 每个 compose 项目独立的网络和卷 |
| 开发/测试/生产 | 同一配置适配不同环境 |
| 可版本控制 | compose 文件可以纳入 Git 管理 |
9.2 Compose 文件语法详解
9.2.1 文件结构
yaml
# docker-compose.yml 完整结构
version: '3.8' # Compose 文件版本
services: # 服务定义(容器配置)
web:
# ... 服务配置
db:
# ... 服务配置
networks: # 自定义网络
frontend:
backend:
volumes: # 声明命名卷
db-data:
cache-data:
configs: # 配置对象
app-config:
file: ./config.yml
secrets: # 敏感信息
db-password:
file: ./secrets/db_password.txt
9.2.2 services 配置详解
yaml
services:
# ========== 服务名称 ==========
web-app:
# 基本配置
image: myapp:latest # 使用镜像
# 或从 Dockerfile 构建
build:
context: . # 构建上下文
dockerfile: Dockerfile # Dockerfile 文件名
args: # 构建参数
VERSION: 1.0.0
target: production # 多阶段构建的目标阶段
container_name: my-web-app # 容器名称
hostname: web-server # 主机名
restart: unless-stopped # 重启策略
# 环境变量
environment:
- APP_ENV=production
- DB_HOST=db
- DB_PORT=3306
- REDIS_HOST=redis
# 从文件加载环境变量
env_file:
- .env
- .env.production
# 端口映射
ports:
- "80:80"
- "443:443"
# - "127.0.0.1:8080:80" # 仅本地访问
# - "8080:80/tcp" # 指定协议
# 卷挂载
volumes:
- ./src:/app/src:ro # 只读挂载
- app-logs:/app/logs # 命名卷
- /data/images:/app/images # 绝对路径
# 网络
networks:
- frontend
- backend
# 资源限制
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
replicas: 2 # 副本数
restart_policy:
condition: on-failure
max_attempts: 3
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
# 依赖关系
depends_on:
db:
condition: service_healthy # 等待依赖服务健康
redis:
condition: service_started
# 链接(旧语法,不推荐)
links:
- db
- redis
# 别名(DNS 名称)
aliases:
- web
- application
# 日志配置
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# 安全选项
security_opt:
- no-new-privileges:true
read_only: true # 只读文件系统
tmpfs:
- /tmp
- /var/run
# 容器内命令
command: ["python3", "app.py"]
# 或
# command: python3 app.py
# 入口点
entrypoint: ["/app/entrypoint.sh"]
# 工作目录
working_dir: /app
# 用户
user: "1000:1000"
# 其他容器
extra_hosts:
- "other-host:192.168.1.100"
# PID 模式
pid: "host"
# 配置文件
configs:
- source: app-config
target: /app/config.yml
mode: 0444
9.2.3 networks 配置详解
yaml
networks:
# 默认 bridge 网络
default:
driver: bridge
# 自定义网络
frontend:
driver: bridge
driver_opts:
com.docker.network.bridge.name: "frontend-br"
ipam:
driver: default
config:
- subnet: 192.168.100.0/24
gateway: 192.168.100.1
labels:
- "com.example.network.frontend=true"
# Overlay 网络(用于 Swarm)
backend:
driver: overlay
attachable: true
# 外部网络(使用已存在的网络)
existing-network:
external: true
9.2.4 volumes 配置详解
yaml
volumes:
# 命名卷(Docker 管理)
db-data:
driver: local
driver_opts:
type: none
device: /data/mysql
o: bind
# 带标签的卷
app-logs:
labels:
com.example.description: "Application logs"
com.example.department: "Operations"
# 外部卷(使用已存在的卷)
existing-volume:
external: true
# tmpfs 卷
temp-cache:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=100m,uid=1000
9.3 Compose 常用命令
9.3.1 服务管理
bash
# 启动所有服务
docker compose up
# 后台启动
docker compose up -d
# 重建并启动(有变更时)
docker compose up -d --build
# 停止并删除容器
docker compose down
# 停止并删除容器和卷
docker compose down -v
# 查看服务状态
docker compose ps
# 查看服务日志
docker compose logs
# 实时跟踪日志
docker compose logs -f
# 查看特定服务日志
docker compose logs -f web-app
# 查看服务资源使用
docker compose top
9.3.2 单个服务操作
bash
# 启动特定服务
docker compose up -d db
# 停止特定服务
docker compose stop web-app
# 重启特定服务
docker compose restart web-app
# 进入服务容器
docker compose exec web-app bash
docker compose exec db mysql -uroot -p
# 查看服务进程
docker compose top web-app
9.3.3 构建相关
bash
# 构建所有服务
docker compose build
# 构建特定服务
docker compose build web-app
# 不使用缓存构建
docker compose build --no-cache
# 拉取最新镜像
docker compose pull
# 推送镜像
docker compose push
9.3.4 镜像和卷管理
bash
# 列出项目中的镜像
docker compose images
# 删除停止的容器
docker compose rm
# 列出项目中的卷
docker compose volume ls
# 删除未使用的卷
docker compose down -v
9.4 环境变量管理
9.4.1 多种设置方式
yaml
services:
web-app:
# 方式1:直接在 compose 文件中设置
environment:
APP_ENV: production
DB_HOST: db
# 方式2:从文件加载
env_file:
- .env
# 方式3:使用变量替换(compose 文件中)
image: myapp:${VERSION:-latest}
# 方式4:在容器内使用
command: python3 app.py
9.4.2 .env 文件示例
bash
# .env 文件
APP_ENV=production
APP_VERSION=1.0.0
DB_ROOT_PASSWORD=supersecret
DB_NAME=myapp
DB_USER=appuser
DB_PASSWORD=apppass123
REDIS_PASSWORD=redis123
9.4.3 多环境配置
bash
# 项目结构
project/
├── docker-compose.yml # 基础配置
├── docker-compose.override.yml # 开发环境覆盖
├── docker-compose.prod.yml # 生产环境配置
├── .env # 默认环境变量
├── .env.dev # 开发环境变量
└── .env.prod # 生产环境变量
# 开发环境(默认)
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 或使用环境变量
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml docker compose up -d
9.5 完整的多容器应用示例
9.5.1 LAMP 架构(Linux + Apache + MySQL + PHP)
yaml
# docker-compose.yml
version: '3.8'
services:
# Web 服务器
apache:
image: php:8.2-apache
container_name: lamp-apache
ports:
- "80:80"
- "443:443"
volumes:
- ./src:/var/www/html
- ./apache/conf.d:/etc/apache2/sites-enabled
networks:
- lamp-network
depends_on:
mysql:
condition: service_healthy
environment:
- DB_HOST=mysql
- DB_NAME=${DB_NAME:-laravel}
- DB_USER=${DB_USER:-user}
- DB_PASSWORD=${DB_PASSWORD:-password}
restart: unless-stopped
# MySQL 数据库
mysql:
image: mysql:8.0
container_name: lamp-mysql
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpassword}
MYSQL_DATABASE: ${DB_NAME:-laravel}
MYSQL_USER: ${DB_USER:-user}
MYSQL_PASSWORD: ${DB_PASSWORD:-password}
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/initdb:/docker-entrypoint-initdb.d
networks:
- lamp-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis 缓存
redis:
image: redis:7-alpine
container_name: lamp-redis
command: redis-server --requirepass ${REDIS_PASSWORD:-redis123}
volumes:
- redis-data:/data
networks:
- lamp-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
lamp-network:
driver: bridge
volumes:
mysql-data:
redis-data:
9.5.2 Python + Nginx + Gunicorn + PostgreSQL + Redis
yaml
# docker-compose.yml
version: '3.8'
services:
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: app-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- static-files:/app/static:ro
networks:
- app-network
depends_on:
- web
restart: unless-stopped
# Python Web 应用
web:
build:
context: .
dockerfile: Dockerfile
target: production
container_name: app-web
volumes:
- static-files:/app/static
- ./logs:/app/logs
environment:
- APP_ENV=production
- DATABASE_URL=postgresql+asyncpg://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
networks:
- app-network
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
# PostgreSQL 数据库
postgres:
image: postgres:15-alpine
container_name: app-postgres
environment:
POSTGRES_DB: ${DB_NAME:-myapp}
POSTGRES_USER: ${DB_USER:-appuser}
POSTGRES_PASSWORD: ${DB_PASSWORD:-apppass123}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-appuser}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis 缓存
redis:
image: redis:7-alpine
container_name: app-redis
command: redis-server --requirepass ${REDIS_PASSWORD:-redis123}
volumes:
- redis-data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-redis123}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
static-files:
postgres-data:
redis-data:
9.5.3 Node.js + MongoDB + Redis
yaml
# docker-compose.yml
version: '3.8'
services:
# Node.js 应用
app:
build:
context: .
dockerfile: Dockerfile
container_name: node-app
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- MONGODB_URI=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
volumes:
- ./uploads:/app/uploads
networks:
- node-network
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
# MongoDB 数据库
mongo:
image: mongo:6
container_name: node-mongo
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-admin}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-password}
volumes:
- mongo-data:/data/db
- mongo-config:/data/configdb
networks:
- node-network
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis 缓存
redis:
image: redis:7-alpine
container_name: node-redis
command: redis-server --requirepass ${REDIS_PASSWORD:-redis123}
volumes:
- redis-data:/data
networks:
- node-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-redis123}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
node-network:
driver: bridge
volumes:
mongo-data:
mongo-config:
redis-data:
9.6 Compose 高级特性
9.6.1 服务扩缩容
bash
# 扩容服务(启动多个实例)
docker compose up -d --scale web=3
# 缩容
docker compose up -d --scale web=1
# 注意:使用 scale 时不要映射固定端口
# 应该使用随机端口或负载均衡器
9.6.2 服务依赖与启动顺序
yaml
services:
db:
image: mysql:8.0
# ...
redis:
image: redis:7
# ...
web:
image: myapp
depends_on:
db:
condition: service_healthy # 等待数据库健康
redis:
condition: service_started # 只需 Redis 启动
# ...
9.6.3 配置文件挂载
yaml
services:
nginx:
image: nginx:alpine
configs:
- source: nginx-conf
target: /etc/nginx/conf.d/default.conf
mode: 0444
configs:
nginx-conf:
file: ./nginx/default.conf
9.6.4 敏感信息管理
yaml
services:
db:
image: mysql:8.0
secrets:
- db-password
environment:
MYSQL_PASSWORD_FILE: /run/secrets/db-password
secrets:
db-password:
file: ./secrets/db_password.txt
9.7 动手实验
实验 9.1:WordPress 一键部署
bash
# 创建项目目录
mkdir -p ~/docker-lab/wordpress
cd ~/docker-lab/wordpress
# 创建 docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-data:/var/www/html
depends_on:
db:
condition: service_healthy
restart: unless-stopped
volumes:
db-data:
wp-data:
EOF
# 启动
docker compose up -d
# 访问 http://localhost:8080 完成 WordPress 安装
实验 9.2:多服务应用
bash
# 使用上面的 Python + Nginx + PostgreSQL + Redis 示例
cd ~/docker-lab
# 创建项目结构
mkdir -p nginx/conf.d
# 创建 Nginx 配置
cat > nginx/conf.d/default.conf << 'EOF'
upstream web {
server web:8000;
}
server {
listen 80;
server_name localhost;
location /static/ {
alias /app/static/;
}
location / {
proxy_pass http://web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
# 启动所有服务
docker compose up -d
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs -f
9.8 本章小结
| 命令 | 说明 |
|---|---|
docker compose up -d |
后台启动所有服务 |
docker compose down |
停止并删除所有服务 |
docker compose ps |
查看服务状态 |
docker compose logs -f |
实时跟踪日志 |
docker compose exec <svc> bash |
进入服务容器 |
docker compose restart <svc> |
重启指定服务 |
docker compose build |
构建服务镜像 |
docker compose pull |
拉取最新镜像 |
9.9 课后练习
- 基础题:使用 Docker Compose 部署 WordPress + MySQL。
- 进阶题:编写一个包含 Web、数据库、缓存、反向代理的完整 compose 文件。
- 实践题:使用多环境配置(dev/prod)管理不同的部署环境。
📖 下一章:企业级镜像仓库 ------ 搭建和管理私有镜像仓库