Docker & Kubernetes 完全掌握 第4篇: 使用 Docker Compose 管理多容器
Docker & Kubernetes Complete Guide Part 4: Multi-Container Management with Docker Compose
前言:Docker Compose 的必要性
实际应用程序大多由多个服务组成。Web 服务器、数据库、缓存、消息队列等各种组件需要协同工作。用单独的 docker run 命令管理这样的多容器环境既繁琐又容易出错。
Docker Compose 是解决这个问题的工具,它允许用一个 YAML 文件定义多个容器,并用一条命令管理整个应用程序栈。
1. 什么是 Docker Compose?
1.1 Docker Compose 的定义
Docker Compose 是用于定义和运行多容器 Docker 应用程序的工具。在 docker-compose.yml 文件中声明式地定义应用程序的服务、网络和卷。
Docker Compose 的主要特点:
- 声明式定义:用 YAML 文件定义整个应用程序栈
- 单命令管理:用
docker compose up启动所有服务 - 环境隔离:为每个项目创建独立的网络
- 变量支持:通过环境变量和
.env文件进行灵活配置 - 开发便利性:特别适用于本地开发环境配置
1.2 Docker Compose V2
Docker Desktop 和最新的 Docker Engine 默认包含 Compose V2。V2 用 Go 重写,使用 docker compose(无连字符)格式。
# V1(旧版)
docker-compose up
# V2(推荐)
docker compose up
2. docker-compose.yml 结构
2.1 基本结构
# docker-compose.yml
version: "3.9" # Compose 文件版本(可选)
services: # 服务(容器)定义
web:
image: nginx:alpine
ports:
- "80:80"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes: # 卷定义(可选)
db-data:
networks: # 网络定义(可选)
backend:
2.2 最小配置示例
# 最简单的 docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
3. 服务定义详解
3.1 image - 指定镜像
services:
web:
# Docker Hub 镜像
image: nginx:alpine
db:
# 仓库镜像
image: registry.example.com/mydb:1.0
app:
# 特定摘要
image: myapp@sha256:abc123...
3.2 build - 构建镜像
services:
app:
# 简单构建
build: .
api:
# 详细构建选项
build:
context: ./api
dockerfile: Dockerfile.prod
args:
VERSION: "2.0"
BUILD_ENV: production
target: production # 多阶段构建目标
frontend:
# 构建后指定镜像标签
build: ./frontend
image: myapp/frontend:latest
3.3 ports - 端口映射
services:
web:
image: nginx
ports:
# 主机:容器
- "80:80"
- "443:443"
# 主机端口自动分配
- "80"
# 只绑定到特定 IP
- "127.0.0.1:3000:3000"
# UDP 端口
- "53:53/udp"
# 长格式
- target: 80
published: 8080
protocol: tcp
mode: host
3.4 volumes - 卷挂载
services:
db:
image: postgres:15
volumes:
# 命名卷 (Named Volume)
- db-data:/var/lib/postgresql/data
# 绑定挂载(主机路径)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
# 只读挂载
- ./config:/etc/app/config:ro
# 长格式
- type: volume
source: db-data
target: /var/lib/postgresql/data
volume:
nocopy: true
volumes:
db-data: # 卷声明
cache-data:
driver: local
3.5 environment - 环境变量
services:
app:
image: myapp
environment:
# Map 格式
NODE_ENV: production
DEBUG: "false"
DATABASE_URL: postgres://user:pass@db:5432/mydb
db:
image: postgres
environment:
# Array 格式
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=myapp
worker:
image: myworker
# 从文件加载环境变量
env_file:
- .env
- .env.local
3.6 command 和 entrypoint
services:
app:
image: node:20
# 覆盖默认命令
command: npm run dev
worker:
image: python:3.11
# 数组格式
command: ["python", "-m", "celery", "worker"]
custom:
image: alpine
# 覆盖入口点
entrypoint: /custom-entrypoint.sh
command: ["--config", "/etc/app/config.yml"]
4. 依赖管理 (depends_on)
4.1 基本依赖
services:
web:
image: nginx
depends_on:
- api
- db
api:
image: myapi
depends_on:
- db
- redis
db:
image: postgres:15
redis:
image: redis:alpine
4.2 条件依赖 (service_healthy)
services:
web:
image: nginx
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
redis:
image: redis:alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
重要:depends_on 只保证启动顺序,不保证服务处于"就绪"状态。在生产环境中请结合 healthcheck 使用 condition: service_healthy。
5. 网络设置
5.1 默认网络行为
Docker Compose 会为项目自动创建默认网络。同一网络中的服务可以通过服务名相互通信。
services:
web:
image: nginx
# 可以通过 "db" 主机名访问 db 服务
db:
image: postgres
# 可以通过 "web" 主机名访问 web 服务
5.2 自定义网络
services:
frontend:
image: nginx
networks:
- frontend-net
api:
image: myapi
networks:
- frontend-net
- backend-net
db:
image: postgres
networks:
- backend-net
networks:
frontend-net:
driver: bridge
backend-net:
driver: bridge
internal: true # 阻止外部访问
5.3 网络别名和固定 IP
services:
db:
image: postgres
networks:
backend:
aliases:
- database
- postgres-primary
ipv4_address: 172.28.0.10
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
6. 实战示例:WordPress + MySQL
# wordpress/docker-compose.yml
services:
wordpress:
image: wordpress:latest
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
depends_on:
db:
condition: service_healthy
networks:
- wordpress-net
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_password
MYSQL_ROOT_PASSWORD: root_password
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- wordpress-net
volumes:
wordpress-data:
db-data:
networks:
wordpress-net:
driver: bridge
7. 实战示例:Node.js + MongoDB + Redis
# nodejs-stack/docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
ports:
- "3000:3000"
environment:
NODE_ENV: development
MONGODB_URI: mongodb://mongo:27017/myapp
REDIS_URL: redis://redis:6379
volumes:
- .:/app
- /app/node_modules
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-net
command: npm run dev
mongo:
image: mongo:7
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: secret
MONGO_INITDB_DATABASE: myapp
volumes:
- mongo-data:/data/db
- ./mongo-init.js:/docker-entrypoint-initdb.d/init.js:ro
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- app-net
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-net
mongo-express:
image: mongo-express
restart: unless-stopped
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: secret
ME_CONFIG_MONGODB_URL: mongodb://admin:secret@mongo:27017/
ME_CONFIG_BASICAUTH: "false"
depends_on:
- mongo
networks:
- app-net
volumes:
mongo-data:
redis-data:
networks:
app-net:
driver: bridge
8. Docker Compose 命令
8.1 基本命令
# 启动服务(后台)
docker compose up -d
# 启动服务(前台,输出日志)
docker compose up
# 构建镜像后启动
docker compose up --build
# 只启动特定服务
docker compose up -d web db
# 停止并删除服务
docker compose down
# 同时删除卷
docker compose down -v
# 同时删除镜像
docker compose down --rmi all
8.2 状态检查命令
# 运行中的服务列表
docker compose ps
# 所有服务(包括已停止的)
docker compose ps -a
# 查看服务日志
docker compose logs
# 特定服务日志
docker compose logs web
# 实时日志跟踪
docker compose logs -f
# 只看最近100行
docker compose logs --tail=100
# 包含时间戳
docker compose logs -t
8.3 服务管理命令
# 重启服务
docker compose restart
docker compose restart web
# 停止服务(保留容器)
docker compose stop
# 启动已停止的服务
docker compose start
# 服务扩展
docker compose up -d --scale web=3
# 应用配置更改
docker compose up -d --force-recreate
8.4 在容器内执行命令 (exec)
# 在容器中执行命令
docker compose exec web sh
docker compose exec db psql -U postgres
# 以特定用户执行
docker compose exec --user root web sh
# 设置环境变量
docker compose exec -e DEBUG=true app npm test
# 用新容器执行 (run)
docker compose run --rm app npm test
docker compose run --rm db psql -U postgres
9. 环境配置分离
9.1 使用 .env 文件
# .env 文件
POSTGRES_USER=admin
POSTGRES_PASSWORD=secret123
APP_PORT=3000
NODE_ENV=development
# docker-compose.yml
services:
app:
ports:
- "${APP_PORT}:3000"
environment:
NODE_ENV: ${NODE_ENV}
db:
image: postgres:15
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
9.2 使用多个 Compose 文件
# docker-compose.yml(基本配置)
services:
app:
build: .
environment:
NODE_ENV: production
db:
image: postgres:15
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
# docker-compose.override.yml(开发环境,自动应用)
services:
app:
build:
context: .
target: development
volumes:
- .:/app
environment:
NODE_ENV: development
ports:
- "3000:3000"
db:
ports:
- "5432:5432"
# docker-compose.prod.yml(生产环境)
services:
app:
restart: always
deploy:
replicas: 3
environment:
NODE_ENV: production
db:
restart: always
# 开发环境(基本 + override 自动合并)
docker compose up
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 明确指定多个文件
docker compose -f docker-compose.yml -f docker-compose.override.yml up
9.3 使用配置文件(可选服务)
services:
app:
image: myapp
db:
image: postgres:15
# 只在开发环境使用
adminer:
image: adminer
profiles:
- debug
ports:
- "8080:8080"
# 只在测试环境使用
test-runner:
image: myapp-test
profiles:
- test
# 只启动默认服务
docker compose up
# 包含 debug 配置文件
docker compose --profile debug up
# 多个配置文件
docker compose --profile debug --profile test up
10. 有用的配置选项
10.1 restart 策略
services:
app:
image: myapp
restart: "no" # 不重启(默认值)
# restart: always # 始终重启
# restart: on-failure # 只在失败时重启
# restart: unless-stopped # 除非手动停止否则重启
10.2 资源限制
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
10.3 日志设置
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
结论
Docker Compose 是管理多容器应用程序的必备工具。本文内容总结如下:
- docker-compose.yml 结构:services、volumes、networks 的作用和定义方法
- 服务定义:image、build、ports、volumes、environment 等核心配置
- 依赖管理:利用 depends_on 和 healthcheck 实现稳定的启动顺序
- 网络:服务间通信和网络隔离
- 实战示例:WordPress、Node.js 技术栈等实际使用案例
- 命令:up、down、ps、logs、exec 等主要命令
- 环境配置:利用 .env 文件、override 文件、配置文件
下一篇将介绍 Kubernetes 的基本概念和架构,迈向容器编排的世界。