自动化部署脚本#
脚本说明#
自动化部署脚本用于自动化部署应用程序到多台服务器,支持代码部署、配置更新、服务重启等操作。
脚本代码#
#!/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 "$@"使用说明#
创建服务器列表文件:
cat > config/servers.txt <<EOF server1.example.com server2.example.com server3.example.com EOF添加执行权限:
chmod +x auto_deploy.sh基本用法:
# 正常部署 ./auto_deploy.sh # 试运行模式 ./auto_deploy.sh -n # 指定服务器列表 ./auto_deploy.sh -s servers.txt回滚部署:
# 回滚到上一个版本 ./auto_deploy.sh -r
功能特点#
- 多服务器并行部署
- 自动备份当前版本
- 代码同步
- 配置更新
- 依赖安装
- 数据库迁移
- 服务重启
- 健康检查
- 自动回滚
- 试运行模式
依赖项#
- ssh: 用于远程连接
- rsync: 用于文件同步
- tar: 用于打包备份
- curl: 用于健康检查
注意事项#
- 确保SSH密钥配置正确
- 确保部署用户有足够权限
- 建议先使用试运行模式(-n)测试
- 备份目录需要有足够空间
- 健康检查需要根据实际情况调整
- 回滚操作会恢复到上一个备份版本