平滑升级的核心逻辑是确保新容器就绪、流量无损切换、旧容器优雅退出;具体包括预检、启动新容器(临时端口)、等待/actuator/health返回up、nginx reload切换流量、sigterm停旧容器并清理,最后可重命名保持一致性。

明确平滑升级的核心逻辑
平滑升级不是简单地 docker stop && docker run,而是要确保新容器就绪、流量无损切换、旧容器优雅退出。Java 项目通常依赖 Spring Boot Actuator 的 /actuator/health 和 /actuator/liveness 端点做就绪探活,Shell 脚本需主动等待新容器健康后再下线旧容器。
脚本关键步骤设计
一个高效的一键升级脚本应包含以下环节:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
-
预检阶段:校验 Docker 是否运行、镜像是否存在、必要环境变量(如
APP_NAME、IMAGE_TAG)是否设置 -
启动新容器:使用
--name ${APP_NAME}-new启动,绑定临时端口或通过反向代理(如 Nginx、Traefik)做服务发现,避免端口冲突 -
健康等待:循环调用
curl -sf http://localhost:8080/actuator/health | jq -r '.status',直到返回"UP"(建议加超时和重试上限) - 流量切换:若用 Nginx,reload 配置指向新容器;若用 Docker 内部 DNS 或 Service Mesh,更新标签或 endpoint
-
旧容器清理:确认新容器稳定运行 30 秒后,执行
docker stop ${APP_NAME}-old && docker rm ${APP_NAME}-old
推荐轻量级实现方式(不依赖编排工具)
以下为可直接复用的 Shell 片段核心逻辑(适配 Spring Boot 默认配置):
#!/bin/bash
APP_NAME="my-java-app"
IMAGE="registry.example.com/myapp"
TAG="${1:-latest}"
OLD_CONTAINER="${APP_NAME}-old"
NEW_CONTAINER="${APP_NAME}-new"
<h1>启动新容器(禁用 restart,用脚本控制生命周期)</h1><p>docker run -d \
--name "$NEW_CONTAINER" \
--network app-net \
-e SPRING_PROFILES_ACTIVE=prod \
-p 8081:8080 \
"$IMAGE:$TAG"</p><h1>等待健康就绪(最多 60 秒,每 2 秒检查一次)</h1><p>for i in $(seq 1 30); do
if curl -sf "<a href="https://www.php.cn/link/5de356e0a5585088cac93fde4c34a8eb">https://www.php.cn/link/5de356e0a5585088cac93fde4c34a8eb</a>" | grep -q '"status":"UP"'; then
echo "✅ New container is UP"
break
fi
sleep 2
if [ $i -eq 30 ]; then
echo "❌ Health check timeout, aborting"
docker stop "$NEW_CONTAINER" && docker rm "$NEW_CONTAINER"
exit 1
fi
done</p><h1>切换流量(示例:Nginx reload)</h1><p>if command -v nginx > /dev/null; then
sed -i "s/:8080/:8081/g" /etc/nginx/conf.d/app.conf
nginx -t && nginx -s reload
fi</p><h1>停旧容器(先发 SIGTERM,等 10 秒再强制 kill)</h1><p>docker stop "$OLD_CONTAINER" 2>/dev/null || true
sleep 10
docker rm "$OLD_CONTAINER" 2>/dev/null || true</p><h1>重命名新容器为旧名,保持一致性(可选)</h1><p>docker rename "$NEW_CONTAINER" "$OLD_CONTAINER"
</p>
增强健壮性的实用技巧
让脚本在生产更可靠:
- 使用
set -euo pipefail开头,遇到错误立即退出 - 对所有
docker命令加|| true或显式错误处理,避免单步失败中断流程 - 记录操作日志到
/var/log/${APP_NAME}-deploy.log,含时间戳和容器 ID - 升级前自动备份当前容器的 JVM 参数、JVM 运行时信息(如
docker exec $OLD_CONTAINER jstat -gc $(pgrep java)) - 支持回滚:将旧镜像 tag 存入文件,失败时快速拉起上一版
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










