Shell脚本如何实现并行发布策略

wen 实用脚本 19

本文目录导读:

Shell脚本如何实现并行发布策略

  1. 使用 & 后台运行 + wait 等待
  2. 控制并发数量(信号量机制)
  3. 使用 GNU Parallel 工具
  4. 带错误处理和回滚策略
  5. 使用 xargs 实现并行
  6. 最佳实践建议

在Shell脚本中实现并行发布策略,主要有以下几种常用方法:

使用 & 后台运行 + wait 等待

最简单的并行执行方式:

#!/bin/bash
# 并行发布函数
deploy_service() {
    local service=$1
    local version=$2
    echo "[$(date +%H:%M:%S)] 开始发布 $service (版本: $version)"
    sleep $((RANDOM % 5 + 2))  # 模拟发布过程
    echo "[$(date +%H:%M:%S)] $service 发布完成"
    return $((RANDOM % 2))
}
# 定义要发布的服务列表
services=("service-a" "service-b" "service-c" "service-d")
version="v2.1.0"
# 并行发布所有服务
echo "开始并行发布..."
for svc in "${services[@]}"; do
    deploy_service "$svc" "$version" &
    pids+=($!)  # 保存后台进程PID
done
# 等待所有发布完成
failed=0
for i in "${!pids[@]}"; do
    wait "${pids[$i]}"
    if [ $? -ne 0 ]; then
        echo "${services[$i]} 发布失败"
        ((failed++))
    fi
done
# 输出结果汇总
echo "===== 发布结果 ====="
echo "总共: ${#services[@]} 个服务"
echo "成功: $(( ${#services[@]} - failed )) 个"
echo "失败: $failed 个"
[ $failed -eq 0 ] && exit 0 || exit 1

控制并发数量(信号量机制)

当服务很多时,需要限制并行数量:

#!/bin/bash
# 使用文件描述符控制并发数
concurrent_deploy() {
    local max_jobs=$1      # 最大并行数
    local services=("${!2}") # 服务列表
    local version=$3
    # 创建命名管道用于控制并发
    tmpdir=$(mktemp -d)
    mkfifo "$tmpdir/fifo"
    exec 3<>"$tmpdir/fifo"
    # 初始化信号量
    for ((i=0; i<max_jobs; i++)); do
        echo >&3
    done
    # 并行发布
    for svc in "${services[@]}"; do
        read -u 3  # 获取信号量
        {
            echo "[$(date +%H:%M:%S)] 开始发布 $svc"
            sleep $((RANDOM % 3 + 1))
            echo "[$(date +%H:%M:%S)] $svc 完成"
            echo >&3  # 释放信号量
        } &
    done
    # 等待所有完成
    wait
    exec 3>&-
    rm -rf "$tmpdir"
}
# 使用示例
services=("svc-1" "svc-2" "svc-3" "svc-4" "svc-5")
concurrent_deploy 3 services[@] "v2.0.0"

使用 GNU Parallel 工具

更专业的并行处理工具:

#!/bin/bash
# 安装: apt-get install parallel 或 brew install parallel
deploy_one_service() {
    local service=$1
    local version=$2
    echo "开始发布 $service"
    # 模拟发布步骤
    echo "  1. 拉取镜像: $service:$version"
    sleep 1
    echo "  2. 停止旧版本"
    sleep 1
    echo "  3. 启动新版本"
    sleep 1
    echo "  4. 健康检查"
    sleep 1
    echo "$service 发布完成"
}
export -f deploy_one_service  # 导出函数供parallel使用
# 定义服务列表
SERVICES="service-a service-b service-c service-d"
VERSION="v3.0.0"
# 并行执行,控制并发数
echo "使用GNU Parallel并行发布..."
echo "$SERVICES" | tr ' ' '\n' | parallel -j 4 deploy_one_service {} $VERSION
# 带进度显示
echo "$SERVICES" | tr ' ' '\n' | parallel --progress -j 3 deploy_one_service {} $VERSION

带错误处理和回滚策略

更完整的生产环境发布脚本:

#!/bin/bash
# 配置
MAX_PARALLEL=3
VERSION=${1:-"latest"}
DEPLOY_LOG="/var/log/deploy_$(date +%Y%m%d_%H%M%S).log"
# 服务配置
declare -A SERVICES
SERVICES=(
    ["api-gateway"]="8080"
    ["user-service"]="8081"
    ["order-service"]="8082"
    ["payment-service"]="8083"
    ["notification-service"]="8084"
)
# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$DEPLOY_LOG"
}
# 健康检查
health_check() {
    local service=$1
    local port=$2
    local retries=10
    for ((i=1; i<=retries; i++)); do
        if curl -sf "http://localhost:$port/health" > /dev/null 2>&1; then
            return 0
        fi
        sleep 2
    done
    return 1
}
# 发布单个服务
deploy_service() {
    local service=$1
    local port=$2
    local version=$3
    local result_file=$4
    log "[$service] 开始部署..."
    # 模拟部署步骤
    {
        log "[$service] 拉取镜像: $service:$version"
        sleep 2
        # 模拟失败(10%概率)
        if [ $((RANDOM % 10)) -eq 0 ]; then
            log "[$service] ❌ 部署失败"
            echo "$service:FAILED" >> "$result_file"
            return 1
        }
        log "[$service] 启动新版本..."
        sleep 3
        # 健康检查
        if health_check "$service" "$port"; then
            log "[$service] ✅ 部署成功"
            echo "$service:SUCCESS" >> "$result_file"
            return 0
        else
            log "[$service] ❌ 健康检查失败,触发回滚"
            # 执行回滚操作
            echo "$service:ROLLBACK" >> "$result_file"
            return 1
        fi
    }
}
# 主函数
main() {
    local result_file=$(mktemp)
    local pids=()
    log "开始并行发布,版本: $VERSION"
    log "最大并行数: $MAX_PARALLEL"
    # 使用队列控制并发数
    local queue=()
    local running=0
    for service in "${!SERVICES[@]}"; do
        port=${SERVICES[$service]}
        # 等待,直到当前并行数小于最大限制
        while [ $running -ge $MAX_PARALLEL ]; do
            for pid in "${pids[@]}"; do
                if ! kill -0 $pid 2>/dev/null; then
                    wait $pid
                    ((running--))
                fi
            done
            sleep 1
        done
        # 启动新的部署进程
        deploy_service "$service" "$port" "$VERSION" "$result_file" &
        pids+=($!)
        ((running++))
    done
    # 等待所有进程完成
    wait
    # 汇总结果
    log "===== 发布汇总 ====="
    local success=0
    local failed=0
    local rollback=0
    while IFS= read -r line; do
        service=${line%:*}
        status=${line#*:}
        case $status in
            SUCCESS)  ((success++)) ;;
            FAILED)   ((failed++)) ;;
            ROLLBACK) ((rollback++)) ;;
        esac
        log "  $service: $status"
    done < "$result_file"
    rm -f "$result_file"
    log "统计: 成功=$success, 失败=$failed, 回滚=$rollback"
    if [ $failed -eq 0 ] && [ $rollback -eq 0 ]; then
        log "✅ 所有服务发布成功"
        return 0
    else
        log "⚠️  部分服务发布异常"
        return 1
    fi
}
# 执行
main "$@"

使用 xargs 实现并行

简单快速的并行方案:

#!/bin/bash
# 并行发布函数
deploy() {
    local service=$1
    echo "发布 $service..."
    sleep $((RANDOM % 3 + 1))
    echo "$service 完成"
}
export -f deploy
# 服务列表
cat << EOF > services.txt
service-a
service-b
service-c
service-d
service-e
EOF
# 并行执行,-P 控制并行数
cat services.txt | xargs -I {} -P 3 bash -c 'deploy "$@"' _ {}

最佳实践建议

  1. 错误处理:每个并行任务都要捕获错误并进行处理
  2. 日志记录:集中收集所有并行任务的日志
  3. 资源限制:根据服务器资源合理设置并发数
  4. 超时控制:为每个任务设置超时时间
  5. 回滚机制:单个任务失败时,最好有自动回滚能力
# 超时控制示例
deploy_with_timeout() {
    local timeout=$1
    shift
    timeout $timeout "$@" &
    pid=$!
    wait $pid 2>/dev/null
    return $?
}
# 使用
deploy_with_timeout 300 deploy_service "svc-a" "v1.0.0"

选择哪种方式取决于你的具体需求:

  • 简单场景:使用 & + wait
  • 需要控制并发:使用管道信号量或 GNU Parallel
  • 生产环境:使用完整的错误处理和回滚方案

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