Docker Compose 实战:用 docker-compose.yml 编排多容器服务

当你用 Docker 跑起第一个容器后会很快发现:真实应用从来不是单个容器,而是「Web 服务 + 数据库 + 缓存 + 反向代理」的组合。手动 docker run 一堆参数既容易出错又无法复现,Docker Compose 正是为解决「多容器编排」而生的官方工具——它用一份 docker-compose.yml 声明所有服务、网络与卷,一条命令就能拉起整套环境。如果你还停留在单容器阶段,建议先读完 Docker 基础实战 再来看本文。

一、Compose 到底解决了什么问题

单条 docker run 只能管一个容器。当项目里有 Spring Boot、MySQL、Redis、Nginx 四个服务时,你需要记住四组端口映射、环境变量、挂载目录和启动顺序,换台机器或交给同事就必然「在我电脑能跑」。一个真实的踩坑场景是:凌晨线上数据库容器被误 docker rm 删掉,因为没人记得那条带 -v 的启动命令,数据卷随之消失。Compose 把这些声明收敛进一个版本化的 YAML 文件,配合 docker compose up -d 一键拉起、docker compose down 一键清理,环境与代码一并进 Git,彻底消灭「配置漂移」,也让新同事 git clone 后一条命令就能还原整套环境。

顺便提醒:Docker 官方自 v2 起已将 Compose 改为 Docker CLI 的子命令(docker compose),旧版的独立二进制 docker-compose(带连字符)已进入维护模式。两者命令几乎一致,新项目请直接用 docker compose,避免在生产机上因版本不一致踩坑。

二、核心字段速查表

字段作用示例
services定义每个容器服务web / db / redis
image使用现成镜像mysql:8.0
build基于 Dockerfile 构建build: ./app
ports端口映射“8080:8080”
environment注入环境变量MYSQL_ROOT_PASSWORD
volumes挂载数据卷db_data:/var/lib/mysql
depends_on控制启动顺序– db – redis
networks自定义网络backend / frontend
healthcheck容器健康探测mysqladmin ping
profiles按需启用服务组profile: debug

三、第一个编排:Web + Redis + MySQL

下面是一个最小可跑的 docker-compose.yml,它定义了三个服务并让它们处于同一默认网络,Web 可直接用服务名 db、redis 互访(Compose 内置 DNS)。

services:
  web:
    build: ./app
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/demo
      SPRING_REDIS_HOST: redis
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: demo
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      retries: 5
  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      retries: 5

volumes:
  db_data:

3.1 网络与端口

Compose 会为每个项目创建一个默认 bridge 网络,服务间用「服务名」即主机名互通,无需暴露数据库端口到宿主机。只有真正需要对外访问的 web 才做 ports 映射;若想套一层反代,可参考 Nginx 反向代理实战 把 8080 收口到 80/443。

3.2 启动与验证

# 后台拉起整套服务
docker compose up -d

# 查看容器状态(含健康)
docker compose ps

# 查看某个服务日志
docker compose logs -f web

# 停止并移除容器(保留数据卷)
docker compose down

3.3 加一层 Nginx 反代

把 Web 藏到 Nginx 后面,对外只暴露 80/443,是常见生产形态。注意 web 不再做宿主机端口映射,而是仅在内部网络对 Nginx 可见:

services:
  nginx:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - web
  web:
    build: ./app
    expose:
      - "8080"
    environment:
      SPRING_REDIS_HOST: redis

3.4 配套的最小 Dockerfile

上面的 web 用 build: ./app 构建,意味着 ./app 目录下要有一份 Dockerfile。一个面向 Spring Boot 的可复现示例:

# ./app/Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/demo-0.0.1.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --retries=3   CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]

把 Dockerfile 与 docker-compose.yml 一起提交,别人拿到仓库就能 docker compose up -d --build 直接跑起来,无需任何口头交接。这也是 Compose 作为「团队环境契约」的价值所在。

四、数据持久化:volumes 与 bind mount

容器文件系统是易失的,数据库数据必须落到卷上。命名卷(named volume)由 Docker 管理、性能更好;bind mount 直接挂宿主机目录,便于本地改代码热加载。生产务必用命名卷,避免把宿主机任意目录直接挂进容器造成权限混乱。

方式写法适用场景
命名卷db_data:/var/lib/mysql生产数据库、需持久化
bind mount./src:/app/src本地开发热更新
tmpfstmpfs: /tmp敏感/临时文件
services:
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql   # 命名卷,数据存活于容器之外
volumes:
  db_data:

五、环境变量与多环境

把密码、端口等抽到 .env,再在 YAML 中用 ${VAR} 引用,可避免敏感信息进仓库。多环境则用 docker-compose.override.yml 叠加开发配置(如挂载源码、开调试端口),生产环境用 -f docker-compose.yml -f docker-compose.prod.yml 组合,保持基础定义不变。

# .env
MYSQL_ROOT_PASSWORD=rootpass
APP_PORT=8080

# docker-compose.yml 中引用
services:
  web:
    ports:
      - "${APP_PORT}:8080"

六、生产环境注意事项

本地能跑不等于生产稳。务必加上 restart: unless-stopped 让进程崩溃后自拉起;用带 condition: service_healthy 的 depends_on 让依赖方真正等数据库就绪而非仅等容器启动;给每个服务设 mem_limit 防止单容器吃满内存拖垮整机;数据库端口不要 bind 到 0.0.0.0,只在内部网络互通。还可把这套栈接到 Prometheus+Grafana 监控实战 采集容器指标,配合告警及时发现异常。

services:
  web:
    restart: unless-stopped
    mem_limit: 512m
    read_only: true
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3

七、Compose 与 Kubernetes 的取舍

Compose 适合单机、中小规模、开发到中小生产;当你需要多节点调度、自动扩缩容、滚动更新时,就该上 K8s。两者并非替代关系——很多团队用 Compose 做本地与生产轻量部署,再在规模扩张时迁移到 Kubernetes 入门 体系。

维度Docker ComposeKubernetes
部署目标单机多节点集群
学习成本低(一个 YAML)高
扩缩容手动/scale自动 HPA
滚动更新有限原生支持
适用规模≤ 数台数十到数千台

八、常见排障清单

排障是 Compose 的实战重点,以下是高频坑:

  • 端口冲突:docker compose up 报 port is already allocated,用 docker ps 找出占用进程或改映射端口。
  • 卷权限:MySQL/Redis 报 permission denied,多是宿主机目录属主与容器内用户不一致,优先改用命名卷而非 bind mount。
  • depends_on 不等就绪:容器起来了应用却连不上库,必须用 condition: service_healthy 而非裸 depends_on。
  • .env 未加载:变量为空,确认 .env 与 compose 文件同目录,且未被 .gitignore 误删。
  • 配置校验:docker compose config 能在启动前暴露缩进与字段拼写错误。

九、常用命令速查

docker compose config      # 校验并展开最终配置
docker compose build       # 重新构建镜像
docker compose up -d --build
docker compose exec db bash # 进入容器
docker compose top         # 查看进程
docker compose down -v     # 连卷一起删(慎用)
docker compose profiles    # 列出可用 profiles

十、用 –scale 做水平扩展

当单实例 Web 扛不住流量时,Compose 也能快速横向扩容(前提是应用无状态、会话外置到 Redis 或数据库)。一条命令起三个 Web 实例:

# 把 web 服务扩到 3 个副本
docker compose up -d --scale web=3

# 查看已起的副本
docker compose ps

注意 --scale 要求 web 不能写死固定宿主机端口(否则副本间端口冲突),应只 expose 内部端口,再靠前面的 Nginx 做负载均衡。这种「无状态 + 反代」的组合,正是从 Compose 平滑过渡到 K8s Deployment 的前奏——理念完全一致,只是调度器换成了集群。

十一、总结

Docker Compose 把「多容器应用」从一串易忘的命令变成一份可版本化、可复现的声明文件。掌握 services/volumes/networks/healthcheck 四个核心,再配合环境变量、多文件叠加与生产级配置,你就能用极小成本管好自己的服务栈。CI 环节可用 GitHub Actions 实战 自动构建镜像并推送,形成从代码到部署的闭环;流量上来后用 --scale 横向扩容,规模再大都可平滑迁移到 K8s。它不万能,却是中小团队最划算的第一块运维基石。

上一篇 从零手写 AI Agent:ReAct 智能体实战
下一篇 Spring Boot 3 升级踩坑实录:从 2.x 平滑迁移到 3.x
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]