Linux 自动化执行失败的兜底策略

1次阅读

脚本执行失败时应使用until循环配合sleep实现可控重试,限制3–5次并区分真失败(如sshcurl)与假失败(如grep),同时添加posix兼容的失败通知机制。

Linux 自动化执行失败的兜底策略

脚本执行失败时如何自动重试

linux自动化任务失败后直接退出,往往比失败本身更危险——它让问题被掩盖,直到某个关键节点崩掉。重试不是加个 while 循环就完事,得控制次数、间隔和退出条件。

常见错误现象:curl 请求超时后脚本立即退出,没重试;rsync 网络抖动失败,后续步骤全跳过;重试逻辑写在脚本开头,但实际失败发生在中间某条命令。

  • untilwhile 更直观:只要命令返回非 0 就继续,成功即停
  • 必须加 sleep,否则可能瞬间打爆目标服务(比如重试 systemctl start 会触发 systemd 的启动频率限制)
  • 重试上限建议设为 3–5 次,超过说明不是临时故障,而是配置或权限等根本问题
  • 示例:
    until timeout 10s curl -f http://api.example.com/health; do sleep 3; done

    ——这里 timeout 防卡死,-f 让 curl 在 HTTP 错误码时也返回非 0

怎么判断“真失败”而不是临时性报错

很多命令失败只是表象,比如 grep 找不到内容返回 1,但这是正常流程;而 ssh 连不上返回 255,才是真异常。不区分就兜底,反而把逻辑搞反。

使用场景:监控脚本里检查日志关键词、部署脚本中验证服务端口是否监听、CI 中等待容器就绪。

  • greptest -ffind 这类“查无结果即失败”的命令,不该进重试——它们的非 0 返回是设计行为
  • 真正要兜底的是网络类(curlwgetssh)、系统调用类(systemctl startdocker run)和权限类(chown 失败但目录存在)
  • 可以用 $? 捕获上一条命令退出码,再结合具体数值做分支:比如 ssh 失败通常是 255,而 cp 权限不足是 1

crontab 任务挂了没人知道?加个失败通知

定时任务静默失败是最常见的兜底盲区。cron 默认只在 stderr 有输出时发邮件,而很多脚本把错误日志重定向到文件或丢弃了。

性能影响几乎为零,但能让你在凌晨三点前就知道数据库备份没跑成。

  • 别依赖 cron 的邮件功能——多数生产环境没配本地 MTA,邮件根本发不出
  • 用最简方式触发通知:失败时调用 curl 推送企业微信/钉钉 Webhook,或写入一个 /tmp/failed-jobs.log 并由另一个轻量脚本轮询
  • 关键点:通知逻辑必须放在整个任务链的末尾,且只在最终失败时触发。例如:
    ./deploy.sh || (echo "$(date) deploy failed" >> /var/log/deploy-fail.log; curl -X POST https://xxx/webhook)
  • 避免在通知里拼接大量日志,tail -n 20 deploy.log 足够定位,太大可能触发 webhook 限流

兜底策略本身不能成为单点故障

你给主流程加了重试、通知、回滚,但如果兜底代码自己出问题(比如依赖的 Python 脚本缺失、Webhook 地址写错),整个防御体系就形同虚设。

最容易被忽略的地方是:兜底逻辑的执行环境和主流程不一致。cron 用的是最小 shell(sh),但你的重试脚本用了 [[source,一跑就跪。

  • 兜底代码优先用 POSIX 兼容语法:if [ "$?" != "0" ] 而不是 [[ $? != 0 ]];用 /bin/sh 而不是 #!/usr/bin/env bash
  • 所有外部命令(curljqlogger)必须显式写绝对路径,或在脚本开头检查是否存在:
    command -v curl >/dev/null || { echo "curl missing"; exit 1; }
  • 不要在兜底逻辑里做复杂状态恢复(比如自动 rollback 数据库 migration),这类操作应由人工确认后手动触发——自动化兜底只负责“别让它彻底失联”,不负责“替你擦屁股”

text=ZqhQzanResources