自动化部署脚本#

脚本说明#

自动化部署脚本用于自动化部署应用程序到多台服务器,支持代码部署、配置更新、服务重启等操作。

脚本代码#

#!/bin/bash

# 自动化部署脚本
# 功能:自动化部署应用程序到多台服务器
# 作者:System Admin
# 日期:2026-01-01

set -euo pipefail

# 配置变量
LOG_FILE="/var/log/deploy.log"
DEPLOY_DIR="/var/www/myapp"
BACKUP_DIR="/backup/deploy"
SOURCE_DIR="./dist"
CONFIG_FILE="./config/deploy.conf"
SERVERS_FILE="./config/servers.txt"
DEPLOY_USER="deploy"
SSH_KEY="$HOME/.ssh/id_rsa"
ROLLBACK=false
DRY_RUN=false
VERBOSE=false

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log() {
    local level=$1
    shift
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $@" | tee -a "$LOG_FILE"
}

log_info() {
    log "INFO" "$@"
}

log_error() {
    log "ERROR" "$@"
}

log_warning() {
    log "WARNING" "$@"
}

# 检查依赖
check_dependencies() {
    log_info "检查依赖"
    
    local dependencies=("ssh" "rsync" "tar")
    
    for dep in "${dependencies[@]}"; do
        if ! command -v $dep &> /dev/null; then
            log_error "缺少依赖: $dep"
            return 1
        fi
    done
    
    log_info "依赖检查通过"
    return 0
}

# 检查配置文件
check_config() {
    log_info "检查配置文件"
    
    if [ ! -f "$SERVERS_FILE" ]; then
        log_error "服务器列表文件不存在: $SERVERS_FILE"
        return 1
    fi
    
    if [ ! -f "$CONFIG_FILE" ]; then
        log_error "配置文件不存在: $CONFIG_FILE"
        return 1
    fi
    
    log_info "配置文件检查通过"
    return 0
}

# 检查源代码
check_source() {
    log_info "检查源代码"
    
    if [ ! -d "$SOURCE_DIR" ]; then
        log_error "源代码目录不存在: $SOURCE_DIR"
        return 1
    fi
    
    if [ -z "$(ls -A $SOURCE_DIR)" ]; then
        log_error "源代码目录为空: $SOURCE_DIR"
        return 1
    fi
    
    log_info "源代码检查通过"
    return 0
}

# 备份当前版本
backup_current_version() {
    local server=$1
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_path="$BACKUP_DIR/$timestamp"
    
    log_info "备份当前版本: $server -> $backup_path"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将备份: $server:$DEPLOY_DIR -> $backup_path"
        return 0
    fi
    
    # 创建备份目录
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" "mkdir -p $backup_path"
    
    # 备份当前版本
    rsync -avz -e "ssh -i $SSH_KEY" \
        "$DEPLOY_USER@$server:$DEPLOY_DIR/" \
        "$backup_path/"
    
    log_info "备份完成: $backup_path"
}

# 部署代码
deploy_code() {
    local server=$1
    
    log_info "部署代码: $server"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将部署代码到: $server:$DEPLOY_DIR"
        return 0
    fi
    
    # 创建部署目录
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" "mkdir -p $DEPLOY_DIR"
    
    # 同步代码
    rsync -avz --delete -e "ssh -i $SSH_KEY" \
        "$SOURCE_DIR/" \
        "$DEPLOY_USER@$server:$DEPLOY_DIR/"
    
    log_info "代码部署完成: $server"
}

# 更新配置
update_config() {
    local server=$1
    
    log_info "更新配置: $server"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将更新配置: $server"
        return 0
    fi
    
    # 同步配置文件
    rsync -avz -e "ssh -i $SSH_KEY" \
        "$CONFIG_FILE" \
        "$DEPLOY_USER@$server:$DEPLOY_DIR/config/"
    
    log_info "配置更新完成: $server"
}

# 安装依赖
install_dependencies() {
    local server=$1
    
    log_info "安装依赖: $server"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将安装依赖: $server"
        return 0
    fi
    
    # 安装npm依赖
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "cd $DEPLOY_DIR && npm install --production"
    
    # 安装其他依赖
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "cd $DEPLOY_DIR && pip install -r requirements.txt"
    
    log_info "依赖安装完成: $server"
}

# 运行数据库迁移
run_migrations() {
    local server=$1
    
    log_info "运行数据库迁移: $server"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将运行数据库迁移: $server"
        return 0
    fi
    
    # 运行数据库迁移
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "cd $DEPLOY_DIR && python manage.py migrate"
    
    log_info "数据库迁移完成: $server"
}

# 重启服务
restart_services() {
    local server=$1
    
    log_info "重启服务: $server"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将重启服务: $server"
        return 0
    fi
    
    # 重启nginx
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "sudo systemctl restart nginx"
    
    # 重启应用服务
    ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "sudo systemctl restart myapp"
    
    log_info "服务重启完成: $server"
}

# 健康检查
health_check() {
    local server=$1
    
    log_info "健康检查: $server"
    
    # 检查服务状态
    local nginx_status=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "sudo systemctl is-active nginx")
    
    local app_status=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
        "sudo systemctl is-active myapp")
    
    log_info "Nginx状态: $nginx_status"
    log_info "应用状态: $app_status"
    
    if [ "$nginx_status" != "active" ]; then
        log_error "Nginx服务未运行"
        return 1
    fi
    
    if [ "$app_status" != "active" ]; then
        log_error "应用服务未运行"
        return 1
    fi
    
    # 检查HTTP响应
    local http_code=$(curl -s -o /dev/null -w "%{http_code}" \
        http://$server/health)
    
    log_info "HTTP状态码: $http_code"
    
    if [ "$http_code" != "200" ]; then
        log_error "健康检查失败: HTTP $http_code"
        return 1
    fi
    
    log_info "健康检查通过"
    return 0
}

# 回滚部署
rollback_deployment() {
    local server=$1
    local backup_path=$2
    
    log_info "回滚部署: $server -> $backup_path"
    
    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将回滚: $server -> $backup_path"
        return 0
    fi
    
    # 恢复备份
    rsync -avz --delete -e "ssh -i $SSH_KEY" \
        "$backup_path/" \
        "$DEPLOY_USER@$server:$DEPLOY_DIR/"
    
    # 重启服务
    restart_services "$server"
    
    log_info "回滚完成: $server"
}

# 部署到单台服务器
deploy_to_server() {
    local server=$1
    
    log_info "===== 开始部署到服务器: $server ====="
    
    # 备份当前版本
    backup_current_version "$server"
    
    # 部署代码
    deploy_code "$server"
    
    # 更新配置
    update_config "$server"
    
    # 安装依赖
    install_dependencies "$server"
    
    # 运行数据库迁移
    run_migrations "$server"
    
    # 重启服务
    restart_services "$server"
    
    # 健康检查
    if ! health_check "$server"; then
        log_error "健康检查失败,准备回滚"
        
        # 获取最新备份
        local latest_backup=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
            "ls -t $BACKUP_DIR | head -1")
        
        if [ -n "$latest_backup" ]; then
            rollback_deployment "$server" "$BACKUP_DIR/$latest_backup"
        fi
        
        return 1
    fi
    
    log_info "===== 部署完成: $server ====="
    return 0
}

# 并行部署
parallel_deploy() {
    local servers=($1)
    local pids=()
    local results=()
    
    log_info "开始并行部署"
    
    # 并行部署到所有服务器
    for server in "${servers[@]}"; do
        deploy_to_server "$server" &
        pids+=($!)
    done
    
    # 等待所有部署完成
    for pid in "${pids[@]}"; do
        wait $pid
        results+=($?)
    done
    
    # 检查结果
    local success=0
    local failed=0
    
    for result in "${results[@]}"; do
        if [ $result -eq 0 ]; then
            success=$((success + 1))
        else
            failed=$((failed + 1))
        fi
    done
    
    log_info "部署结果: 成功=$success, 失败=$failed"
    
    if [ $failed -gt 0 ]; then
        return 1
    fi
    
    return 0
}

# 生成部署报告
generate_report() {
    local date=$(date +%Y%m%d)
    local report_file="/tmp/deploy_report_$date.txt"
    
    {
        echo "部署报告"
        echo "========"
        echo "部署时间: $(date)"
        echo "源代码: $SOURCE_DIR"
        echo "部署目录: $DEPLOY_DIR"
        echo ""
        
        echo "部署的服务器:"
        cat "$SERVERS_FILE"
        echo ""
        
        echo "部署日志:"
        tail -100 "$LOG_FILE"
    } > "$report_file"
    
    log_info "部署报告已生成: $report_file"
}

# 显示帮助
show_help() {
    echo "用法: $0 [选项]"
    echo ""
    echo "选项:"
    echo "  -s <服务器文件>  服务器列表文件(默认: ./config/servers.txt)"
    echo "  -u <用户>        部署用户(默认: deploy)"
    echo "  -k <SSH密钥>    SSH密钥文件(默认: ~/.ssh/id_rsa)"
    echo "  -d <部署目录>    部署目录(默认: /var/www/myapp)"
    echo "  -r               回滚模式"
    echo "  -n               试运行模式"
    echo "  -v               详细输出"
    echo "  -h               显示帮助信息"
    echo ""
    echo "示例:"
    echo "  $0"
    echo "  $0 -s servers.txt -u deploy"
    echo "  $0 -r"
    echo "  $0 -n"
}

# 主函数
main() {
    # 解析选项
    while getopts "s:u:k:d:rnvh" opt; do
        case $opt in
            s)
                SERVERS_FILE="$OPTARG"
                log_info "服务器列表: $SERVERS_FILE"
                ;;
            u)
                DEPLOY_USER="$OPTARG"
                log_info "部署用户: $DEPLOY_USER"
                ;;
            k)
                SSH_KEY="$OPTARG"
                log_info "SSH密钥: $SSH_KEY"
                ;;
            d)
                DEPLOY_DIR="$OPTARG"
                log_info "部署目录: $DEPLOY_DIR"
                ;;
            r)
                ROLLBACK=true
                log_info "回滚模式"
                ;;
            n)
                DRY_RUN=true
                log_info "试运行模式"
                ;;
            v)
                VERBOSE=true
                log_info "详细输出"
                ;;
            h)
                show_help
                exit 0
                ;;
            *)
                log_error "无效选项: $opt"
                show_help
                exit 1
                ;;
        esac
    done
    
    log_info "===== 开始自动化部署 ====="
    
    # 检查依赖
    if ! check_dependencies; then
        exit 1
    fi
    
    # 检查配置
    if ! check_config; then
        exit 1
    fi
    
    # 检查源代码
    if ! check_source; then
        exit 1
    fi
    
    # 读取服务器列表
    local servers=($(cat "$SERVERS_FILE"))
    
    if [ ${#servers[@]} -eq 0 ]; then
        log_error "服务器列表为空"
        exit 1
    fi
    
    log_info "部署到 ${#servers[@]} 台服务器"
    
    # 执行部署
    if [ "$ROLLBACK" = true ]; then
        log_info "执行回滚操作"
        for server in "${servers[@]}"; do
            local latest_backup=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$server" \
                "ls -t $BACKUP_DIR | head -1")
            
            if [ -n "$latest_backup" ]; then
                rollback_deployment "$server" "$BACKUP_DIR/$latest_backup"
            fi
        done
    else
        # 并行部署
        parallel_deploy "${servers[*]}"
    fi
    
    # 生成报告
    generate_report
    
    log_info "===== 自动化部署完成 ====="
}

# 执行主函数
main "$@"

使用说明#

  1. 创建服务器列表文件:

    cat > config/servers.txt <<EOF
    server1.example.com
    server2.example.com
    server3.example.com
    EOF
  2. 添加执行权限:

    chmod +x auto_deploy.sh
  3. 基本用法:

    # 正常部署
    ./auto_deploy.sh
    
    # 试运行模式
    ./auto_deploy.sh -n
    
    # 指定服务器列表
    ./auto_deploy.sh -s servers.txt
  4. 回滚部署:

    # 回滚到上一个版本
    ./auto_deploy.sh -r

功能特点#

  • 多服务器并行部署
  • 自动备份当前版本
  • 代码同步
  • 配置更新
  • 依赖安装
  • 数据库迁移
  • 服务重启
  • 健康检查
  • 自动回滚
  • 试运行模式

依赖项#

  • ssh: 用于远程连接
  • rsync: 用于文件同步
  • tar: 用于打包备份
  • curl: 用于健康检查

注意事项#

  1. 确保SSH密钥配置正确
  2. 确保部署用户有足够权限
  3. 建议先使用试运行模式(-n)测试
  4. 备份目录需要有足够空间
  5. 健康检查需要根据实际情况调整
  6. 回滚操作会恢复到上一个备份版本