本文目录导读:

在Shell脚本中实现条件回滚策略,通常用于处理复杂的操作序列,确保在某个步骤失败时能够安全地恢复到初始状态,以下是几种常见的实现方法:
基本回滚模式
#!/bin/bash
set -e # 遇到错误立即退出
# 记录已执行的操作
declare -a executed_steps=()
declare -a rollback_commands=()
# 注册回滚命令
register_rollback() {
rollback_commands+=("$1")
}
# 执行回滚
rollback() {
echo "=== 开始回滚 ==="
for (( i=${#rollback_commands[@]}-1; i>=0; i-- )); do
echo "回滚步骤 $i: ${rollback_commands[$i]}"
eval "${rollback_commands[$i]}" || echo "回滚步骤 $i 失败: ${rollback_commands[$i]}"
done
echo "=== 回滚完成 ==="
}
# 主流程
main() {
# 步骤1: 创建文件
echo "步骤1: 创建临时文件"
touch /tmp/test_1.txt
register_rollback "rm -f /tmp/test_1.txt"
# 步骤2: 修改配置文件
echo "步骤2: 修改配置"
cp /etc/config /etc/config.backup
register_rollback "cp /etc/config.backup /etc/config"
# 步骤3: 执行可能失败的操作
echo "步骤3: 执行关键操作"
if ! some_command; then
echo "步骤3 失败,开始回滚"
rollback
exit 1
fi
echo "所有步骤成功完成"
}
# 捕获退出信号
trap rollback EXIT
main
使用函数实现回滚
#!/bin/bash
# 回滚堆栈
ROLLBACK_STACK=()
# 压入回滚函数
push_rollback() {
ROLLBACK_STACK+=("$1")
}
# 执行所有回滚
execute_rollback() {
local total=${#ROLLBACK_STACK[@]}
echo "执行 $total 个回滚操作"
for (( i=total-1; i>=0; i-- )); do
echo "执行回滚 #$i"
eval "${ROLLBACK_STACK[$i]}"
done
ROLLBACK_STACK=() # 清空堆栈
}
# 示例操作函数
create_file() {
local file=$1
echo "创建文件: $file"
touch "$file" || return 1
# 注册回滚
local rollback_cmd="rm -f $file"
push_rollback "$rollback_cmd"
}
copy_config() {
local src=$1 dst=$2
echo "复制配置: $src -> $dst"
cp "$src" "$dst" || return 1
# 注册回滚
local rollback_cmd="cp $dst $src"
push_rollback "$rollback_cmd"
}
start_service() {
local service=$1
echo "启动服务: $service"
systemctl start "$service" || return 1
# 注册回滚
local rollback_cmd="systemctl stop $service"
push_rollback "$rollback_cmd"
}
# 主函数
main() {
# 执行操作序列
create_file "/tmp/backup.tar.gz" || return 1
copy_config "/etc/nginx/nginx.conf" "/tmp/nginx.conf.backup" || return 1
start_service "nginx" || return 1
echo "所有操作成功完成"
return 0
}
# 执行并处理错误
if ! main; then
echo "操作失败,执行回滚"
execute_rollback
exit 1
fi
带状态检查的回滚
#!/bin/bash
# 状态管理
STATE_FILE="/tmp/deploy_state.txt"
ROLLBACK_FILE="/tmp/rollback_commands.txt"
init_state() {
> "$STATE_FILE"
> "$ROLLBACK_FILE"
}
record_step() {
local step=$1
echo "$step" >> "$STATE_FILE"
}
record_rollback() {
local command=$1
echo "$command" >> "$ROLLBACK_FILE"
}
execute_rollback() {
echo "=== 执行条件回滚 ==="
# 读取状态文件
local states=()
while IFS= read -r line; do
states+=("$line")
done < "$ROLLBACK_FILE"
# 逆序执行回滚
for (( i=${#states[@]}-1; i>=0; i-- )); do
local cmd="${states[$i]}"
echo "执行回滚命令: $cmd"
eval "$cmd" || {
echo "回滚命令失败: $cmd"
# 继续执行其他回滚
}
done
# 清理状态文件
rm -f "$STATE_FILE" "$ROLLBACK_FILE"
}
# 带条件的回滚
conditional_rollback() {
local condition=$1
local rollback_cmd=$2
if [ "$condition" = "true" ]; then
eval "$rollback_cmd"
echo "条件回滚执行: $rollback_cmd"
fi
}
# 示例使用
main() {
init_state
# 步骤1: 创建目录
echo "步骤1: 创建应用目录"
mkdir -p /var/www/new_app
record_step "create_dir"
record_rollback "rm -rf /var/www/new_app"
# 步骤2: 复制文件
echo "步骤2: 复制应用文件"
cp -r /tmp/app/* /var/www/new_app/
record_step "copy_files"
record_rollback "rm -rf /var/www/new_app"
# 步骤3: 设置权限
echo "步骤3: 设置文件权限"
chmod -R 755 /var/www/new_app
record_step "set_permissions"
# 检查磁盘空间
available_space=$(df /var/www/new_app | awk 'NR==2 {print $4}')
if [ "$available_space" -lt 1000000 ]; then
echo "磁盘空间不足 (可用: $available_space KB)"
execute_rollback
exit 1
fi
echo "所有步骤完成"
}
# 执行主函数
main
事务型回滚模式
#!/bin/bash
# 事务管理器
TRANSACTION_LOG="/tmp/transaction.log"
ROLLBACK_LOG="/tmp/rollback.log"
begin_transaction() {
echo "开始事务: $(date)" > "$TRANSACTION_LOG"
echo "" > "$ROLLBACK_LOG"
}
commit_transaction() {
echo "提交事务: $(date)" >> "$TRANSACTION_LOG"
rm -f "$TRANSACTION_LOG" "$ROLLBACK_LOG"
}
rollback_transaction() {
echo "回滚事务: $(date)" >> "$TRANSACTION_LOG"
if [ -f "$ROLLBACK_LOG" ]; then
echo "开始执行回滚..."
while IFS= read -r line; do
echo "回滚: $line"
eval "$line" || echo "回滚失败: $line"
done < "$ROLLBACK_LOG"
fi
# 清理
rm -f "$TRANSACTION_LOG" "$ROLLBACK_LOG"
}
add_operation() {
local description=$1
local action=$2
local rollback=$3
echo "执行: $description"
eval "$action" || {
echo "操作失败: $description"
rollback_transaction
return 1
}
# 记录回滚命令
echo "$rollback" >> "$ROLLBACK_LOG"
}
# 示例
main() {
begin_transaction
# 添加操作
add_operation "创建备份" \
"cp /etc/config /etc/config.bak" \
"cp /etc/config.bak /etc/config" || exit 1
add_operation "修改配置" \
"echo 'new_config' > /etc/config" \
"cp /etc/config.bak /etc/config" || exit 1
add_operation "重启服务" \
"systemctl restart nginx" \
"systemctl restart nginx_old" || exit 1
# 所有操作成功
commit_transaction
echo "所有操作成功完成"
}
main
实用的部署回滚脚本
#!/bin/bash
# 定义版本和回滚点
DEPLOY_DIR="/var/www/app"
BACKUP_DIR="/var/backups/app"
VERSION_FILE="$DEPLOY_DIR/version.txt"
# 创建备份点
create_checkpoint() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local checkpoint_dir="$BACKUP_DIR/checkpoint_$timestamp"
echo "创建检查点: $checkpoint_dir"
mkdir -p "$checkpoint_dir"
cp -r "$DEPLOY_DIR"/* "$checkpoint_dir/"
echo "$checkpoint_dir" > /tmp/current_checkpoint
echo "$timestamp" > "$VERSION_FILE"
}
# 回滚到检查点
rollback_to_checkpoint() {
local checkpoint=$(cat /tmp/current_checkpoint 2>/dev/null)
if [ -z "$checkpoint" ] || [ ! -d "$checkpoint" ]; then
echo "错误: 没有可用的检查点"
return 1
fi
echo "回滚到: $checkpoint"
# 停止服务
systemctl stop nginx 2>/dev/null
# 回滚文件
rm -rf "$DEPLOY_DIR"/*
cp -r "$checkpoint"/* "$DEPLOY_DIR/"
# 启动服务
systemctl start nginx 2>/dev/null
echo "回滚完成"
}
# 带条件的部署
deploy() {
local version=$1
echo "开始部署版本: $version"
# 条件检查
if [ ! -f "/tmp/app_$version.tar.gz" ]; then
echo "错误: 版本包不存在"
return 1
fi
# 创建备份点
create_checkpoint
# 解压新版本
tar -xzf "/tmp/app_$version.tar.gz" -C "$DEPLOY_DIR" || {
echo "解压失败,执行回滚"
rollback_to_checkpoint
return 1
}
# 检查配置文件
if [ ! -f "$DEPLOY_DIR/config/app.conf" ]; then
echo "配置文件缺失,回滚"
rollback_to_checkpoint
return 1
fi
# 重启服务
systemctl restart nginx || {
echo "服务重启失败,回滚"
rollback_to_checkpoint
return 1
}
echo "部署成功"
}
# 主菜单
case "${1:-help}" in
deploy)
deploy "$2"
;;
rollback)
rollback_to_checkpoint
;;
check)
echo "当前版本: $(cat $VERSION_FILE 2>/dev/null || echo 'unknown')"
;;
*)
echo "用法: $0 {deploy|rollback|check} [version]"
;;
esac
关键原则
- 原子性: 确保操作要么全部成功,要么全部回滚
- 幂等性: 回滚操作应该是可重复执行的
- 状态记录: 使用文件或变量记录已执行的操作
- 错误处理: 捕获所有可能的失败场景
- 资源清理: 确保在脚本退出时清理临时资源
选择哪种模式取决于你的具体需求:
- 简单操作:使用基本回滚模式
- 复杂操作:使用函数或事务模式
- 生产部署:使用检查点模式