Docker Compose 编排实战:从单机到多服务,我踩过的 5 个坑
结论先行: Docker Compose 不是"写个 yaml 就完事",真正的坑藏在网络配置、数据持久化和启动顺序里。这篇文章用 3 个实战案例+5 个踩坑记录,让你少走 2 天弯路。


1. 为什么我需要 Docker Compose?
3 年前我第一次用 Docker 跑项目,一个后端 + 一个 MySQL,手动 docker run 了 5 次。项目扩到 4 个服务后,每次部署都是一场灾难:端口冲突、容器连不上、重启顺序记不住。
Docker Compose 解决的核心问题就两个:声明式编排 和 环境一致性。你写一个 docker-compose.yml,团队拉下来直接 up,环境一模一样。
2. 实战案例:一个典型的 Web 应用
2.1 项目结构
myapp/
├── backend/ # Go/Node.js 后端
├── frontend/ # React 前端
├── nginx/ # 反向代理配置
├── docker-compose.yml
└── .env
2.2 初版 docker-compose.yml(有坑版)
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- postgres
- redis
postgres:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
redis:
image: redis:7-alpine
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
看起来对?启动后你会遇到 3 个问题。别急,下面一个个踩。
3. 踩坑记录:5 个让我加班到凌晨的问题
坑 1:depends_on 只保证启动顺序,不保证服务就绪
现象: backend 启动后立即连接 postgres,但 postgres 还没初始化完成,报错 connection refused。
解决方案: 使用 healthcheck + condition: service_healthy
services:
postgres:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 5s
timeout: 5s
retries: 5
backend:
build: ./backend
depends_on:
postgres:
condition: service_healthy
注意: Docker Compose v2 开始支持
condition,v1 不行。用docker compose(新版)而不是docker-compose(旧版)。
坑 2:容器间通信默认用服务名,但端口映射会搞晕你
现象: backend 代码里写 localhost:5432 连 postgres,启动后报错。
原因: 在 Docker 网络里,容器之间通过服务名通信,不是 localhost。backend 容器里 localhost 指向自己,不是宿主机。
正确写法: 在代码配置里用服务名
# 错误
DATABASE_URL = "postgresql://user:pass@localhost:5432/myapp"
# 正确
DATABASE_URL = "postgresql://user:pass@postgres:5432/myapp"
坑 3:数据不持久化,docker compose down 后全丢
现象: 测试时 docker compose down && docker compose up,数据库数据全没了。
解决方案: 用命名卷(named volumes)而不是绑定挂载(bind mounts)来存数据库数据。
services:
postgres:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data: # 声明命名卷
提示: 绑定挂载(
./data:/var/lib/...)适合代码热更新,但数据库数据用命名卷更安全,避免宿主机权限问题。
坑 4:环境变量管理混乱,多环境切换痛苦
现象: 开发、测试、生产用不同配置,改 docker-compose.yml 容易出错。
解决方案: 用 .env 文件 + 变量替换
.env 文件:
DB_USER=myapp_user
DB_PASSWORD=dev_password_123
DB_NAME=myapp_dev
docker-compose.yml 引用:
services:
postgres:
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
不同环境用不同 .env 文件:
# 开发
cp .env.dev .env && docker compose up
# 生产
cp .env.prod .env && docker compose up
坑 5:Nginx 反向代理容器连不上后端
现象: Nginx 配置里写 proxy_pass http://localhost:8080;,结果 502。
原因: Nginx 容器里 localhost 指向它自己,不是 backend 容器。
正确配置:
upstream backend {
server backend:8080; # 用服务名
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
4. 最终版:稳定可用的 docker-compose.yml
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
postgres:
image: postgres:15
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- backend
restart: unless-stopped
volumes:
postgres_data:
redis_data:
5. 日常操作命令清单
# 启动所有服务(后台)
docker compose up -d
# 查看日志(实时)
docker compose logs -f backend
# 进入容器调试
docker compose exec backend sh
# 重建某个服务(代码更新后)
docker compose up -d --build backend
# 停止并清理(保留卷)
docker compose down
# 彻底清理(删卷,慎用)
docker compose down -v
6. 延伸思考
-
生产环境怎么办? Docker Compose 适合开发/测试,生产环境建议用 Kubernetes 或 Docker Swarm。但你可以在本地用 Compose 模拟生产环境,再迁移到 K8s。
-
多项目怎么隔离? 用
--project-name参数或者不同目录,避免网络冲突。 -
性能问题? 本地开发时,数据库用命名卷比绑定挂载快 30%-50%,因为避免了文件系统翻译开销。
-
下一步学什么? 看完这篇,你应该能搞定中小型项目。想进阶,学习 Dockerfile 多阶段构建、Compose 的
extends字段、以及如何用docker stack deploy部署到 Swarm。
最后一句: 别相信"写个 yaml 就完事"的教程。Docker Compose 真正的价值在于帮你把"跑起来"变成"稳定跑起来"。上面 5 个坑,我每个都熬到凌晨 3 点。现在你知道了,希望你别熬。

评论已关闭!