←
Blog
Awshgreen
archive
Article · 2026-09-22

Docker Compose 上线前检查清单:配置、日志、健康检查与回滚

Docker Compose 很适合单机部署:应用、数据库、缓存和反向代理可以用一组配置描述清楚。但开发环境里的 compose.yaml 往往不能直接拿去生产。生产环境最重要的不是“能不能 up -d”,而是升级、日志、数据和恢复路径是否清楚。

本文用两个 Compose 文件演示一种简单的分层方法:基础文件描述服务本身,生产文件只覆盖上线所需的差异。

基础文件和生产覆盖文件分开

基础 compose.yaml 可以保持适合本地开发:

services:
  app:
    build: .
    expose:
      - "8000"
    environment:
      APP_ENV: development
    volumes:
      - .:/app

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: change-me-in-development
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

生产文件 compose.production.yaml 只写生产差异:

services:
  app:
    build: .
    env_file:
      - .env.production
    environment:
      APP_ENV: production
    volumes: []
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  db:
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 10s
      timeout: 5s
      retries: 5

read_only: true 不是所有应用都能直接使用。如果程序需要写缓存、上传文件或临时文件,就把这些位置单独挂载卷,不要为了省事把整个容器文件系统重新开放写入。

合并后的配置一定要先检查:

docker compose -f compose.yaml -f compose.production.yaml config

这个步骤可以提前发现缩进、变量和合并结果的问题,也能确认生产环境到底使用了哪些端口、卷和环境变量。

只把真正需要的端口暴露给宿主机

应用和数据库通常不应该直接暴露到公网。若只有 Nginx 或 Caddy 需要接受公网流量,应用可以只使用 Compose 网络:

services:
  app:
    expose:
      - "8000"

  proxy:
    image: nginx:stable
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      app:
        condition: service_healthy

expose 只提供给同一 Compose 网络中的服务,ports 才会映射到宿主机。数据库如果没有远程管理需求,甚至不需要 ports。

如果应用确实需要被宿主机上的反向代理访问,可以只绑定本机:

ports:
  - "127.0.0.1:8000:8000"

这样公网仍然只能通过反向代理访问。

重启策略不要和另一个进程管理器打架

对普通 Compose 服务,restart: unless-stopped 通常比手动写一个 systemd 无限重启脚本更直接。Docker 官方也提醒,不要同时把 Docker 重启策略和宿主机进程管理器叠加使用,否则两套机制可能互相拉起、互相停止。

重启策略解决的是“容器退出或 Docker 重启后怎么办”,不是应用健康本身。健康检查解决的是“容器还活着,但服务已经不能正常响应”的可观察性问题。两者用途不同,不要把一个当成另一个。

日志要有上限

开发环境里看 docker compose logs -f 很方便,生产环境如果不限制日志,磁盘迟早会被写满。可以给服务配置日志上限:

services:
  app:
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "5"

这不是完整的日志归档方案,但至少能避免单个容器无限吃掉磁盘。应用本身也应该降低无意义的 debug 日志,并为错误记录请求 ID、时间和服务名,方便把一次请求串起来。

一套相对稳妥的发布流程

# 1. 检查合并后的配置
docker compose -f compose.yaml -f compose.production.yaml config

# 2. 构建新镜像
docker compose -f compose.yaml -f compose.production.yaml build app

# 3. 只重建应用,不主动动数据库
docker compose -f compose.yaml -f compose.production.yaml up -d --no-deps app

# 4. 查看状态和最近日志
docker compose -f compose.yaml -f compose.production.yaml ps
docker compose -f compose.yaml -f compose.production.yaml logs --tail=100 app

如果应用有数据库迁移,先明确迁移是否向后兼容,再执行迁移。不要把“启动容器”和“删除数据卷”写进同一条随手复制的命令里。docker compose down 默认不会删除卷,但带上 -v 就会删除项目卷,这个选项必须单独确认。

备份不是把 Compose 文件复制一份

Compose 文件只描述如何启动服务,不能替代数据备份。至少要分别考虑:

  1. 数据库的逻辑备份,例如 PostgreSQL 的 pg_dump。
  2. 上传文件或媒体目录。
  3. .env.production、反向代理配置和 TLS 证书的安全备份。
  4. 镜像版本、Compose 文件和迁移脚本。

数据库备份完成后,最好在临时目录或另一台机器上做一次恢复演练。一个“备份命令成功”的文件,不等于已经证明可以恢复业务。

小服务器的最后检查

[ ] 生产环境没有把源码目录绑定进容器
[ ] 数据库没有无必要的公网端口
[ ] 环境变量没有提交到 Git
[ ] 容器日志有大小上限
[ ] 应用有健康检查
[ ] 镜像版本不是永远漂移的 latest
[ ] 只更新需要更新的服务
[ ] 数据库和上传文件有独立备份
[ ] 至少做过一次恢复测试

Compose 的价值不是让命令变短,而是让部署状态更容易被别人读懂、更容易在另一台机器上复现。把配置、数据、日志和发布动作分开,单机部署也可以很有秩序。

参考资料