实用脚本能批量回滚吗?

wen 实用脚本 9

本文目录导读:

实用脚本能批量回滚吗?

  1. 核心原则:脚本必须支持“可逆操作”
  2. 场景一:数据库脚本(最常见)
  3. 场景二:文件/配置修改脚本
  4. 场景三:自动化运维脚本(Ansible, SaltStack)
  5. 批量回滚的常见陷阱与注意事项
  6. 一个简单的“双脚本”模式(适合新手)

对于实用脚本的批量回滚,答案是肯定的,但这完全取决于脚本本身的设计是否支持幂等性、版本控制和异常处理。

“能否回滚”不在于“脚本”这个载体,而在于“脚本里写了什么”

下面我将从几个常见的场景出发,说明如何实现以及需要注意的风险。

核心原则:脚本必须支持“可逆操作”

一个可回滚的脚本,通常需要包含两部分逻辑:

  1. 正向逻辑(部署/修改):执行主要任务。
  2. 逆向逻辑(回滚/恢复):撤销正向操作,恢复到执行前的状态。

数据库脚本(最常见)

这是最需要批量回滚能力的场景,如果一个 SQL 脚本批量修改了大量数据,通常依靠事务备份表

如何实现:

  1. 开启事务:在脚本开头使用 BEGIN transaction;,如果中间任何一步失败,执行 ROLLBACK; 即可。
  2. 备份旧数据:在修改前,将受影响的数据插入到一张备份表中(table_backup_20231027)。
  3. 提供回滚文件:提供一个独立的 rollback.sql 文件,内容就是反向操作。
    • 正向:UPDATE users SET status = 1 WHERE id IN (1,2,3);
    • 回滚:UPDATE users SET status = 0 WHERE id IN (1,2,3);
      • 注意:这种回滚要求你在执行正向脚本时,必须记录下修改了哪些ID。

批量回滚方式:

# 假设你有一个回滚脚本目录
mysql -h host -u user -p database < ./rollback/rollback_20231027.sql

或者,将回滚命令集成到同一个脚本中,通过参数控制:

# 假设脚本支持 --rollback 参数
./deploy_database.sh --rollback

文件/配置修改脚本

对于修改配置文件、替换二进制文件等操作,回滚通常依赖版本控制(Git)文件快照

如何实现:

  1. 使用 Git 管理:脚本在修改前,先 git add .git commit 一个快照,回滚时直接 git reset --hard <commit_hash>
  2. 备份原文件:脚本在复制新文件之前,先将原文件重命名或移动到备份目录。
    • 正向:cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%Y%m%d) 然后替换为新配置。
    • 回滚:cp /etc/nginx/nginx.conf.bak.20231027 /etc/nginx/nginx.conf

批量回滚方式:

# 遍历所有备份文件,恢复原位置
for backup_file in $(find /etc/nginx/ -name "*.bak.20231027"); do
    original_file=$(echo $backup_file | sed 's/.bak.[0-9]*//')
    cp "$backup_file" "$original_file"
done
echo "批量回滚完成,请重启服务"

自动化运维脚本(Ansible, SaltStack)

如果你使用的不是裸 shell 脚本,而是像 AnsibleSaltStack 这样的工具,它们天生就支持幂等性和回滚。

如何实现:

  • 幂等性:如果某个 task 执行失败,Ansible 会直接报错,不会继续执行,你可以配置 ignore_errors: no,并在遇到错误时触发 meta: end_play
  • 快照回滚:最彻底的方法,在云环境中,脚本在执行前先调用 API 对目标虚拟机或数据库创建快照
    • 正向:部署新代码。
    • 回滚:aws ec2 create-snapshot --volume-id vol-xxxx -> 如果出错 -> aws ec2 restore-snapshot --volume-id vol-xxxx --snapshot-id snap-xxx

批量回滚方式(利用工具自身):

# Ansible 剧本如果执行失败,可以用之前的 tag 重新部署旧版本
ansible-playbook -i inventory deploy.yml --tags "rollback_v1.2"

批量回滚的常见陷阱与注意事项

  1. 非原子性:脚本是对多个目标(100台机器、100万行数据)执行的,如果第50台机器失败了,前49台已经成功,无法自动撤销,你需要:

    • 方案A:从第50台开始停止,手动处理前49台(不如方案B好)。
    • 方案B:使用蓝绿部署金丝雀发布,先改一小部分,验证通过后全面部署,回滚时整体切换流量即可,无需逐台操作。
  2. 依赖关系:回滚脚本可能会引入新的依赖问题,正向脚本修改了 A 表和 B 表,回滚脚本需要同时恢复 A 和 B,但可能 B 表的数据已经被其他进程修改,导致回滚失败。

  3. 日志记录至关重要:脚本在修改任何资源前,必须输出日志,记录修改的对象、原始值和目标值,批量回滚时,这些日志是你知道“该取消什么”的唯一依据。

  4. 小心“硬删除”:如果正向脚本执行了 rm -rfDROP TABLE,而你没有预先备份,回滚将变得非常困难(甚至不可能)。建议: 脚本中永远不要直接删除,而是先移动到回收站(mv /app/code /app/code_old_20231027),直到确认新版本稳定后再清理。

一个简单的“双脚本”模式(适合新手)

如果你只是写一个简单的 shell 脚本做批量操作,可以按如下结构设计:

# deploy.sh (部署脚本)
#!/bin/bash
ACTION=${1:-deploy}  # deploy 或 rollback
if [ "$ACTION" == "deploy" ]; then
    echo "执行部署..."
    # 1. 备份
    cp -r /app/config /app/config.backup.$(date +%Y%m%d)
    # 2. 修改
    echo "new config" > /app/config/app.conf
    echo "备份完成,可回滚时使用:$0 rollback $(date +%Y%m%d)"
elif [ "$ACTION" == "rollback" ]; then
    ROLLBACK_DATE=$2  # 20231027
    if [ -z "$ROLLBACK_DATE" ]; then
        echo "请指定回滚日期,$0 rollback 20231027"
        exit 1
    fi
    echo "执行回滚到日期 $ROLLBACK_DATE ..."
    cp -r /app/config.backup.$ROLLBACK_DATE /app/config
    echo "回滚完成,请重启服务"
fi

批量回滚命令:

# 对100台服务器批量执行
for ip in $(cat server_list.txt); do
    ssh root@$ip "/path/to/deploy.sh rollback 20231027"
done
脚本类型 能否批量回滚 推荐实现方式
shell脚本 可以,但需谨慎 备份原文件 + 编写对应的回滚脚本
SQL脚本 可以,必须用事务 BEGIN + ROLLBACK(单次)或 rollback.sql(批量)
Ansible/Playbook 天生支持 利用幂等性、meta: end_playtags 回滚
Kubernetes (kubectl) 天生支持 kubectl rollout undo deployment/app
云API脚本 可以,但开销大 执行前创建快照,失败后用快照恢复

核心建议:

  • 如果可能,尽量使用成熟的工具(Ansible, K8s, 数据库迁移工具)来执行批量操作,它们内置了比裸脚本更好的回滚机制。
  • 如果必须写裸脚本,务必在脚本开头增加 --dry-run 参数(试运行),让它只打印“我会做什么”,而不实际执行,先在测试环境跑通回滚流程,再上生产。
  • 永远不要在没有任何备份或快照的情况下,对一个重要系统执行不可逆的批量脚本操作。

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