Shell脚本如何实现分批发布策略

wen 实用脚本 22

本文目录导读:

Shell脚本如何实现分批发布策略

  1. 核心概念与流程
  2. Shell脚本实现(通用框架)
  3. 关键设计点解析
  4. 更高级的场景与优化
  5. 使用建议

在Shell脚本中实现分批发布策略,核心思想是控制每次更新的节点数量设置观察窗口以及处理回滚逻辑

下面是一个通用的、可维护的分批发布Shell脚本框架,包含核心功能和关键设计。

核心概念与流程

  1. 全量节点列表:定义所有需要部署的服务器IP或主机名。
  2. 批次大小:每批同时操作的节点数量(2台)。
  3. 批次间隔:每批部署完成后,等待观察的时间(60秒)。
  4. 健康检查:在每批部署后,自动检查应用是否正常。
  5. 回滚机制:如果某批次部署后健康检查失败,自动或手动触发回滚。

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 自带的maxUnavailablemaxSurge参数即可实现分批,无需Shell脚本再重复造轮子。

使用建议

  • 测试环境先跑:在非生产环境验证脚本的批次逻辑、回滚逻辑和健康检查阈值。
  • 健康检查要灵敏且准确:健康检查接口应不仅检查进程存活,还应检查业务关键链路是否正常(例如数据库连接是否正常)。
  • 日志和告警:脚本应输出清晰的日志,失败时能联动告警系统(如发送飞书/钉钉机器人消息)。
  • 版本管理:建议将部署包(如app-v1.2.3.tar.gz)事先分发到所有节点的临时目录,避免分批部署时跨区域传输导致延迟。

这个框架为你提供了一个可落地的分批发布Shell脚本模板,你可以根据实际的基础设施和部署流程修改deploy_nodehealth_checkrollback_node三个核心函数。

抱歉,评论功能暂时关闭!