← 返回博客

Docker Compose 部署实战:从开发到生产的配置演进

Docker Compose 是"能用"和"好用"差距最大的工具之一。本文从一份能跑起来的 docker-compose.yml 出发,逐步加入生产环境需要的配置:日志轮转、健康检查、网络隔离、数据备份、CI/CD 集成。附可复用的生产级配置模板。【点击查看生产级配置】

先说结论:Compose 是中小项目的最佳平衡点

Docker Compose 可能是 Docker 生态里最被低估的工具——不是因为功能不够强,而是因为大多数人只用了它 20% 的能力。

本文从一份”能跑”的 docker-compose.yml 开始,逐步演进到一份”生产就绪”的配置。每步都说明为什么加、加了以后解决什么问题。


第一阶段:能跑(开发环境)

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret

这能跑,但仅限本地开发。问题很多:密码明文、没持久化、没健康检查、日志不限制、网络不隔离。直接上生产的话,几天内就会出问题。


第二阶段:加持久化和配置管理

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net
  db:
    image: postgres:16
    env_file: .env
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-net

volumes:
  pg-data:

networks:
  app-net:

三个关键改动:

  1. 命名卷pg-data 是 Docker 管理的命名卷,不会因为 docker compose down 丢失数据。比 bind mount 更安全(不需要关心宿主机路径),而且方便备份。

  2. 健康检查depends_on 加了 condition: service_healthy,确保 app 服务在数据库真正就绪后才启动,而不是在容器启动后就启动。不这么做的话,app 启动时数据库还没准备好,会导致 app 崩溃重启。

  3. 网络隔离:创建一个内部网络 app-net,只有这个网络里的服务才能互相访问。外部的请求只能通过 app 暴露的 3000 端口进入,数据库不暴露端口,安全性高了一个等级。


第三阶段:加日志轮转和重启策略

services:
  app:
    build: .
    env_file: .env
    restart: unless-stopped
    logging:
      driver: "local"
      options:
        max-size: "10m"
        max-file: "3"
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net
  db:
    image: postgres:16
    env_file: .env
    restart: unless-stopped
    logging:
      driver: "local"
      options:
        max-size: "10m"
        max-file: "3"
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-net

日志轮转是最容易被忽视的配置。Docker 默认的 json-file 驱动会把所有日志写到一个文件里,不做任何限制。如果容器每天产生几百 MB 日志,几天后就会占满磁盘。这不是理论问题——我见过好几次因为日志撑爆磁盘导致服务宕机的事故。

配置 logging.driver: "local" 并限制 max-size: 10mmax-file: 3,每个容器最多保留 30MB 日志,轮转自动进行。

restart: unless-stopped 确保服务在崩溃后自动重启,但如果你手动停了它,Docker 不会自作主张给你重启。


第四阶段:加备份和运维脚本

上面的配置日常运行够用了,但还有一个重要的事没做——数据库备份

#!/bin/bash
# backup-db.sh - 在宿主机上通过 cron 定时执行
BACKUP_DIR=/var/backups/postgres
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DB_CONTAINER=$(docker compose ps -q db)
docker exec $DB_CONTAINER pg_dump -U $POSTGRES_USER $POSTGRES_DB | gzip > $BACKUP_DIR/db_$TIMESTAMP.sql.gz
# 保留最近 30 天的备份,更早的自动删除
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete

然后设置 cron:

0 3 * * * /usr/local/bin/backup-db.sh

每天凌晨 3 点自动备份,保留 30 天。如果数据重要,还可以加一步把备份上传到对象存储:

aws s3 cp $BACKUP_DIR/db_$TIMESTAMP.sql.gz s3://my-backups/db/

第五阶段:加 CI/CD 集成

到这一步,部署应该变成一条命令:

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build and deploy
        run: |
          docker compose build
          docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

这里用了 docker-compose.prod.yml 覆盖生产环境的配置差异:

# docker-compose.prod.yml
services:
  app:
    environment:
      - NODE_ENV=production
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M

生产环境覆盖文件只写和环境相关的差异,核心配置留在 docker-compose.yml 里,避免两份配置不一致。


总结:一份生产就绪的 Compose 配置清单

配置项开发环境生产环境
持久化不必须命名卷
健康检查不必须必须
日志轮转不必须必须
重启策略不设置unless-stopped
网络隔离不必须必须
资源限制不必须建议
数据库备份不必须必须(cron + 远端存储)
CI/CD不必须推荐

Docker Compose 不是玩具,它的上限取决于你配置的完整度。 一份配了日志轮转、健康检查、网络隔离和备份策略的 Compose 文件,可以在单机部署场景下稳定跑很久。

需要 Docker 部署方案设计或 CI/CD 流水线搭建?联系我们,说清你的服务架构和部署环境,24 小时内回可行性。

相关阅读

常见问题

docker-compose.yml 适合生产环境吗?

适合中小规模项目。Docker Compose 的定位是单机编排,不是集群调度。如果你的项目部署在单台服务器上,或者只需要 3-5 个容器协作,Compose 是够用的,而且运维成本远低于 K8s。当你的服务数量超过 10 个、需要多机部署或自动扩缩容时,才值得考虑 K8s 或 Nomad。

.env 文件里的敏感信息怎么处理?

.env 文件不应该提交到 Git 仓库(加到 .gitignore)。生产环境的敏感信息建议用 Docker Secrets 或环境变量注入的方式传递。小团队可以用一个安全的 vault 脚本在部署时生成 .env 文件,CI/CD 工具(如 GitHub Actions Secrets)也可以直接注入环境变量,避免明文存储。

容器日志怎么处理?不处理会有什么问题?

默认的 json-file 驱动会将所有日志写到一个 JSON 文件里,不做任何限制。如果容器每天产生几百 MB 日志,几天后就会占满磁盘。生产环境一定要配置日志轮转:在 docker-compose.yml 里设置 logging.driver 为 local 或 json-file,并限制 max-size(如 10m)和 max-file(如 3)。更彻底的方案是使用日志采集工具(如 Loki + Promtail 或 Filebeat)将日志统一发送到集中存储。

容器里的数据库怎么备份?

不要直接在容器里跑 mysqldump 然后指望它持久化。推荐的做法是:① 数据库数据挂载到宿主机的命名卷(named volume),方便直接备份卷文件;② 写一个独立的备份脚本,在宿主机上定时执行(cron),用 docker exec 进入容器执行数据库导出,然后压缩上传到对象存储(如 S3、OSS);③ 定期验证备份的可恢复性——不能恢复的备份等于没有备份。

本文来自 AI Enable Harness 一线交付实践。需要同类系统或优化服务?

订阅博客更新

新文章发布后第一时间邮件通知。不定期发送,不推销。

订阅 →