跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Docker + Nginx 实现 Java 服务零停机发布,解决 502 问题

总结了在 Docker Compose 加 Nginx 架构下部署 SpringBoot 等 Java 服务时遇到的发布期间 502 错误问题。通过双实例兜底策略,结合 Nginx 的 proxy_next_upstream 自动重试机制以及健康检查脚本,实现了零停机发布。核心步骤包括配置 Nginx upstream 备用节点、编写 Docker Compose 定义主备服务、以及使用 Shell 脚本控制启动顺序和健康状态轮询。该方案适用于无 Kubernetes 环境的中小企业,确保发布过程用户无感知且接口稳定。

灵魂摆渡发布于 2026/3/30更新于 2026/9/1078 浏览

一、前言

在传统服务器部署中,我们更新后端服务通常是:

  • 停止旧容器
  • 启动新容器

这个过程会直接导致:

  • 发布期间接口 502 Bad Gateway
  • 用户访问中断、前端报错
  • 监控告警、接口超时失败

即使使用'先启动临时实例、再关闭旧实例',如果 Nginx 配置、健康检查、脚本逻辑不正确,依然会出现各种诡异问题:发布瞬间 502、脚本卡死、接口不通等。

本文带你从零搭建一套真正可用、无感知、零停机的发布方案。

二、遇到的典型问题(你大概率也踩过)

直接重启容器 → 必现 502

原因:主容器重启期间,Nginx 仍在转发请求,无可用后端服务。

加了 backup 副本 → 依然偶尔 502

upstream ring_servers {
    server ring:8080;
    server ring_temp:8080 backup;
}

原因:

  • backup 只有主节点完全挂掉才会切换
  • Nginx 没有失败重试机制,重启瞬间仍会丢请求
  • 健康检查不及时

发布脚本检查接口 → 直接卡死

原因:

  • 接口未就绪时,curl 阻塞等待
  • 检查公网入口受 Nginx 影响,无法真实判断服务状态

健康检查接口被登录拦截 → 返回 401

原因:健康接口走了登录校验,HTTP 200 但业务码 401,导致脚本误判。

三、整体零停机发布方案(通用架构)

核心思路:双实例兜底 + Nginx 自动重试 + 安全发布脚本

  1. 正常运行:主服务提供流量
  2. 发布时:先启动临时副本 → 等待就绪 → 重启主服务 → 主服务就绪 → 关闭临时副本
  3. Nginx:自动转发到可用节点,用户无感知

四、第一步:Nginx 配置(最关键,解决 502)

  1. upstream 负载均衡配置
upstream ring_servers {
    least_conn; # 主服务
    server ring:8080 max_fails=1 fail_timeout=1s;
    # 临时服务(发布兜底,backup 模式)
    server ring_temp:8080 max_fails=3 fail_timeout=30s backup;
    # 核心:出错自动重试下一个节点,用户看不到 502
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_next_upstream_tries 2;
    keepalive 32;
}
  1. 代理通用配置
proxy_connect_timeout 10s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
proxy_buffering on;

说明:proxy_next_upstream 是零停机的灵魂,请求失败会自动重试下一个节点。

五、第二步:docker-compose.yml 配置

只保留核心结构,可直接套用:

version: "3.9"
services:
  # 主服务
  ring:
    image: ring:1.0.0
    restart: always
    ports:
      - "8080:8080"
    networks:
      - webnet
    healthcheck:
      test: ["CMD", "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "http://localhost:8080/api/health"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 60s
  # 临时服务(发布用)
  ring_temp:
    image: ring:1.0.0
    restart: "no" # 不自动重启
    ports:
      - "8081:8080"
    networks:
      - webnet
networks:
  webnet:
    driver: bridge

六、第三步:生产可用零停机发布脚本

重要说明 脚本中使用的接口:/api/app/checkServerStatus。这只是一个样例接口,你可以替换为自己项目中能判断项目正常运行的任意接口,例如 /actuator/health、/api/health、/system/health。只要该接口能在服务启动完成后返回 HTTP 200 即可。

零停机发布脚本(最终版)

#!/bin/bash
set +e
# ==================== 配置项(根据自己项目修改) ====================
# 公网要监控的接口(验证用户是否能正常访问)
MONITOR_URL="https://你的域名/api/app/checkServerStatus"
# 主服务端口检查(不经过 Nginx)
MAIN_CHECK_URL="http://127.0.0.1:8080/api/app/checkServerStatus"
# 临时服务端口检查
TEMP_CHECK_URL="http://127.0.0.1:8081/api/app/checkServerStatus"
MAX_RETRY=30
RETRY_INTERVAL=5
# ====================================================================
# 后台实时监控公网接口
start_monitor(){
    while true; do
        current_time=$(date "+%Y-%m-%d %H:%M:%S")
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --insecure $MONITOR_URL 2>/dev/null)
        if [ "$HTTP_CODE" = "200" ]; then
            echo -e "\033[32m[${current_time}] ✅ 公网接口正常:200\033[0m"
        else
            echo -e "\033[31m[${current_time}] ❌ 公网接口异常:${HTTP_CODE}\033[0m"
        fi
        sleep 1
    done
}
# 检查端口服务是否就绪
check_service_ready(){
    local url=$1
    local desc=$2
    echo -e "\033[36m=== 等待 ${desc} 就绪 ===\033[0m"
    local retry=0
    while [ $retry -lt $MAX_RETRY ]; do
        code=$(curl -s -o /dev/null -w "%{http_code}" --insecure $url 2>/dev/null)
        if [ "$code" = "200" ]; then
            echo -e "\033[32m✅ ${desc} 启动成功\033[0m"
            return 0
        fi
        retry=$((retry+1))
        echo "重试 ${retry}/${MAX_RETRY},状态码:${code},等待 ${RETRY_INTERVAL}s..."
        sleep $RETRY_INTERVAL
    done
    echo -e "\033[31m❌ ${desc} 启动超时\033[0m"
    kill $MONITOR_PID 2>/dev/null
    exit 1
}
# ==================== 发布主流程 ====================
echo -e "\n==================== 开始零停机发布 ====================\n"

# 1. 启动后台监控
start_monitor &
MONITOR_PID=$!

# 2. 启动临时副本
echo -e "\n1. 启动临时服务"
docker compose up -d ring_temp
check_service_ready "$TEMP_CHECK_URL" "临时服务"

# 3. 重启主服务
echo -e "\n2. 重启主服务"
docker compose up -d --force-recreate ring
check_service_ready "$MAIN_CHECK_URL" "主服务"

# 4. 关闭临时服务
echo -e "\n3. 关闭临时服务"
docker compose stop ring_temp
docker compose rm -f ring_temp

# 5. 停止监控
kill $MONITOR_PID 2>/dev/null
sleep 1
echo -e "\n\033[32m=====================================================\033[0m"
echo -e "\033[32m✅ 发布完成!全程无 502,用户无感知\033[0m"
echo -e "\033[32m=====================================================\033[0m"
exit 0

七、第四步:使用与验证

  1. 赋予执行权限
chmod +x deploy_ring.sh
  1. 执行发布
./deploy_ring.sh
  1. 观察效果 你会看到:
  • 公网接口全程输出 ✅ 200
  • 主服务重启期间无任何 502
  • 发布完成后自动关闭临时副本
  • 用户完全无感知

八、核心避坑总结(非常重要)

  1. 必须先启动临时副本,再重启主服务
  2. Nginx 必须配置 proxy_next_upstream,这是无 502 的关键
  3. 健康检查不要检查公网入口,直接检查容器端口更稳定
  4. 临时服务使用 backup 模式,只做兜底不做负载,避免启动期间被流量打挂
  5. 健康接口尽量免登录,只返回 HTTP 200 即可,不要做业务校验
  6. 脚本中 /api/app/checkServerStatus 只是示例,替换成你自己的健康检查接口

九、适用场景

  • SpringBoot 后端服务
  • 单体应用 / 无微服务架构
  • Docker Compose 部署
  • 中小企业服务器、无 K8s 环境
  • 追求低成本、稳定、无感知发布

目录

  1. 一、前言
  2. 二、遇到的典型问题(你大概率也踩过)
  3. 三、整体零停机发布方案(通用架构)
  4. 四、第一步:Nginx 配置(最关键,解决 502)
  5. 五、第二步:docker-compose.yml 配置
  6. 主服务
  7. 临时服务(发布用)
  8. 六、第三步:生产可用零停机发布脚本
  9. ==================== 配置项(根据自己项目修改) ====================
  10. 公网要监控的接口(验证用户是否能正常访问)
  11. 主服务端口检查(不经过 Nginx)
  12. 临时服务端口检查
  13. ====================================================================
  14. 后台实时监控公网接口
  15. 检查端口服务是否就绪
  16. ==================== 发布主流程 ====================
  17. 1. 启动后台监控
  18. 2. 启动临时副本
  19. 3. 重启主服务
  20. 4. 关闭临时服务
  21. 5. 停止监控
  22. 七、第四步:使用与验证
  23. 八、核心避坑总结(非常重要)
  24. 九、适用场景

更多推荐文章

查看全部
  • 华为预训练大模型白皮书核心观点与技术趋势分析
  • Anaconda 开始菜单快捷方式丢失及 mkmenus 报错修复
  • VSCode Copilot 配置文件提示警告分析与解决
  • 哈希表原理与实现:线性探测及链地址法
  • Stable Diffusion v2-1-base 模型使用指南与代码实践
  • PyOxidizer Python 部署打包工具使用指南
  • 清华团队发布 OpenClaw 研究报告:AI 智能体生态闭环解析
  • 华为 OD 机试双机位 C 卷 - 评委评分与选手排名
  • Node.js 下载安装及环境配置教程
  • Python 学习过程中的核心难点与分阶段突破指南
  • 大模型核心技术解析:提示工程、函数调用、RAG 与微调
  • Stable Diffusion Windows 环境搭建与使用指南
  • Ubuntu 20.04 系统下 QGroundControl 地面站安装教程
  • DockerHub 镜像加速配置指南(Windows、Mac、Linux)
  • 浏览器缓存机制详解与前端代码更新缓存解决方案
  • 扩散模型(Diffusion Model)原理与图像生成实战
  • macOS Homebrew 完全指南:安装、换源与核心命令速查
  • DeepSeek-R1 技术报告深度解读与推理能力进化分析
  • 次模函数(Submodular Function)核心概念与 AI 应用解析
  • Spring AI 集成 Ollama 与 LLaVA 实现多模态大模型应用实战

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online