← Back to Lab
// DevOps2026.07.205 min read

Docker Compose 热更新必现 502:排障与回滚实践

单机 Docker Compose 热更新必现 502 的根因(Nginx upstream 不重解析)与 Cloudflare IPv6 偶发失败的排障、回滚实践。

Docker Compose 热更新必现 502?一次生产环境滚动更新的排障与回滚实践

适用场景:单机 Docker Compose 部署「前端 + 后端 + Nginx 反代」的项目,需要频繁热更新。

一、背景

一个单机 Docker Compose 项目:Nginx 反代容器 + 前端容器 + 后端容器,数据走 Postgres / Redis 容器。开发期几乎每天热更新,于是把部署流程沉淀成了一套"可读、可回滚、不动密钥"的脚本。下面是两个高频坑。

二、坑 1:重建容器必现 502

现象

docker compose up -d 重建前后端容器后,访问前端 / 调 API 偶发 502 Bad Gateway;过一会儿或手动 reload 后恢复。

根因

Nginx 的 upstream 是用容器名(Docker 内部 DNS)定义的。Nginx 在启动时解析一次 upstream IP 并缓存,不会自动重解析。容器被 up -d 重建后,新容器拿到了新的 IP,而 Nginx 仍持有指向旧容器(已退出)的失效 upstream 句柄 → 502。

# nginx.conf(典型写法,启动即解析并缓存 upstream)
upstream backend {
    server backend:8000;   # 解析一次,容器重建后 IP 变了但 Nginx 不知道
}

解决

每次 up -d 之后,必执行 nginx -s reload 让它重新解析 upstream。把它写进部署脚本的硬步骤:

docker compose up -d
docker exec <nginx-container> nginx -s reload   # 关键:重建后必现 502 的解药

验证时盯一个指标:reload 后 502 条数应为 0。漏掉这步,前端页面能出但偶发 502,非常难查。

进阶:若想彻底免 reload,可在 Nginx 里用 resolver + 变量形式上游(set $upstream backend; proxy_pass http://$upstream;),让 Nginx 每次请求都走 DNS 解析。但单机场景,reload 一步最简单可靠。

三、坑 2:Cloudflare IPv6 ENETUNREACH

现象

前端构建阶段 pnpm install --frozen-lockfile 偶发失败:ENETUNREACH,连接的是一个 Cloudflare 的 IPv6 地址(2606:4700::...)。

根因

构建容器内部 IPv6 不可达,而 npm registry 偶尔把域名解析到 IPv6 地址。一次失败,重试走 IPv4 慢速重试后就成功,next build 也能正常产出静态页。

解决与边界

  • 构建失败 ≠ 部署失败:失败不会动 latest 镜像标签,运行中的容器零影响。直接重试即可。
  • 根治:在构建环境固定 registry 走 IPv4,或禁用 IPv6 解析(/etc/resolv.conf 去掉 IPv6 nameserver,或 nodefamily: 4)。
# 重试即可,不动运行容器
pnpm install --frozen-lockfile   # IPv4 重试通过后继续
docker compose up -d

四、回滚策略(部署前的标配动作)

  1. 部署前打回滚标签:给当前正常镜像打 pre-<date> 标签(仅引用旧镜像,近乎零空间);
  2. 干净暂存:上传代码时排除 .env / .venv / node_modules / .next,绝不动生产密钥与生产 compose;
  3. 构建 → up -d → nginx reload → 验证/health 直连 200、前端出真实 HTML、502=0、容器 healthy;
  4. 验证通过再清理:删回滚标签 + docker builder prune 回收悬空构建层,释放磁盘。

安全红线(永远不碰):运行中的容器、数据卷(PG / Redis / 业务数据目录)、.env 密钥、生产 compose 文件。

五、复盘

  • nginx -s reload打回滚标签 做成部署脚本的硬步骤,不要靠"记得";
  • 构建失败不等于部署失败——latest 标签没动,运行容器就不受影响,重试或换网络环境即可;
  • 回滚标签 + 构建缓存 prune 是低成本的空间回收手段,但只在验证通过后执行。

单机部署没有 K8s 的滚动更新兜底,这些"土办法"反而最稳。