Docker Compose 实战指南
Docker Compose 将多容器应用的配置集中到一份 YAML 文件中,通过 docker compose up/down 一键创建或销毁整个运行环境,确保团队成员在相同配置下获得一致的开发、测试体验。
1. V1 与 V2 版本差异
| 特性 | docker-compose (V1) | docker compose (V2) |
|---|---|---|
| 状态 | 已停止维护 (2023.06) | 当前标准 |
| 语言 | Python | Go |
| 集成 | 独立二进制 | Docker CLI 插件 |
| 性能 | 一般 | 启动更快,并发效率更高 |
V2 用 Go 重写,与 Docker 引擎深度集成,直接复用 CLI 上下文和认证机制。V1 已于 2023 年 6 月停止维护,新项目一律使用 V2。
在选择工具之前,先厘清 Compose 的定位边界。
2. 与 Kubernetes 对比
| 特性 | docker run | Docker Compose | Kubernetes |
|---|---|---|---|
| 定位 | 单容器运行 | 单机多容器编排 | 集群容器编排 |
| 管理方式 | 命令式 | 声明式 YAML | 声明式 YAML |
| 伸缩性 | 手动 | 单机伸缩 | 自动水平伸缩 |
| 适用场景 | 快速测试 | 开发、测试、小型部署 | 大规模生产环境 |
Compose 的边界是单机多容器编排。需要跨节点调度、自动伸缩时,才需要迁移至 Kubernetes。
3. 核心配置结构
docker-compose.yml 由三个顶层块组成:services 定义各容器,networks 配置服务间网络,volumes 管理持久化存储。
3.1 配置示例
1 | # version 字段在 V2+ 规范中可选,通常省略 |
执行 docker compose up 后:
- Compose 解析配置文件
- 根据
depends_on先启动cache服务 - 构建
webapp镜像并启动容器 - 创建默认网络,
webapp通过主机名cache访问 Redis
⚠️ 卷挂载路径格式为
主机路径:容器路径(左主右容),写反会导致数据挂载异常。
3.2 环境变量管理
敏感信息存入 .env 文件,Compose 启动时自动加载:
1 | # .env |
1 | services: |
3.3 启动顺序与健康检查
depends_on 只保证容器启动顺序,不保证服务就绪。需配合 healthcheck 使用:
1 | services: |
depends_on + healthcheck 的完整启动判定逻辑:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
A["docker compose up"] --> B["启动 db 容器"]
B --> C{"healthcheck 通过?"}
C -->|"是"| D["启动 webapp 容器"]
C -->|"否, 重试中"| E["等待 interval 后重试"]
E --> C
C -->|"超过 retries"| F["标记 unhealthy, webapp 不启动"]
D --> G["所有服务就绪"]
classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
classDef success fill:#10B981,stroke:#059669,color:#fff
classDef warning fill:#F59E0B,stroke:#D97706,color:#fff
classDef danger fill:#EF4444,stroke:#DC2626,color:#fff
class A primary
class G success
class E warning
class F danger3.4 Profiles 按需启动
为特定场景的服务分配 profile,仅在显式指定时启动:
1 | services: |
1 | docker compose --profile dev up |
配置结构理解后,网络与存储是另外两个关键环节。
4. 网络配置
4.1 Expose 与 Ports
| 配置 | 作用范围 | 用途 |
|---|---|---|
ports | 宿主机 + 内部网络 | 对外暴露服务 |
expose | 仅内部网络 | 服务间通信 |
数据库等内部服务只用 expose,不配置 ports:
1 | services: |
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
subgraph Host ["宿主机"]
P["ports: 8080"]
end
subgraph Network ["Compose 内部网络"]
W["webapp"]
D["database (expose: 5432)"]
end
External["外部访问"] --> P
P --> W
W --> D
External -.->|"禁止"| D
classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
classDef danger fill:#EF4444,stroke:#DC2626,color:#fff
class W primary
class D danger4.2 服务发现
Compose 网络内直接用服务名作为主机名:
redis://cache:6379(正确)redis://localhost:6379(错误,容器内localhost指向自身)
⚠️ 跨容器通信使用服务名(如
cache、db),localhost只指向容器自身。
网络确定了服务间通信方式,存储则决定数据的生命周期。
5. 存储挂载
Compose 支持三种挂载方式,选型不当会导致数据丢失或权限问题:
| 类型 | 数据位置 | 持久化 | 适用场景 |
|---|---|---|---|
| Bind mount | 宿主机指定路径 | 是 | 开发时代码同步 |
| Named volume | Docker 管理区域 | 是 | 数据库、持久化数据 |
| tmpfs | 内存 | 否(重启清空) | 敏感临时数据、单元测试 |
1 | services: |
选型建议:
- 开发源码用 bind mount,其余持久化数据统一用 named volume
- 数据库文件不要用 bind mount,Linux/macOS 路径权限差异容易导致启动失败
- 不需持久化的临时目录(如
/tmp、/run/secrets)用 tmpfs
6. 开发体验
6.1 BuildKit 缓存加速
通过 cache_from 复用已有镜像层,显著缩短 CI 和多人协作时的构建时间:
1 | services: |
首次构建后推送缓存镜像,后续 CI 直接复用:
1 | docker compose build |
Docker 23+ 默认启用 BuildKit,无需额外设置环境变量。
6.2 Compose Watch 热更新
Compose Watch(Docker Engine 25+)监听文件变化,按路径规则自动触发不同操作:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
A["文件变更"] --> B{"匹配路径规则?"}
B -->|"src/**"| C["sync\n仅传输变更文件"]
B -->|"package.json"| D["rebuild\n重建镜像"]
B -->|"config/**"| E["sync+restart\n同步后重启容器"]
classDef trigger fill:#3B82F6,stroke:#2563EB,color:#fff
classDef decision fill:#F59E0B,stroke:#D97706,color:#1E3A5F
classDef sync fill:#10B981,stroke:#059669,color:#fff
classDef rebuild fill:#EF4444,stroke:#DC2626,color:#fff
classDef restart fill:#0EA5E9,stroke:#0284C7,color:#fff
class A trigger
class B decision
class C sync
class D rebuild
class E restart对应的 YAML 配置:
1 | services: |
1 | docker compose up --watch |
sync 只传输变更文件,不会将宿主机 node_modules 覆盖容器内依赖目录,比 bind mount 整个项目目录更安全。
开发阶段配置就绪后,进入部署阶段的关键配置。
7. 部署实践
7.1 多环境配置
compose.override.yml 在开发时自动合并覆盖基础配置,生产环境用 -f 显式指定:
1 | compose.yml # 基础配置(镜像、依赖关系) |
1 | # compose.yml |
1 | # compose.override.yml |
1 | # 开发:自动合并 override |
7.2 重启策略
1 | services: |
| 策略 | 行为 |
|---|---|
no | 从不重启(默认) |
always | 总是重启,包括 docker compose stop 后 |
on-failure[:N] | 非零退出码时重启,可限制最大次数 |
unless-stopped | 手动 stop 不重启,其余情况自动重启(推荐) |
7.3 资源限制
不加限制的容器可能耗尽宿主机资源,影响同机其他服务:
1 | services: |
7.4 日志管理
默认 json-file 驱动不限制文件大小,长期运行会撑满磁盘:
1 | services: |
生产环境可换用 loki 或 fluentd 驱动集中推送日志,只需修改 driver 和 options。
7.5 Secrets 管理
环境变量可被 docker inspect 明文读取,敏感凭据改用 secrets:
1 | services: |
容器从 /run/secrets/<name> 读取内容,密钥不进入镜像层,不出现在 docker inspect 输出中。
7.6 配置复用
include 嵌入外部 Compose 文件,支持远程 Git 仓库:
1 | include: |
8. 常用命令
8.1 Run 与 Exec
| 命令 | 容器生命周期 | 用途 |
|---|---|---|
run | 创建新容器,执行后销毁 | 一次性任务(迁移、安装依赖) |
exec | 进入已运行容器 | 调试、查看日志 |
1 | docker compose run --rm webapp npm install |
8.2 Up 与 Run
| 维度 | up | run |
|---|---|---|
| 启动范围 | 所有服务 | 单个服务 |
| 端口映射 | 生效 | 默认不生效 |
| 容器生命周期 | 持续运行 | 执行后退出 |
强制端口映射:docker compose run --service-ports web
8.3 水平扩展
1 | docker compose up -d --scale worker=3 |
约束:
- 仅限无状态服务,数据库等有状态服务不可扩展
- 扩展服务不能定义固定宿主机端口
- 无自动伸缩,需手动调整副本数(自动伸缩需 Kubernetes HPA)
Compose 内置 DNS 对多副本自动轮询负载均衡。
⚠️
--scale的服务若配置了固定宿主机端口,多副本启动时端口冲突。需移除ports,改用expose或动态端口。