自动化脚本怎样做到失败也说人话
最让人紧张的自动化,不是执行慢,而是输出一串绿色文字后留下一个不完整状态。某次部署脚本在复制配置后启动服务失败,却因为最后一条 echo 成功而返回 0。监控认为任务完成,直到用户打开页面才发现服务没有监听。此后我把“失败时能准确说明发生了什么”当作脚本的首要功能。
入口先验证事实
脚本开始时检查必需命令、运行身份、目标目录、剩余空间、配置语法和服务当前状态。每项检查输出对象和结果,不打印密钥。参数使用明确名称,危险动作要求目标在允许根目录内;路径先解析再比较,避免空变量或相对路径指向意外位置。
支持 --dry-run,但试运行不仅是打印命令。它实际读取配置、解析模板、检查连接和计算将改变的文件,只跳过最终写入与重启。这样试运行可以发现大多数前置错误。
每个阶段有输入与产物
部署分为获取、校验、备份、写入、验证、切换六个阶段。阶段开始记录输入摘要,结束记录产物路径与校验值。函数返回结构化结果,顶层统一映射退出码。
set -euo pipefail
trap 'echo "阶段=${STAGE} 行=${LINENO} 状态=$?" >&2' ERR
单靠 set -e 不够,管道、条件和子进程仍需显式处理。外部命令输出保存到日志,面向人的终端只显示摘要和下一步,既可诊断又不会被几千行淹没。
幂等与中断恢复
重复执行应该收敛到同一状态。写配置先生成临时文件,校验通过后原子替换;创建用户和目录前检查当前属性;数据库迁移记录版本,不用“错误就忽略”。阶段完成写入状态标记,中断后可以从安全边界继续,也可以明确要求回滚。
回滚不是反向再跑一遍所有命令。每次修改前保存确切旧值,切换失败时按逆序恢复。数据库等不可轻易逆转的步骤在开始前单独确认,并优先采用向前修复。
错误信息模板
错误必须包含四件事:正在做的动作、作用对象、底层原因、建议操作。例如:
无法切换 Nginx 配置:/etc/nginx/sites-enabled/blog
语法检查退出 1;旧配置仍在生效。
查看:/var/log/deploy/2026-05-02.log
这比“部署失败”多不了几行,却能让维护者立即知道服务是否仍安全。
发布检查
- [x] 任意阶段失败都返回非零状态
- [x] 重复运行不会创建重复资源
- [x] Ctrl+C 后临时文件被清理,现有服务保持可用
- [x] dry-run 不写入但完成真实校验
- [x] 日志含会话编号、阶段与耗时
- [x] 回滚在独立副本上实际演练
- [x] 成功输出列出变更、验证结果与下一步
自动化的价值不只是省下键盘操作,而是把一次正确流程固化。一个脚本如果只能在所有条件完美时工作,就只是更快地隐藏问题。让失败可见、状态可恢复、结果可验证,才是它值得被长期运行的原因。
评论区