生产环境如何使用 Docker Compose 实现零停机更新

文章导读
生产环境用 Docker Compose 实现零停机更新,核心是配置 healthcheck 健康检查 + deploy.update_config.order 设为 start-first,配合优雅关闭逻辑,适合单主机多副本场景;若需要更完整的滚动更新能力,建议结合 Docker Swarm 或迁移到 Kubernetes。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 参考来源
A A

生产环境用 Docker Compose 实现零停机更新,核心是配置 healthcheck 健康检查 + deploy.update_config.order 设为 start-first,配合优雅关闭逻辑,适合单主机多副本场景;若需要更完整的滚动更新能力,建议结合 Docker Swarm 或迁移到 Kubernetes。

先说结论:Docker Compose 可以通过健康检查和更新顺序配置实现最小化中断更新,但原生滚动更新能力有限,生产环境需配合外部负载均衡或考虑 Swarm 模式。

  • 适合:单主机多副本服务、有健康检查接口的应用、可优雅关闭的服务
  • 先准备:配置 healthcheck、设置 stop_grace_period、确保应用监听 SIGTERM 信号
  • 验收:更新过程中持续请求测试、检查容器状态、验证日志无错误

命令速用版

基础更新命令(后台执行,避免阻塞):

docker-compose up -d `--no-deps` <service_name>

带构建的更新(代码变更后):

docker-compose up -d `--build` `--no-deps` <service_name>

查看容器健康状态:

docker-compose ps

检查特定容器健康详情:

docker inspect `--format`='{{.State.Health.Status}}' <container_id>

为什么会这样

Docker Compose 执行更新时,默认行为是先停止旧容器再启动新容器,这会导致短暂的服务中断。要实现零停机,关键在于改变这个顺序:让新容器先启动并通过健康检查,确认就绪后再停止旧容器。

这个过程依赖三个条件:第一,应用需要提供健康检查接口(如/healthz),让 Compose 能判断容器是否真正就绪;第二,应用需要支持优雅关闭,收到 SIGTERM 信号后完成正在处理的请求再退出;第三,需要有多个副本或外部负载均衡,确保更新期间始终有可用实例处理流量。

需要注意的是,Docker Compose 本身的滚动更新能力不如 Kubernetes 或 Docker Swarm 完整,单副本服务即使配置了 start-first 也可能出现短暂不可用。

分步处理

第一步:配置健康检查

在 docker-compose.yml 中为服务添加 healthcheck,确保 Compose 能检测容器是否真正可用:

services:
  web:
    image: myapp:v2
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 40s

检查点:执行 docker-compose up 后,用 docker-compose ps 查看状态列应显示 (healthy)。

第二步:配置更新策略

生产环境如何使用 Docker Compose 实现零停机更新

使用 deploy 配置控制更新顺序和并行度(注意:deploy 在 Swarm 模式下才完全生效,普通 Compose 部分支持):

services:
  web:
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first

关键点:order: start-first 确保新容器启动并健康后,旧容器才会被停止。

第三步:配置优雅关闭

设置 stop_grace_period 给容器足够时间完成正在处理的请求:

services:
  web:
    stop_grace_period: 30s

同时确保应用代码监听 SIGTERM 信号,收到后完成当前请求再退出。以 Go 服务为例,需要捕获 syscall.SIGTERM 并调用 server.Shutdown()。

第四步:执行更新

更新镜像版本后执行:

docker-compose pull
docker-compose up -d `--no-deps` web

回滚提醒:如果更新后发现问题,立即执行 docker-compose up -d `--no-deps` web 回退到上一版本镜像(需提前保留旧镜像)。

怎么验证是否生效

检查容器健康状态

docker-compose ps
# 状态列应显示 (healthy) 而非 (starting) 或 (unhealthy)

持续请求测试

在另一个终端执行持续请求,观察更新过程中是否有失败:

while true; do curl -s http://localhost:80/healthz; echo; sleep 1; done

更新期间不应出现连接拒绝或 500 错误。

生产环境如何使用 Docker Compose 实现零停机更新

查看容器启动顺序

docker ps `--format` "table {{.Names}}\t{{.Status}}\t{{.CreatedAt}}"
# 确认新容器创建时间在旧容器停止之前

检查应用日志

docker-compose logs `--tail`=100 web
# 确认无异常错误,优雅关闭日志正常

常见坑

单副本服务的局限

即使配置了 start-first,单副本服务在新旧容器切换瞬间仍可能出现短暂不可用。生产环境建议至少 2 个副本,或配合外部负载均衡使用。

deploy 配置的兼容性

deploy 配置在 Docker Compose v3+ 中定义,但完整功能需要 Docker Swarm 模式。普通 docker-compose up 可能忽略部分 deploy 参数,需提前测试验证。

健康检查接口缺失

如果应用没有健康检查端点,healthcheck 配置无法生效。需要先在代码中实现/healthz 或类似接口,返回 200 状态码表示服务就绪。

数据库迁移风险

零停机更新只解决服务层连续性问题,数据库结构变更可能新旧版本不兼容。建议采用向后兼容的迁移策略,或单独安排数据库维护窗口。

资源不足导致启动失败

start-first 策略会同时运行新旧容器,需要确保主机有足够资源(CPU、内存)支撑临时加倍的容器数量,否则新容器可能启动失败导致更新中断。

参考来源

  • CSDN 博客 - Docker Compose 服务如何实现零 downtime 更新?90% 工程师忽略的 3 个关键步骤
  • CSDN 博客 - 【Docker Compose 平滑更新实战指南】:掌握零停机部署的 5 大核心技巧
  • CSDN 博客 - 【资深架构师亲授】:如何用 docker-compose up `--build` 实现零停机发布?
  • 阿里云开发者社区 - 零停机部署 Go 服务:用 Docker Compose + 滚动更新,上线如换轮胎不停车
  • GitCode - 3 步实现零停机!Docker Compose 持续部署全流程解析