本文基于 Docker Compose V2,所有内容均来自 Docker 官方文档和生产环境实践。
全文约 14800 字,建议收藏后阅读。读完本文,你将从 Docker Compose 新手成长为能够独立部署复杂多容器应用的专家。
一、Docker Compose 核心概念与底层原理
1.1 什么是 Docker Compose
Docker Compose 是 Docker 官方推出的多容器应用编排工具,它允许你使用一个 YAML 文件来定义和运行多个 Docker 容器组成的应用程序。
简单来说,如果你需要同时运行一个 Web 应用、一个数据库、一个缓存和一个消息队列,传统方式需要手动执行多个 docker run 命令,并且还要处理它们之间的网络连接、依赖关系等问题。而 Docker Compose 让你可以在一个文件中定义所有这些服务,然后用一条命令启动整个应用栈。
Docker Compose 的核心价值:
- 一键部署:一条命令启动/停止整个应用
- 环境一致性:开发、测试、生产环境使用相同的配置
- 依赖管理:自动处理服务之间的启动顺序和依赖关系
- 可重复性:相同的配置在任何地方都能得到相同的结果
- 声明式配置:描述"想要什么",而不是"怎么做"
1.2 Docker Compose V1 vs V2:为什么必须升级到 V2
重要提醒:Compose V1(docker-compose 命令)已于 2023 年 7 月停止接收更新,并且不再包含在新的 Docker Desktop 版本中。所有新的开发和部署都应该使用 Compose V2(docker compose 命令) 。
| 特性 | Compose V1 | Compose V2 |
|---|---|---|
| 命令格式 | docker-compose(带连字符) |
docker compose(空格分隔) |
| 实现语言 | Python | Go |
| 集成方式 | 独立二进制文件 | Docker CLI 插件 |
| 性能 | 较慢 | 显著提升(特别是构建速度) |
| 容器命名 | 使用下划线分隔(project_service_1) |
使用连字符分隔(project-service-1) |
| 支持状态 | 已弃用 | 官方推荐,持续更新 |
| BuildKit 支持 | 有限 | 原生支持,默认启用 |
升级方法:
- Windows/macOS:升级到最新版 Docker Desktop 即可自动获得 Compose V2
- Linux:通过 Docker 官方仓库安装或手动下载二进制文件到
$HOME/.docker/cli-plugins/目录
1.3 Docker Compose 工作原理
Docker Compose 本身不创建容器 ,它只是一个客户端工具,通过 Docker API 与 Docker Engine 通信,将 Compose 文件中的声明转换为一系列 Docker API 调用。

关键底层细节:
- 项目(Project) :Compose 将一个应用视为一个项目,默认使用当前目录的名称作为项目名称。你可以通过
--project-name标志或COMPOSE_PROJECT_NAME环境变量覆盖。 - 服务(Service) :项目由多个服务组成,每个服务对应一个或多个相同配置的容器。
- 网络隔离:Compose 会为每个项目创建一个独立的桥接网络,同一项目中的所有服务默认都连接到这个网络,并且可以通过服务名称互相访问。
- 数据卷管理:Compose 会自动管理命名数据卷,确保数据在容器重启或重建时不会丢失。
1.4 Docker Compose 的适用场景与局限性
适用场景:
- 开发环境:快速搭建一致的开发环境
- 测试环境:自动化测试的环境准备
- 小型到中型生产环境:单节点部署
- CI/CD 流水线:构建和测试应用
- 演示和原型:快速展示应用功能
局限性:
- 仅支持单节点部署,不支持多节点集群
- 没有内置的服务发现和负载均衡(需要配合反向代理)
- 没有滚动更新和蓝绿部署功能(需要手动实现)
- 不适合超大规模应用部署
注意:对于大规模生产环境,应该使用 Kubernetes 而不是 Docker Compose。但对于 90% 的中小型应用,Docker Compose 已经足够强大且简单易用。
二、docker-compose.yml 文件详解(Compose Specification)
2.1 文件格式与基本结构
Compose 文件使用 YAML 格式编写,默认文件名是 compose.yaml 或 docker-compose.yml(两者都支持,推荐使用 compose.yaml)。
重要更新:自 Docker Compose v1.27 起,version 字段已被弃用 。现在所有新的 Compose 文件都应该省略 version 字段,直接使用最新的 Compose Specification。
基本结构:
yaml
# 顶级元素:services(必需)
services:
# 服务1定义
service-name-1:
# 服务配置
image: nginx:alpine
ports:
- "80:80"
# 服务2定义
service-name-2:
# 服务配置
build: .
depends_on:
- service-name-1
# 顶级元素:networks(可选)
networks:
# 自定义网络定义
my-network:
driver: bridge
# 顶级元素:volumes(可选)
volumes:
# 自定义数据卷定义
my-volume:
# 顶级元素:configs(可选)
configs:
# 配置文件定义
my-config:
file: ./config.conf
# 顶级元素:secrets(可选)
secrets:
# 敏感信息定义
my-secret:
file: ./secret.txt
2.2 services 顶级元素详解
services 是 Compose 文件中唯一必需的顶级元素,用于定义应用中的所有服务。每个服务对应一个或多个容器。
2.2.1 基本配置
image:指定服务使用的镜像。
yaml
services:
nginx:
image: nginx:1.25-alpine # 推荐使用具体版本,不要使用 latest
container_name:指定容器名称。
yaml
services:
nginx:
image: nginx:alpine
container_name: my-nginx # 自定义容器名称
注意 :如果指定了
container_name,则该服务不能进行扩容(scale),因为容器名称必须是唯一的。
command:覆盖容器默认的 CMD 指令。
bash
services:
app:
image: node:20-alpine
command: npm start # 字符串形式
# 或者数组形式(推荐,避免 shell 解析问题)
command: ["npm", "start"]
entrypoint:覆盖容器默认的 ENTRYPOINT 指令。
arduino
services:
app:
image: node:20-alpine
entrypoint: ["/app/entrypoint.sh"]
working_dir:设置容器内的工作目录。
yaml
services:
app:
image: node:20-alpine
working_dir: /app
user:指定运行容器内进程的用户。
makefile
services:
app:
image: node:20-alpine
user: "1000:1000" # UID:GID
2.2.2 构建配置
build:指定从 Dockerfile 构建镜像。
yaml
# 简单形式:指定上下文路径
services:
app:
build: . # 使用当前目录下的 Dockerfile 构建
# 完整形式
services:
app:
build:
context: ./app # 构建上下文路径
dockerfile: Dockerfile.prod # 指定 Dockerfile 名称
args: # 构建参数
NODE_ENV: production
VERSION: 1.0.0
target: production # 多阶段构建的目标阶段
cache_from: # 构建缓存源
- myapp:latest
network: host # 构建时使用的网络
重要提示: 构建上下文是 Docker 守护进程可以访问的文件和目录的集合。Docker 会将上下文中的所有文件发送给守护进程,因此应该尽量减小上下文大小,将不需要的文件添加到 .dockerignore 中。
2.2.3 端口配置
ports:将容器端口映射到主机端口。
makefile
services:
nginx:
image: nginx:alpine
ports:
- "80:80" # 主机端口:容器端口
- "443:443"
- "127.0.0.1:8080:80" # 只绑定到 localhost
- "8000-8010:8000-8010" # 端口范围映射
- "9000" # 随机映射主机端口到容器 9000 端口
expose:暴露端口给同一网络中的其他服务,但不映射到主机。
yaml
services:
db:
image: postgres:16-alpine
expose:
- "5432" # 同一网络中的服务可以通过 db:5432 访问
2.2.4 环境变量配置
environment:设置环境变量。
ini
services:
db:
image: postgres:16-alpine
environment:
# 数组形式
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
# 或者对象形式(推荐)
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
env_file:从文件中加载环境变量。
yaml
services:
app:
image: node:20-alpine
env_file:
- .env # 默认加载
- .env.production # 额外的环境文件
最佳实践 :永远不要在 Compose 文件中硬编码敏感信息(如密码、API 密钥等)。应该使用
.env文件,并将.env添加到.gitignore中。对于高度敏感的数据,应该使用 Docker Secrets。
2.2.5 数据卷配置
volumes:挂载数据卷或主机目录到容器。
bash
services:
db:
image: postgres:16-alpine
volumes:
# 命名数据卷(推荐,由 Docker 管理)
- postgres_data:/var/lib/postgresql/data
# 绑定挂载(主机目录)
- ./config:/etc/postgresql/config:ro # 只读挂载
- ./logs:/var/log/postgresql:rw # 读写挂载(默认)
# tmpfs 挂载(临时文件系统,存储在内存中)
- type: tmpfs
target: /tmp
tmpfs:
size: 100M
# 必须在顶级 volumes 中声明命名数据卷
volumes:
postgres_data:
三种挂载类型对比:
| 类型 | 语法 | 特点 | 适用场景 |
|---|---|---|---|
| 命名数据卷 | volume_name:/container/path |
Docker 管理,性能好,数据持久化 | 数据库数据、应用状态 |
| 绑定挂载 | /host/path:/container/path |
主机目录直接挂载,开发时方便 | 开发环境代码挂载、配置文件 |
| tmpfs | type: tmpfs |
内存中存储,速度快,数据不持久 | 临时文件、缓存 |
2.2.6 网络配置
networks:指定服务连接的网络。
yaml
services:
web:
image: nginx:alpine
networks:
- frontend
- backend
app:
image: node:20-alpine
networks:
- backend
db:
image: postgres:16-alpine
networks:
- backend
networks:
frontend:
backend:
重要 :默认情况下,Compose 会创建一个名为
{project_name}_default的网络,所有服务都会连接到这个网络。只有当你需要网络隔离时,才需要自定义网络。
network_mode:指定网络模式。
yaml
services:
app:
image: node:20-alpine
network_mode: bridge # 默认
# network_mode: host # 使用主机网络
# network_mode: none # 禁用网络
# network_mode: service:web # 共享另一个服务的网络命名空间
2.2.7 依赖关系与启动顺序
depends_on:指定服务之间的依赖关系,决定启动顺序。
yaml
services:
web:
image: nginx:alpine
depends_on:
- app
- db
app:
image: node:20-alpine
depends_on:
- db
- redis
db:
image: postgres:16-alpine
redis:
image: redis:7-alpine
非常重要的注意事项:
depends_on只保证容器的启动顺序,不保证服务的就绪顺序。也就是说,它只会等待容器启动,而不会等待容器内的应用程序完全启动并准备好接受请求。
例如,上面的配置中,app 服务会在 db 和 redis 容器启动后立即启动,但此时 PostgreSQL 可能还在初始化过程中,无法接受数据库连接,导致 app 服务启动失败。
解决方案:
- 应用层重试:在应用程序中实现数据库连接重试逻辑
- 健康检查 :使用
healthcheck配合depends_on的条件语法 - 等待脚本 :使用
wait-for-it.sh等工具等待依赖服务就绪
健康检查配合 depends_on(推荐):
bash
services:
app:
image: node:20-alpine
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
2.2.8 重启策略
restart:指定容器的重启策略。
yaml
services:
app:
image: node:20-alpine
restart: no # 默认,不重启
# restart: always # 总是重启
# restart: on-failure # 仅在失败时重启
# restart: unless-stopped # 除非手动停止,否则总是重启
生产环境推荐使用 restart: unless-stopped,这样容器在崩溃或 Docker 重启时会自动重启,但如果你手动停止了容器,它不会自动重启。
2.2.9 健康检查
healthcheck:配置容器的健康检查,用于判断容器内的应用是否正常运行。
bash
services:
web:
image: nginx:alpine
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s # 检查间隔
timeout: 10s # 超时时间
retries: 3 # 重试次数
start_period: 30s # 启动后等待多久开始检查
disable: false # 是否禁用健康检查
test 命令的三种形式:
["CMD", "command", "arg1", "arg2"]:直接执行命令,不通过 shell["CMD-SHELL", "command arg1 arg2"]:通过 shell 执行命令"command arg1 arg2":等同于["CMD-SHELL", "command arg1 arg2"]
2.2.10 资源限制
deploy.resources:配置容器的资源限制和预留。
yaml
services:
app:
image: node:20-alpine
deploy:
resources:
limits: # 资源上限
cpus: "1.0" # 最多使用 1 个 CPU 核心
memory: 512M # 最多使用 512MB 内存
reservations: # 资源预留
cpus: "0.25" # 预留 0.25 个 CPU 核心
memory: 128M # 预留 128MB 内存
生产环境必须配置资源限制,防止单个容器占用所有主机资源,导致其他服务无法运行。
2.3 networks 顶级元素详解
networks 用于定义自定义网络。默认情况下,Compose 会创建一个桥接网络,但你可以自定义网络的驱动、IP 地址范围等。
yaml
networks:
frontend:
driver: bridge # 默认驱动
driver_opts:
com.docker.network.bridge.enable_icc: "true" # 启用容器间通信
backend:
driver: bridge
ipam: # IP 地址管理
config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
external-network:
external: true # 使用已存在的外部网络
name: my-existing-network # 外部网络的实际名称
2.4 volumes 顶级元素详解
volumes 用于定义命名数据卷。
bash
volumes:
postgres_data:
driver: local # 默认驱动
driver_opts:
type: none
o: bind
device: /data/postgres # 绑定到主机指定目录
redis_data:
external: true # 使用已存在的外部数据卷
name: my-existing-redis-volume
2.5 configs 与 secrets 顶级元素详解
configs 和 secrets 用于管理配置文件和敏感信息,避免将它们硬编码在镜像或 Compose 文件中。
configs 示例:
yaml
services:
nginx:
image: nginx:alpine
configs:
- source: nginx-config
target: /etc/nginx/nginx.conf
mode: 0444
configs:
nginx-config:
file: ./nginx.conf # 从文件加载配置
secrets 示例:
yaml
services:
db:
image: postgres:16-alpine
secrets:
- postgres-password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password
secrets:
postgres-password:
file: ./postgres-password.txt # 从文件加载密码
注意 :在单机 Docker 环境中,secrets 只是简单地将文件挂载到容器的
/run/secrets/目录,并没有加密。但它仍然比硬编码密码要好,因为密码不会出现在 Compose 文件或镜像中。
2.6 变量替换与 .env 文件
Docker Compose 支持在 Compose 文件中使用环境变量进行变量替换。
基本语法:
ruby
services:
db:
image: postgres:${POSTGRES_VERSION:-16}-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
变量修饰符:
${VAR:-default}:如果 VAR 未设置或为空,使用 default${VAR-default}:如果 VAR 未设置,使用 default${VAR:?error}:如果 VAR 未设置或为空,抛出错误并退出${VAR?error}:如果 VAR 未设置,抛出错误并退出
.env 文件: Compose 会自动从当前目录下的 .env 文件加载环境变量。
ini
# .env 文件示例
POSTGRES_VERSION=16
POSTGRES_DB=myapp
POSTGRES_USER=postgres
POSTGRES_PASSWORD=supersecretpassword
最佳实践:
- 提供一个
.env.example文件作为模板,包含所有必需的环境变量 - 将
.env添加到.gitignore中,不要提交到版本控制 - 使用不同的
.env文件对应不同的环境(.env.dev,.env.prod) - 使用
${VAR:?error}语法验证必需的环境变量,实现快速失败
三、Docker Compose 常用命令详解
3.1 基本生命周期命令
docker compose up
创建并启动所有服务。
bash
# 前台运行,显示所有日志
docker compose up
# 后台运行(守护进程模式)
docker compose up -d
# 启动前重新构建镜像
docker compose up --build
# 强制重新创建所有容器
docker compose up --force-recreate
# 只启动指定的服务
docker compose up -d web db
docker compose down
停止并删除所有服务容器、网络。
csharp
# 停止并删除容器和网络
docker compose down
# 同时删除数据卷(谨慎使用!会丢失所有数据)
docker compose down -v
# 同时删除镜像
docker compose down --rmi all
# 删除孤立的容器(不在 Compose 文件中定义的容器)
docker compose down --remove-orphans
docker compose start/stop/restart
启动、停止或重启已存在的服务。
bash
# 启动所有服务
docker compose start
# 启动指定服务
docker compose start web
# 停止所有服务
docker compose stop
# 停止指定服务
docker compose stop db
# 重启所有服务
docker compose restart
# 重启指定服务
docker compose restart app
3.2 查看与调试命令
docker compose ps
列出所有服务及其状态。
bash
# 列出运行中的服务
docker compose ps
# 列出所有服务(包括已停止的)
docker compose ps -a
docker compose logs
查看服务日志。
ini
# 查看所有服务的日志
docker compose logs
# 查看指定服务的日志
docker compose logs web
# 实时跟踪日志
docker compose logs -f
# 显示最后 N 行日志
docker compose logs --tail=100
# 显示日志时间戳
docker compose logs -t
docker compose exec
在运行中的容器内执行命令。
bash
# 在 app 容器中启动 bash
docker compose exec app bash
# 以 root 用户执行命令
docker compose exec -u root app bash
# 执行一次性命令
docker compose exec db psql -U postgres myapp
docker compose run
运行一个一次性的服务容器。
bash
# 运行 app 服务并执行 npm test
docker compose run app npm test
# 运行时不启动依赖服务
docker compose run --no-deps app npm test
# 运行后自动删除容器
docker compose run --rm app npm test
docker compose exec vs docker compose run:
exec:在已经运行的容器中执行命令run:创建一个新的容器并执行命令,执行完后容器会停止
3.3 构建与镜像命令
docker compose build
构建服务镜像。
perl
# 构建所有服务的镜像
docker compose build
# 构建指定服务的镜像
docker compose build app
# 不使用缓存构建
docker compose build --no-cache
# 构建并拉取最新的基础镜像
docker compose build --pull
docker compose pull
拉取服务使用的镜像。
bash
# 拉取所有服务的镜像
docker compose pull
# 拉取指定服务的镜像
docker compose pull db
docker compose push
推送服务镜像到镜像仓库。
perl
# 推送所有服务的镜像
docker compose push
# 推送指定服务的镜像
docker compose push app
3.4 其他常用命令
docker compose config
验证并查看合并后的 Compose 配置。
lua
# 验证配置是否正确
docker compose config
# 查看合并后的完整配置
docker compose config --no-interpolate
docker compose top
显示服务容器中运行的进程。
bash
# 显示所有服务的进程
docker compose top
# 显示指定服务的进程
docker compose top app
docker compose stats
显示服务容器的资源使用情况。
bash
# 显示所有服务的资源使用情况
docker compose stats
# 显示指定服务的资源使用情况
docker compose stats app db
docker compose scale
扩容服务。
ini
# 将 app 服务扩容到 3 个实例
docker compose up --scale app=3 -d
# 或者
docker compose scale app=3
注意 :如果服务指定了
container_name或ports映射到固定主机端口,则不能进行扩容。
docker compose watch
自动监控文件变化并重新构建和重启服务(Compose V2.22+)。
bash
# 启动文件监控
docker compose watch
这是开发环境的一个非常有用的功能,可以实现代码热重载。
四、实战案例:从简单到复杂的应用部署
4.1 案例一:最简单的 Nginx 静态网站
文件结构:
css
.
├── docker-compose.yml
└── html
└── index.html
docker-compose.yml:
yaml
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
html/index.html:
xml
<!DOCTYPE html>
<html>
<head>
<title>Hello Docker Compose</title>
</head>
<body>
<h1>Hello Docker Compose!</h1>
</body>
</html>
启动命令:
docker compose up -d
访问 http://localhost 即可看到页面。
4.2 案例二:Node.js + PostgreSQL + Redis 应用
文件结构:
bash
.
├── .env
├── docker-compose.yml
└── app
├── Dockerfile
├── package.json
└── server.js
.env:
ini
POSTGRES_DB=myapp
POSTGRES_USER=postgres
POSTGRES_PASSWORD=supersecretpassword
REDIS_URL=redis://redis:6379
DATABASE_URL=postgres://postgres:supersecretpassword@db:5432/myapp
docker-compose.yml:
bash
services:
app:
build: ./app
ports:
- "3000:3000"
environment:
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
redis_data:
app/Dockerfile:
sql
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
启动命令:
docker compose up -d
4.3 案例三:带 Nginx 反向代理和 HTTPS 的完整应用
文件结构:
erlang
.
├── .env
├── docker-compose.yml
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── certs
│ ├── fullchain.pem
│ └── privkey.pem
└── app
├── Dockerfile
└── ...
docker-compose.yml:
yaml
services:
nginx:
build: ./nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
restart: unless-stopped
networks:
- frontend
- backend
app:
build: ./app
environment:
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- backend
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
networks:
frontend:
backend:
volumes:
postgres_data:
redis_data:
nginx/nginx.conf:
ini
events {}
http {
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass http://app:3000;
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;
}
}
}
4.4 案例四:多环境配置(开发/生产)
使用多个 Compose 文件实现不同环境的配置。
基础配置:docker-compose.yml
yaml
services:
app:
build: ./app
environment:
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
开发环境覆盖:docker-compose.override.yml(默认自动加载)
yaml
services:
app:
ports:
- "3000:3000"
volumes:
- ./app:/app
- /app/node_modules
environment:
NODE_ENV: development
command: npm run dev
db:
ports:
- "5432:5432"
redis:
ports:
- "6379:6379"
生产环境覆盖:docker-compose.prod.yml
bash
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
restart: unless-stopped
app:
environment:
NODE_ENV: production
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
db:
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
redis:
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
启动开发环境:
docker compose up -d
启动生产环境:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
五、高级特性与最佳实践
5.1 使用 Profiles 管理可选服务
Profiles 允许你定义哪些服务在特定环境下应该启动。
yaml
services:
app:
image: node:20-alpine
# 总是启动
db:
image: postgres:16-alpine
# 总是启动
redis:
image: redis:7-alpine
profiles: ["with-redis"] # 只有指定 with-redis profile 时才启动
adminer:
image: adminer:latest
ports:
- "8080:8080"
profiles: ["debug"] # 只有指定 debug profile 时才启动
启动命令:
csharp
# 只启动 app 和 db
docker compose up -d
# 启动 app、db 和 redis
docker compose --profile with-redis up -d
# 启动所有服务
docker compose --profile "*" up -d
5.2 使用 extends 共享配置
extends 允许你从另一个 Compose 文件或同一文件中的另一个服务继承配置。
yaml
# base.yml
services:
base-app:
build: ./app
environment:
NODE_ENV: production
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
yaml
# docker-compose.yml
services:
app1:
extends:
file: base.yml
service: base-app
environment:
APP_NAME: app1
app2:
extends:
file: base.yml
service: base-app
environment:
APP_NAME: app2
5.3 日志配置最佳实践
配置日志驱动和日志轮转:
python
services:
app:
image: node:20-alpine
logging:
driver: json-file # 默认驱动
options:
max-size: "10m" # 单个日志文件最大大小
max-file: "3" # 最多保留 3 个日志文件
生产环境推荐配置:
arduino
x-logging: &default-logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
app:
image: node:20-alpine
logging: *default-logging
db:
image: postgres:16-alpine
logging: *default-logging
redis:
image: redis:7-alpine
logging: *default-logging
5.4 安全最佳实践
-
不要以 root 用户运行容器
makefileservices: app: image: node:20-alpine user: "1000:1000" # 使用非 root 用户 -
使用只读文件系统
yamlservices: app: image: node:20-alpine read_only: true tmpfs: - /tmp - /app/tmp -
删除不必要的 capabilities
yamlservices: app: image: node:20-alpine cap_drop: - ALL -
不要暴露不必要的端口
-
使用具体的镜像标签,不要使用 latest
-
定期更新基础镜像
-
扫描镜像漏洞
5.5 性能优化最佳实践
- 使用多阶段构建减小镜像大小
- 合理配置资源限制
- 使用命名数据卷而不是绑定挂载
- 避免在容器中运行多个进程
- 使用缓存加速构建
- 使用 .dockerignore 减小构建上下文
六、生产环境部署与运维
6.1 生产环境部署清单
在将应用部署到生产环境之前,请确保你已经完成了以下检查:
- 所有服务都配置了
restart: unless-stopped - 所有关键服务都配置了健康检查
- 所有服务都配置了资源限制
- 所有持久化数据都使用了命名数据卷
- 没有硬编码任何敏感信息
- 日志配置了轮转
- 应用运行在非 root 用户下
- 只暴露了必要的端口
- 配置了 HTTPS
- 有备份策略
- 有监控和告警
6.2 备份与恢复
备份数据卷:
bash
# 创建备份容器,将数据卷内容打包
docker run --rm \
-v myapp_postgres_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/postgres_backup_$(date +%Y%m%d).tar.gz -C /data .
恢复数据卷:
bash
# 停止相关服务
docker compose stop db
# 创建恢复容器,将备份内容解压到数据卷
docker run --rm \
-v myapp_postgres_data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/postgres_backup_20260420.tar.gz -C /data
# 启动服务
docker compose start db
6.3 滚动更新
Docker Compose 本身不支持真正的滚动更新,但你可以通过以下方式实现类似的效果:
ini
# 构建新镜像
docker compose build app
# 启动新的容器实例
docker compose up -d --scale app=2 --no-recreate
# 等待新容器健康检查通过
sleep 30
# 停止旧容器
docker compose stop app-old
# 删除旧容器
docker compose rm -f app-old
# 缩容回原来的数量
docker compose up -d --scale app=1 --no-recreate
对于更复杂的更新需求,建议使用 Kubernetes。
6.4 监控与告警
使用 docker stats 监控资源使用:
docker compose stats
使用 Prometheus + Grafana 进行全面监控:
yaml
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
restart: unless-stopped
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
restart: unless-stopped
volumes:
grafana_data:
七、常见问题排查与解决方案
7.1 端口冲突问题
错误信息:
vbnet
Error response from daemon: driver failed programming external connectivity on endpoint ...: Bind for 0.0.0.0:80 failed: port is already allocated
解决方案:
-
查找占用端口的进程:
csssudo lsof -i :80 -
停止占用端口的进程
-
或者修改 Compose 文件中的端口映射
7.2 服务启动顺序问题
问题描述: 应用服务在数据库服务准备好之前就启动了,导致连接失败。
解决方案:
- 使用健康检查配合
depends_on的条件语法(推荐) - 在应用程序中实现重试逻辑
- 使用等待脚本
7.3 数据丢失问题
问题描述: 执行 docker compose down 后,数据库数据丢失。
原因: 没有使用命名数据卷,而是使用了匿名数据卷。
解决方案:
- 始终使用命名数据卷存储持久化数据
- 不要轻易使用
docker compose down -v,它会删除所有数据卷 - 定期备份数据
7.4 镜像拉取失败问题
解决方案:
- 检查网络连接
- 配置镜像加速器
- 登录到私有镜像仓库
- 检查镜像名称和标签是否正确
7.5 权限问题
错误信息:
arduino
Permission denied: '/app/data'
解决方案:
- 确保容器内的用户对挂载的目录有读写权限
- 使用
user指令指定与主机目录权限匹配的 UID 和 GID - 修改主机目录的权限
八、总结
Docker Compose 是一个非常强大且简单易用的多容器应用编排工具。它通过声明式的配置文件,让你可以轻松地定义、运行和管理复杂的多容器应用。 希望本文能够帮助你全面掌握 Docker Compose,让你的开发和部署工作更加高效和轻松。如果你觉得本文对你有帮助,欢迎点赞、收藏和分享。