Docker Swarm Java 应用部署与平滑更新
- [📘 一、概述](#📘 一、概述)
- [🧩 二、准备工作](#🧩 二、准备工作)
-
- 1、docker-compose-swarm.yml
- 2、nginx.conf
- [3、prod.sh + Dockerfile(构建并推送镜像)](#3、prod.sh + Dockerfile(构建并推送镜像))
- 4、diagnose.sh(诊断脚本,可选)
- [🚀 三、首次部署](#🚀 三、首次部署)
- [🔄 四、平滑更新 Java 服务](#🔄 四、平滑更新 Java 服务)
- [🧰 五、问题排查与回滚](#🧰 五、问题排查与回滚)
- [✅ 六、总结](#✅ 六、总结)
📘 一、概述
本文介绍如何使用 Docker Swarm 集群部署 Java 应用,并实现 零停机的平滑滚动更新。
适用于中小型分布式系统、微服务架构项目的生产环境。
🧩 二、准备工作
1、docker-compose-swarm.yml
说明:Swarm 不支持 docker-compose build,因此需要提前构建并推送镜像到远程仓库(如腾讯云容器镜像服务)。
yaml
version: "3.9"
services:
redis:
image: redis:7.2
networks:
- app-net
deploy:
replicas: 1
restart_policy:
condition: on-failure
max_attempts: 3
resources:
limits:
memory: 512M
reservations:
memory: 256M
java-app:
image: ccr.ccs.tencentyun.com/lensung_supply/aigc-api-aiyunhua:aigc-api-assistant-1.0.0
depends_on:
- redis
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
ports:
- "9799:9799"
networks:
- app-net
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9799/actuator/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 90s
deploy:
replicas: 2 # 至少两个副本实现平滑切换
restart_policy:
condition: on-failure
max_attempts: 3
update_config:
parallelism: 1 # 每次更新1个副本
delay: 15s # 每两个副本更新间隔15秒
monitor: 60s # 监控健康状态60秒
failure_action: rollback
order: start-first # 先启动新副本,再停止旧副本
resources:
limits:
memory: 2G
reservations:
memory: 1G
nginx:
image: nginx:1.25
depends_on:
- java-app
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/logs:/var/log/nginx
ports:
- "8000:80"
networks:
- app-net
deploy:
replicas: 1
restart_policy:
condition: on-failure
max_attempts: 3
resources:
limits:
memory: 256M
reservations:
memory: 128M
networks:
app-net:
driver: overlay
🟡 小贴士:
⚠️ Swarm 环境不支持 container_name,会自动命名副本。
💡 如果 Java 服务启动较慢,start_period 可调大到 120s。
✅ 建议在各节点上提前执行 docker login ccr.ccs.tencentyun.com。
2、nginx.conf
支持 CORS、WebSocket、Gzip 压缩,转发到 Swarm 服务名 java-app。
bash
user nginx;
worker_processes auto;
events { worker_connections 1024; }
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
gzip on;
gzip_min_length 1k;
gzip_comp_level 2;
gzip_types text/plain text/css application/javascript application/json application/xml;
gzip_vary on;
map $http_upgrade $connection_upgrade {
default keep-alive;
websocket upgrade;
}
server {
listen 80;
server_name aigc.aiyunhua.com;
location /v1 {
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Headers Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Requested-With,token,platform,satoken always;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS,HEAD,PUT,DELETE always;
add_header Access-Control-Allow-Credentials true always;
if ($request_method = OPTIONS) { return 200; }
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://java-app:9799;
proxy_redirect off;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location / { return 403; }
}
}
3、prod.sh + Dockerfile(构建并推送镜像)
bash
#!/bin/bash -v
buildPath="ccr.ccs.tencentyun.com/lensung_supply/aigc-api-aiyunhua"
name="aigc-api-assistant"
version="1.0.1"
git pull
#mvn clean install package -Dmaven.test.skip=true
docker login --username=100009090170 --password=hzls2020 ccr.ccs.tencentyun.com
docker build -t $buildPath:$name-$version --platform=linux/amd64 .
docker push $buildPath:$name-$version
docker rmi $buildPath:$name-$version
4、diagnose.sh(诊断脚本,可选)
此脚本可快速检测:
- Swarm 集群状态
- 服务副本是否健康
- 镜像版本是否匹配
- 网络配置是否存在
bash
#!/bin/bash
# Docker Swarm 服务诊断脚本
# 使用方法: ./diagnose.sh
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
STACK_NAME="aigc-stack"
SERVICE_NAME="${STACK_NAME}_java-app"
CORRECT_IMAGE="ccr.ccs.tencentyun.com/lensung_supply/aigc-api-aiyunhua/aigc-api-assistant-1.0.0"
echo -e "${BLUE}=== Docker Swarm 服务诊断 ===${NC}"
echo ""
# 1. 检查Swarm状态
echo -e "${BLUE}1. 检查Docker Swarm状态${NC}"
if docker info | grep -q "Swarm: active"; then
echo -e "${GREEN}✓ Docker Swarm已激活${NC}"
else
echo -e "${RED}✗ Docker Swarm未激活${NC}"
echo -e "${YELLOW}请执行: docker swarm init${NC}"
exit 1
fi
# 2. 检查服务状态
echo -e "\n${BLUE}2. 检查服务状态${NC}"
docker service ls --filter name="${STACK_NAME}_"
# 3. 检查Java服务详情
echo -e "\n${BLUE}3. 检查Java服务详情${NC}"
if docker service ls | grep -q "$SERVICE_NAME"; then
echo -e "${YELLOW}当前镜像地址:${NC}"
docker service inspect "$SERVICE_NAME" --format "{{.Spec.TaskTemplate.ContainerSpec.Image}}"
echo -e "\n${YELLOW}期望镜像地址:${NC}"
echo "$CORRECT_IMAGE"
# 检查镜像地址是否匹配
CURRENT_IMAGE=$(docker service inspect "$SERVICE_NAME" --format "{{.Spec.TaskTemplate.ContainerSpec.Image}}")
if [[ "$CURRENT_IMAGE" == "$CORRECT_IMAGE" ]]; then
echo -e "${GREEN}✓ 镜像地址正确${NC}"
else
echo -e "${RED}✗ 镜像地址不匹配${NC}"
echo -e "${YELLOW}需要更新服务镜像${NC}"
fi
else
echo -e "${RED}✗ 服务 $SERVICE_NAME 不存在${NC}"
fi
# 4. 检查服务副本状态
echo -e "\n${BLUE}4. 检查服务副本状态${NC}"
if docker service ls | grep -q "$SERVICE_NAME"; then
docker service ps "$SERVICE_NAME" --no-trunc
else
echo -e "${RED}✗ 无法检查副本状态${NC}"
fi
# 5. 检查镜像是否存在
echo -e "\n${BLUE}5. 检查镜像是否存在${NC}"
if docker images | grep -q "aigc-api-aiyunhua"; then
echo -e "${GREEN}✓ 本地存在相关镜像${NC}"
docker images | grep "aigc-api-aiyunhua"
else
echo -e "${YELLOW}! 本地不存在相关镜像${NC}"
fi
# 6. 检查网络
echo -e "\n${BLUE}6. 检查网络${NC}"
docker network ls --filter name="${STACK_NAME}_"
# 7. 提供修复建议
echo -e "\n${BLUE}=== 修复建议 ===${NC}"
# 检查镜像地址问题
CURRENT_IMAGE=$(docker service inspect "$SERVICE_NAME" --format "{{.Spec.TaskTemplate.ContainerSpec.Image}}" 2>/dev/null || echo "")
if [[ "$CURRENT_IMAGE" != "$CORRECT_IMAGE" ]]; then
echo -e "${YELLOW}1. 镜像地址不匹配,需要更新服务:${NC}"
echo -e " docker service update --image $CORRECT_IMAGE $SERVICE_NAME"
fi
# 检查副本问题
REPLICAS=$(docker service ls --filter name="$SERVICE_NAME" --format "{{.Replicas}}" 2>/dev/null || echo "0/0")
if [[ "$REPLICAS" == "0/"* ]]; then
echo -e "${YELLOW}2. 副本数为0,可能的原因:${NC}"
echo -e " - 镜像拉取失败"
echo -e " - 健康检查失败"
echo -e " - 资源不足"
echo -e " - 配置错误"
echo -e "\n${YELLOW}建议检查服务日志:${NC}"
echo -e " docker service logs $SERVICE_NAME"
fi
echo -e "\n${YELLOW}3. 如果问题持续,建议重新部署:${NC}"
echo -e " docker stack rm $STACK_NAME"
echo -e " docker stack deploy -c docker-compose-swarm.yml $STACK_NAME"
echo -e "\n${GREEN}=== 诊断完成 ===${NC}"
🚀 三、首次部署
bash
# 初始化 Swarm
docker swarm init
# 登录镜像仓库
docker login ccr.ccs.tencentyun.com
# 拉取镜像
docker pull ccr.ccs.tencentyun.com/lensung_supply/aigc-api-aiyunhua:aigc-api-assistant-1.0.0
# 部署服务
docker stack deploy -c docker-compose-swarm.yml aigc-stack
# 查看服务状态
docker service ls
# 查看 Java 应用副本详情
docker service ps aigc-stack_java-app
# 查看运行日志
docker logs -f <容器ID>
# 删除整个堆栈
docker stack rm aigc-stack
🔄 四、平滑更新 Java 服务
可使用版本号迭代(如 1.0.0 → 1.0.1 → 1.0.2),也可使用固定的 latest 标签。
bash
docker service update \
--image ccr.ccs.tencentyun.com/lensung_supply/aigc-api-aiyunhua:aigc-api-assistant-1.0.1 \
--update-parallelism 1 \
--update-delay 15s \
--update-monitor 60s \
--update-failure-action rollback \
--update-order start-first \
aigc-stack_java-app
✅ 更新策略说明
| 参数 | 含义 | 推荐值 |
|---|---|---|
--update-parallelism |
同时更新副本数 | 1 |
--update-delay |
两次更新间隔 | 10--20s |
--update-monitor |
健康检测周期 | 60s |
--update-failure-action |
更新失败操作 | rollback |
--update-order |
更新顺序 | start-first |
🧰 五、问题排查与回滚
| 场景 | 排查命令 | 解决方式 |
|---|---|---|
| 新镜像无法拉取 | docker pull |
确认仓库登录权限 |
| 服务更新失败 | docker service ps |
查看退出状态与错误 |
| 健康检查失败 | docker logs |
检查启动慢、依赖未连接 |
| 强制回滚 | docker service rollback aigc-stack_java-app |
回到上一个稳定版本 |
✅ 六、总结
| 阶段 | 关键点 | 说明 |
|---|---|---|
| 镜像构建 | 确保远程仓库可访问 | 否则 Swarm 管理节点无法获取 digest |
| 健康检查 | /actuator/health 必须返回 200 |
影响滚动更新的成功率 |
| 滚动更新 | start-first + 2 副本 |
保证无中断平滑切换 |
| 自动回滚 | failure_action: rollback |
避免错误镜像影响生产 |