深色模式
幂等与可回滚:变更安全三原则
摘要:多数自动化事故不是因为命令写错,而是因为「再跑一次就出问题」和「失败后回不去」。本文给出变更安全三原则,并分别演示 Shell、Ansible、Terraform、K8s 中如何落地。
适用环境
- 已有任意自动化脚本 / playbook / 流水线
- 变更对象是生产或准生产环境
- 具备基本的版本控制(Git)
操作步骤
1. 原则一:幂等(跑 N 次结果一致)
执行一次与多次,系统最终状态相同且不产生额外副作用。反例是往文件 append、递增计数器。
bash
# 反例:跑三次就有三行
echo "10.0.0.1 db" >> /etc/hosts
# 正例:先判断
grep -q "10.0.0.1 db" /etc/hosts || echo "10.0.0.1 db" >> /etc/hosts1
2
3
4
2
3
4
Ansible 天然幂等,但 command/shell 例外,必须加判断:
yaml
- name: Initialize database only once
ansible.builtin.command: /opt/app/bin/initdb
args:
creates: /var/lib/app/.initialized # 文件存在则跳过1
2
3
4
2
3
4
2. 原则二:可预测(执行前知道会发生什么)
bash
ansible-playbook site.yml --check --diff
terraform plan -out=tfplan && terraform show tfplan
kubectl diff -f deployment.yaml
helm upgrade --install app ./chart --dry-run --debug1
2
3
4
2
3
4
没有 dry-run 能力的自制脚本,至少要提供 ./deploy.sh --dry-run 模式。
3. 原则三:可回滚(失败能退回原状)
bash
cp -a app.conf "app.conf.bak.$(date +%s)" # Shell:变更前备份
trap 'cp -a app.conf.bak.* app.conf' ERR
# Ansible:下发时自动备份 backup: true
# Terraform:git revert HEAD && terraform apply
kubectl rollout undo deployment/myapp # Kubernetes
helm rollback myapp 21
2
3
4
5
6
2
3
4
5
6
4. 把三原则写成检查清单
bash
cat > ~/automation/change-checklist.md <<'EOF'
## 变更自检
- [ ] 幂等:连续执行两次,第二次无 changed?
- [ ] 可预测:dry-run 输出与预期一致?
- [ ] 可回滚:回滚命令已写好并验证过?
- [ ] 影响面:是否先在 1 台/1 个副本灰度?
- [ ] 验证项:变更后用什么命令确认成功?
EOF1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
危险
「可回滚」必须是验证过的。备份文件存在不等于能恢复,定期演练恢复流程。
验证
bash
ansible-playbook -i inventory.ini site.yml # 第一次:changed > 0
ansible-playbook -i inventory.ini site.yml # 第二次:changed = 01
2
2
- [ ] 第二次执行
changed=0,且服务无重启 - [ ] dry-run 输出能准确反映将要发生的变更
- [ ] 回滚命令在测试环境实际执行成功过
常见坑
幂等但每次都重启服务
用 template + validate + notify 组合,只有真正 changed 才重启。
dry-run 不等于真跑
--check 模式下 command/shell 被跳过。dry-run 通过后仍需灰度验证。
回滚本身失败了
数据库 schema 回滚、不可逆删除无法通过「还原文件」解决。此类变更必须改为向前兼容的两阶段发布。