Docker Compose 编排实战

2026-05-05 20:16 Docker Compose 编排实战已关闭评论

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. 延伸思考

  1. 生产环境怎么办? Docker Compose 适合开发/测试,生产环境建议用 Kubernetes 或 Docker Swarm。但你可以在本地用 Compose 模拟生产环境,再迁移到 K8s。

  2. 多项目怎么隔离?--project-name 参数或者不同目录,避免网络冲突。

  3. 性能问题? 本地开发时,数据库用命名卷比绑定挂载快 30%-50%,因为避免了文件系统翻译开销。

  4. 下一步学什么? 看完这篇,你应该能搞定中小型项目。想进阶,学习 Dockerfile 多阶段构建、Compose 的 extends 字段、以及如何用 docker stack deploy 部署到 Swarm。


最后一句: 别相信"写个 yaml 就完事"的教程。Docker Compose 真正的价值在于帮你把"跑起来"变成"稳定跑起来"。上面 5 个坑,我每个都熬到凌晨 3 点。现在你知道了,希望你别熬。

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://teachcourse.cn/4099.html ,谢谢支持!

资源分享

一键pdf转文本工具 一键pdf转文本工具
Android Studio如何快速更改目录结构和包名? Android Studio如何快速更改目
009-ubuntu系统nano命令语法实例 009-ubuntu系统nano命令语法实例
Android学习笔记七:Java源码深入学习 Android学习笔记七:Java源码深

评论已关闭!