集群环境下 Tomcat 自动化部署的核心是:Nginx 流量切分 + 分批滚动 + 健康检查 + 版本回滚 + 会话共享,全程零停机、可回滚、可审计。下面给你一套可直接落地的生产级方案。
一、生产环境必备前提(先做这几步)
1. Tomcat 集群基础配置
-
关闭自动部署(避免误触发)xml
<!-- conf/server.xml Host 节点 --> <Host name="localhost" appBase="webapps" autoDeploy="false" deployOnStartup="false" unpackWARs="true"> -
开启 Manager 文本接口(用于脚本调用)xml
<!-- conf/tomcat-users.xml --> <role rolename="manager-script"/> <user username="deploy" password="StrongPass123" roles="manager-script"/> -
会话共享(避免热部署时用户掉线)
- 方案 1:Redis 存储 Session(推荐)
- 方案 2:Tomcat 集群 Session 复制(小集群可用)
2. Nginx 负载均衡配置(关键)
nginx
# /etc/nginx/nginx.conf 或 sites-available/xxx
upstream tomcat_cluster {
server 192.168.1.101:8080 max_fails=2 fail_timeout=30s;
server 192.168.1.102:8080 max_fails=2 fail_timeout=30s;
server 192.168.1.103:8080 max_fails=2 fail_timeout=30s;
keepalive 64;
}
server {
listen 80;
location / {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
二、自动化部署核心方案(3 种主流)
方案 1:Shell 脚本 + Nginx 流量切分(最通用)
1. 集群部署脚本(deploy_cluster.sh)
bash
运行
#!/bin/bash
# 集群零停机部署:Nginx 下线 → 部署 → 健康检查 → 上线
# 用法:./deploy_cluster.sh myapp /path/to/app.war
APP_NAME=$1
WAR_PATH=$2
# 集群节点列表
TOMCAT_NODES=("192.168.1.101:8080" "192.168.1.102:8080" "192.168.1.103:8080")
NGINX_CONF="/etc/nginx/nginx.conf"
DEPLOY_USER="deploy"
DEPLOY_PASS="StrongPass123"
BACKUP_DIR="/data/tomcat_backup/${APP_NAME}"
LOG_FILE="/var/log/deploy_${APP_NAME}_$(date +%Y%m%d).log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE; }
# 1. Nginx 下线节点
nginx_offline() {
local node=$1
log "下线节点: $node"
sed -i "s/server $node;/server $node backup;/g" $NGINX_CONF
nginx -s reload
sleep 3
}
# 2. Nginx 上线节点
nginx_online() {
local node=$1
log "上线节点: $node"
sed -i "s/server $node backup;/server $node;/g" $NGINX_CONF
nginx -s reload
}
# 3. 健康检查
health_check() {
local node=$1
local url="http://$node/$APP_NAME/health"
log "健康检查: $url"
for i in {1..10}; do
code=$(curl -s -o /dev/null -w "%{http_code}" $url)
if [ $code -eq 200 ]; then
log "健康检查通过"
return 0
fi
log "第 $i 次失败,5秒后重试"
sleep 5
done
log "健康检查失败"
return 1
}
# 4. 部署单个节点
deploy_node() {
local node=$1
local ip=$(echo $node | cut -d: -f1)
local port=$(echo $node | cut -d: -f2)
nginx_offline $node
# 热部署
deploy_result=$(curl -u $DEPLOY_USER:$DEPLOY_PASS \
-T $WAR_PATH \
"http://$ip:$port/manager/text/deploy?path=/$APP_NAME&update=true")
if [[ $deploy_result != *OK* ]]; then
log "部署失败: $deploy_result"
nginx_online $node
return 1
fi
# 健康检查
if ! health_check $node; then
log "回滚上一版本"
last_war=$(ls -t $BACKUP_DIR/${APP_NAME}_*.war | head -1)
curl -u $DEPLOY_USER:$DEPLOY_PASS -T $last_war \
"http://$ip:$port/manager/text/deploy?path=/$APP_NAME&update=true"
nginx_online $node
return 1
fi
nginx_online $node
log "节点 $node 部署完成"
return 0
}
# 主流程
mkdir -p $BACKUP_DIR
# 备份当前版本(取第一个节点)
first_node=${TOMCAT_NODES[0]}
first_ip=$(echo $first_node | cut -d: -f1)
first_port=$(echo $first_node | cut -d: -f2)
backup_war="${BACKUP_DIR}/${APP_NAME}_$(date +%Y%m%d%H%M%S).war"
curl -u $DEPLOY_USER:$DEPLOY_PASS \
"http://$first_ip:$first_port/manager/text/undeploy?path=/$APP_NAME" >/dev/null 2>&1
curl -u $DEPLOY_USER:$DEPLOY_PASS \
"http://$first_ip:$first_port/manager/text/deploy?path=/$APP_NAME&war=file:/opt/tomcat/webapps/${APP_NAME}.war" \
-o $backup_war 2>&1
log "已备份: $backup_war"
# 分批部署所有节点
for node in "${TOMCAT_NODES[@]}"; do
if ! deploy_node $node; then
log "部署终止,集群异常"
exit 1
fi
sleep 5
done
log "✅ 集群部署全部完成"
exit 0
2. 使用方式
bash
运行
chmod +x deploy_cluster.sh
./deploy_cluster.sh myapp /data/build/myapp.war
方案 2:Ansible 批量部署(大规模集群首选)
1. inventory 配置(hosts)
ini
[tomcat_cluster]
192.168.1.101
192.168.1.102
192.168.1.103
[tomcat_cluster:vars]
app_name=myapp
war_path=/data/build/myapp.war
deploy_user=deploy
deploy_pass=StrongPass123
2. Ansible Playbook(deploy_tomcat.yml)
yaml
- name: 零停机部署 Tomcat 集群
hosts: tomcat_cluster
serial: 1 # 一次只部署1台,分批滚动
become: yes
vars:
nginx_conf: /etc/nginx/nginx.conf
backup_dir: /data/tomcat_backup/{{ app_name }}
tasks:
- name: 创建备份目录
file: path={{ backup_dir }} state=directory
- name: Nginx 下线当前节点
command: sed -i "s/server {{ inventory_hostname }}:8080;/server {{ inventory_hostname }}:8080 backup;/g" {{ nginx_conf }}
delegate_to: nginx_server
- name: 重载 Nginx
command: nginx -s reload
delegate_to: nginx_server
- name: 热部署 WAR
uri:
url: "http://{{ inventory_hostname }}:8080/manager/text/deploy?path=/{{ app_name }}&update=true"
method: PUT
user: "{{ deploy_user }}"
password: "{{ deploy_pass }}"
src: "{{ war_path }}"
status_code: 200
- name: 健康检查
uri:
url: "http://{{ inventory_hostname }}:8080/{{ app_name }}/health"
status_code: 200
retries: 10
delay: 5
- name: Nginx 上线当前节点
command: sed -i "s/server {{ inventory_hostname }}:8080 backup;/server {{ inventory_hostname }}:8080;/g" {{ nginx_conf }}
delegate_to: nginx_server
- name: 重载 Nginx
command: nginx -s reload
delegate_to: nginx_server
3. 执行部署
bash
运行
ansible-playbook -i hosts deploy_tomcat.yml
方案 3:Tomcat Parallel Deployment(蓝绿部署,零切换)
利用 Tomcat 内置的并行部署能力,同一路径同时运行两个版本,新流量切到新版本,旧版本无流量后自动下线。
1. 版本化 WAR 命名
plaintext
myapp##001.war # 旧版本
myapp##002.war # 新版本
2. 脚本部署新版本
bash
运行
# 部署新版本
curl -u deploy:StrongPass123 -T myapp##002.war \
"http://192.168.1.101:8080/manager/text/deploy?path=/myapp&update=true"
# 验证:访问 /myapp 自动路由到新版本
3. 优势
- 真正零停机,无需 Nginx 切流量
- 回滚只需删除新版本 WAR
- 适合对可用性要求极高的场景
三、生产环境关键保障措施
1. 版本管理与回滚
- 所有 WAR 包按版本命名:
myapp_v1.2.3.war - 保留最近 5 个版本,自动清理过期备份
- 回滚脚本:一键部署上一版本
2. 健康检查标准
- 提供
/health接口,返回 200 表示正常 - 检查内容:数据库连接、缓存、核心依赖
- 部署后最多重试 10 次,失败立即回滚
3. 权限与审计
- 脚本仅允许
deploy用户执行 - 所有操作记录日志,包含时间、节点、结果
- 集成 Jenkins/GitLab CI,实现一键触发
4. 会话不丢失
- 必须使用 Redis 等外部 Session 存储
- 避免依赖 Tomcat 本地 Session
四、常见问题与避坑
- Nginx 下线后仍有流量
- 检查 Nginx 配置是否正确 reload
- 增加
backup标记,确保流量不进入
- 健康检查超时
- 延长检查间隔(5 秒 / 次)
- 优化应用启动速度
- 内存泄漏
- 热部署后监控 JVM 堆内存
- 避免在应用中创建非守护线程
- 集群不一致
- 确保所有节点 Tomcat 版本、配置一致
- 用 Ansible 统一管理配置
五、方案对比与选型建议
表格
| 方案 | 适用场景 | 复杂度 | 零停机 | 回滚 |
|---|---|---|---|---|
| Shell + Nginx | 中小集群(3–5 节点) | 低 | ✅ | ✅ |
| Ansible | 大规模集群(>5 节点) | 中 | ✅ | ✅ |
| Parallel Deployment | 极致可用性场景 | 低 | ✅✅ | ✅✅ |
推荐组合:中小集群用 Shell 脚本;大规模集群用 Ansible;核心业务用 Parallel Deployment。