Docker Compose:编排多容器应用
Docker Compose 多容器管理
概述
Docker Compose 是 Docker 官方提供的用于定义和运行多容器应用的工具。通过一个 YAML 文件,你可以配置应用需要的所有服务、网络和数据卷,然后使用一条命令即可启动并管理整个栈。对于微服务架构、开发环境和测试场景,Compose 能极大简化容器编排工作。
为什么需要 Docker Compose?
单独使用 docker run 启动多个容器时,你需要手动管理复杂的选项、网络和依赖关系。Compose 实现了 基础设施即代码,将多容器配置版本化、可重复执行,并支持:
- 声明式配置:清晰描述服务、端口、环境变量等。
- 一键启动/停止:整个栈的生命周期管理。
- 开发便捷性:快速重建、日志聚合、服务隔离。
- 生产过渡:Compose 文件可扩展到 Swarm 集群。
安装 Docker Compose
Docker Compose 现已集成到 Docker CLI 中(Docker Compose V2 作为插件)。新版本推荐使用 docker compose(无中划线)命令。
Linux(Ubuntu/Debian)
# 安装 Docker 引擎(包含 Compose 插件)
sudo apt update
sudo apt install docker.io docker-compose-plugin -y
# 验证
docker compose version
macOS / Windows
安装 Docker Desktop 即自动包含 Docker Compose,无需额外操作。
注意:旧版独立的
docker-compose(Python 编写)仍可使用,但官方推荐迁移到 Compose V2 插件。
核心概念
| 概念 | 说明 |
|---|---|
| 服务(services) | 一个容器化的应用组件(如 web 服务、数据库),定义镜像、端口等。 |
| 网络(networks) | 允许服务间通信,默认创建 bridge 网络。 |
| 卷(volumes) | 持久化数据,可被多个服务共享或挂载。 |
| 项目(project) | 由一个 Compose 文件定义的一组服务,默认用目录名作为项目名。 |
编写 docker-compose.yml
下面是一个经典的 Web 应用 + Redis 示例,包含两个服务。
version: "3.9" # Compose 文件格式版本(兼容性说明见下文)
services:
web:
build: ./web # 从当前目录下的 web/ 构建镜像
ports:
- "8080:80" # 主机端口:容器端口
environment:
- REDIS_HOST=redis # 服务名作为主机名,Compose 自动解析
depends_on:
- redis # 启动顺序依赖,但不等待 Redis 就绪
redis:
image: redis:7-alpine # 直接使用官方镜像
volumes:
- redis-data:/data # 挂载命名卷持久化数据
volumes:
redis-data: # 声明卷,默认由 Compose 管理
文件格式版本说明
version 字段对应 Compose 文件格式,推荐使用 3.9(兼容最新 Docker 引擎)。Docker Compose V2 实际上已弱化此要求,但仍建议保留以避免警告。
常用命令
所有命令需在有 docker-compose.yml 的目录下执行(或通过 -f 指定文件)。
| 命令 | 功能 |
|---|---|
docker compose up |
创建并启动所有服务(前台运行) |
docker compose up -d |
后台运行所有服务 |
docker compose down |
停止并移除容器、网络(保留数据卷) |
docker compose down -v |
同时删除匿名卷和命名卷(谨慎!) |
docker compose ps |
列出项目中的容器及状态 |
docker compose logs -f [service] |
实时查看服务日志 |
docker compose build |
重新构建服务的镜像 |
docker compose restart [service] |
重启指定服务 |
docker compose exec service sh |
进入正在运行的服务容器内部 |
docker compose start / stop |
启动/停止已有容器,不会重建 |
服务间通信与网络
默认情况下,Compose 为你的应用创建一个独立的网络,所有服务均可通过服务名互相访问。例如 web 服务连接 redis 时,只需将主机名设为 redis,Docker 的内置 DNS 会自动解析。
你也可以自定义网络以实现更精细的控制:
networks:
frontend:
backend:
services:
web:
networks:
- frontend
- backend
redis:
networks:
- backend
此时 web 同时连接前端和后端网络,而 redis 仅在后端网络中,前端网络无法直接访问 redis。
数据卷与持久化
- 命名卷:在顶层
volumes声明,由 Docker 管理,适合数据库存储。 - 绑定挂载:直接挂载主机路径,适合开发时实时同步代码。
services:
app:
volumes:
- ./src:/app/src # 绑定挂载,主机当前 src/ 映射到容器 /app/src
- node_modules:/app/node_modules # 命名卷,防止模块被挂载覆盖
volumes:
node_modules:
环境变量与配置文件
直接定义
services:
db:
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myapp
引用 .env 文件
在项目根目录创建 .env 文件,键值对会被 Compose 自动加载(但需在 YAML 中显式引用):
# .env
MYSQL_ROOT_PASSWORD=supersecret
services:
db:
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
使用 env_file
services:
app:
env_file:
- ./config/app.env
多环境与覆盖文件
利用多个 Compose 文件叠加配置,实现开发/生产分离。
基础文件 docker-compose.yml:
services:
web:
image: myapp:latest
ports:
- "80:80"
生产覆盖 docker-compose.prod.yml:
services:
web:
environment:
- ENV=production
restart: always
启动时指定多个文件(右侧文件覆盖左侧):
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
开发者友好功能
实时重载与开发伺服
结合绑定挂载和文件监视工具(如 nodemon, webpack-dev-server),容器内代码修改可即时生效。
services:
dev:
build: .
volumes:
- .:/app
command: npm run dev # 启动带热重载的开发服务器
ports:
- "3000:3000"
容器内执行一次性任务
services:
migration:
build: .
command: python manage.py migrate
depends_on:
- db
在 up 时该服务会执行并退出,不会长期运行。
实战:构建一个简单的微服务栈
目标:用 Python Flask + Redis + Nginx 搭建一个计数器应用。
项目结构:
myapp/
├── docker-compose.yml
├── web/
│ ├── Dockerfile
│ └── app.py
└── nginx/
└── nginx.conf
1. web 服务(Flask)
web/app.py:
from flask import Flask
import redis
import os
app = Flask(__name__)
cache = redis.Redis(host=os.getenv('REDIS_HOST', 'redis'), port=6379)
@app.route('/')
def hello():
count = cache.incr('hits')
return f'Hello! This page has been visited {count} times.'
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
web/Dockerfile:
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install flask redis
COPY . .
CMD ["python", "app.py"]
2. Nginx 反向代理
nginx/nginx.conf:
server {
listen 80;
location / {
proxy_pass http://web:5000; # 使用服务名
proxy_set_header Host $host;
}
}
3. Compose 编排文件
version: "3.9"
services:
web:
build: ./web
environment:
- REDIS_HOST=redis
networks:
- backend
redis:
image: redis:alpine
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- web
networks:
- frontend
- backend
networks:
frontend:
backend:
4. 启动应用
docker compose up -d
浏览器访问 http://localhost,刷新即可看到访问计数递增。
常见问题与调试技巧
| 问题 | 排查方式 |
|---|---|
| 服务无法启动 | docker compose logs <service> 查看日志 |
| 服务间连通失败 | docker compose exec <service> ping <target> 测试 DNS 解析 |
| 端口冲突 | 检查主机端口,修改 ports 左侧端口或停止占用进程 |
| 镜像未更新 | docker compose build --no-cache 强制重新构建 |
| 数据卷权限错误 | 注意容器内用户 UID/GID,或使用 user: 映射 |
从旧版 docker-compose 迁移
- 命令从
docker-compose改为docker compose(不带连字符)。 - 移除 Compose 文件中的
version(可选,但保留无害)。 - V2 支持更快的构建和 GPU 设备等高级特性,完全向下兼容。
最佳实践总结
- 保持 Compose 文件简洁:一个文件定义核心服务,通过覆盖文件处理环境差异。
- 显式声明资源:为关键服务配置
restart: unless-stopped保证自愈。 - 使用
.env管理敏感信息,切勿将密钥硬编码并提交到版本控制。 - 利用
depends_on结合健康检查(使用condition: service_healthy)确保就绪顺序。 - 生产环境考虑扩展:结合
docker compose up --scale web=3水平扩缩,但需配合负载均衡。
掌握 Docker Compose 意味着你可以用代码化、可重复的方式管理整个容器生态,从本地开发到部署流水线都受益匪浅。