本文目录导读:

在Shell脚本中实现分批发布策略,核心思想是控制每次更新的节点数量、设置观察窗口以及处理回滚逻辑。
下面是一个通用的、可维护的分批发布Shell脚本框架,包含核心功能和关键设计。
核心概念与流程
- 全量节点列表:定义所有需要部署的服务器IP或主机名。
- 批次大小:每批同时操作的节点数量(2台)。
- 批次间隔:每批部署完成后,等待观察的时间(60秒)。
- 健康检查:在每批部署后,自动检查应用是否正常。
- 回滚机制:如果某批次部署后健康检查失败,自动或手动触发回滚。
Shell脚本实现(通用框架)
#!/bin/bash
# ======================================================
# 文件名: batch_deploy.sh
# 描述: 实现分批发布的核心逻辑
# 用法: ./batch_deploy.sh [版本号] [回滚]
# ======================================================
# ---------- 配置区 ----------
# 1. 定义全量节点列表(IP或主机名,一行一个)
NODES=(
"10.0.0.1"
"10.0.0.2"
"10.0.0.3"
"10.0.0.4"
"10.0.0.5"
)
# 2. 分批参数
BATCH_SIZE=2 # 每批部署的节点数
BATCH_INTERVAL=60 # 批次间隔(秒),用于观察
DEPLOY_TIMEOUT=300 # 单节点部署超时时间(秒)
# 3. 路径配置
APP_HOME="/opt/myapp"
BACKUP_DIR="/opt/backups"
VERSION_FILE="/opt/.deploy_version" # 记录当前版本号
LOG_FILE="/var/log/deploy.log"
# ---------- 函数定义 ----------
# 日志函数
log() {
local level="$1"
local msg="$2"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] ${msg}" | tee -a "${LOG_FILE}"
}
# 单节点部署函数(此处为模拟,实际替换为scp + ssh命令)
deploy_node() {
local node="$1"
local version="$2"
log "INFO" "开始部署节点: ${node}, 版本: ${version}"
# --- 实际部署命令(示例) ---
# 1. 上传包:scp ./pkg/yourapp-${version}.tar.gz ${node}:${APP_HOME}/temp/
# 2. SSH执行:
# ssh "${node}" << 'EOF'
# # 停服 -> 备份旧包 -> 解压 -> 替换配置 -> 启动 -> 校验
# systemctl stop yourapp
# mv ${APP_HOME}/app ${APP_HOME}/app_backup
# tar -xzf ${APP_HOME}/temp/yourapp-${version}.tar.gz -C ${APP_HOME}
# systemctl start yourapp
# # 等待启动
# sleep 5
# EOF
# --- 模拟耗时 ---
sleep 2
log "INFO" "节点 ${node} 部署完成"
return 0
}
# 健康检查函数
health_check() {
local node="$1"
# --- 健康检查命令(示例) ---
# curl -s -o /dev/null -w "%{http_code}" http://${node}:8080/health
# 如果返回 200,则 return 0
log "INFO" "正在检查节点 ${node} 健康状态"
# 模拟检查(80%概率成功)
local rand_num=$(( RANDOM % 10 ))
if [ ${rand_num} -lt 8 ]; then
log "INFO" "节点 ${node} 健康检查通过"
return 0
else
log "ERROR" "节点 ${node} 健康检查失败"
return 1
fi
}
# 回滚单个节点
rollback_node() {
local node="$1"
local version="$2" # 待回滚的版本(旧版本)
log "WARN" "开始回滚节点: ${node}, 目标版本: ${version}"
# --- 回滚命令 ---
# ssh "${node}" "cd ${APP_HOME} && tar -xzf backup_${version}.tar.gz && systemctl restart yourapp"
sleep 1
log "INFO" "节点 ${node} 回滚完成"
}
# ---------- 主流程 ----------
main() {
local version="${1:-v1.0.0}" # 默认版本号
local rollback_mode="${2:-false}" # 是否为回滚模式
log "INFO" "========== 开始部署,版本: ${version} =========="
log "INFO" "批次大小: ${BATCH_SIZE}, 批次间隔: ${BATCH_INTERVAL}s"
# 记录总节点数
local total_nodes=${#NODES[@]}
local deployed_count=0
# 循环处理,每次取 BATCH_SIZE 个节点
while [ ${deployed_count} -lt ${total_nodes} ]; do
# 计算当前批次结束索引
local batch_end=$(( deployed_count + BATCH_SIZE - 1 ))
if [ ${batch_end} -ge ${total_nodes} ]; then
batch_end=$(( total_nodes - 1 ))
fi
# 提取当前批次节点
local batch_nodes=("${NODES[@]:${deployed_count}:$(( batch_end - deployed_count + 1 ))}")
log "INFO" "===== 当前批次节点: ${batch_nodes[*]} ====="
# 在后台并行部署当前批次节点
for node in "${batch_nodes[@]}"; do
(
deploy_node "${node}" "${version}"
) &
done
# 等待当前批次后台任务完成
wait
# ----- 健康检查阶段 -----
log "INFO" "开始对当前批次节点进行健康检查"
local batch_check_passed=true
for node in "${batch_nodes[@]}"; do
if ! health_check "${node}"; then
batch_check_passed=false
log "ERROR" "批次健康检查失败,开始回滚"
# 回滚逻辑: 回滚当前批次 + 之前已部署的节点
for nd in "${batch_nodes[@]}"; do
rollback_node "${nd}" "${version}"
done
# 回滚之前部署的所有节点(根据你的策略决定是否回滚全部)
for (( idx=0; idx<deployed_count; idx++ )); do
rollback_node "${NODES[idx]}"
done
log "ERROR" "部署已回滚,退出"
exit 1
fi
done
if [ "${batch_check_passed}" = true ]; then
log "INFO" "当前批次健康检查全部通过"
deployed_count=$(( deployed_count + BATCH_SIZE ))
# 记录已成功部署的版本(可选)
echo "${version}" > "${VERSION_FILE}"
# 检查是否还有剩余节点未部署
if [ ${deployed_count} -lt ${total_nodes} ]; then
log "INFO" "等待 ${BATCH_INTERVAL} 秒后进入下一批..."
sleep ${BATCH_INTERVAL}
fi
fi
done
log "INFO" "========== 所有节点部署成功!版本: ${version} =========="
}
# ---------- 入口 ----------
main "$@"
关键设计点解析
- 分批控制:使用数组切片
${NODES[@]:${deployed_count}:${BATCH_SIZE}},每次从剩余节点中取指定数量,实现“滚动前进”。 - 并行部署:用
&后台运行+wait实现单批次内节点并行部署,提高效率。 - 健康检查后置:部署动作不包含健康检查,单独一个函数,职责清晰。
- 分批间隔:
sleep ${BATCH_INTERVAL}给监控和流量切换留出时间。 - 回滚范围:脚本示例中是回滚当前批次+之前所有批次,实际可调整为“仅回滚当前批次,让旧版本继续服务”,取决于你的灰度策略(金丝雀发布 vs 蓝绿部署)。
- 版本记录:写入文件
${VERSION_FILE},便于后续回滚或审计。
更高级的场景与优化
a. 基于负载均衡器的平滑上线/下线
在部署前从负载均衡器摘除节点,部署成功后重新加入。
# 摘除节点
remove_from_lb() {
local node="$1"
# 调用 API 从 nginx/HAProxy/阿里云SLB 摘除
# consul services deregister / nginx_upstream_remove
}
# 加入节点
add_to_lb() {
local node="$1"
# 调用 API 将节点加入负载均衡器
}
然后在deploy_node前后调用。
b. 更健壮的异常处理
- 超时控制:
timeout命令限制部署/检查耗时。 - 幂等性:部署脚本应设计为可重复执行(多次执行结果一致)。
c. 如果使用K8s,分批由K8s原生控制
对于Kubernetes环境,使用kubectl rollout 自带的maxUnavailable和maxSurge参数即可实现分批,无需Shell脚本再重复造轮子。
使用建议
- 测试环境先跑:在非生产环境验证脚本的批次逻辑、回滚逻辑和健康检查阈值。
- 健康检查要灵敏且准确:健康检查接口应不仅检查进程存活,还应检查业务关键链路是否正常(例如数据库连接是否正常)。
- 日志和告警:脚本应输出清晰的日志,失败时能联动告警系统(如发送飞书/钉钉机器人消息)。
- 版本管理:建议将部署包(如
app-v1.2.3.tar.gz)事先分发到所有节点的临时目录,避免分批部署时跨区域传输导致延迟。
这个框架为你提供了一个可落地的分批发布Shell脚本模板,你可以根据实际的基础设施和部署流程修改deploy_node、health_check和rollback_node三个核心函数。