运维里做自愈,最怕的是把恢复任务写成“发现不对就重启”,结果把一个偶发抖动搞成了事故。我处理这类问题时,第一件事不是写playbook,而是先确认监控侧到底拿到了什么信号:是连接超时、返回码异常,还是主机直接无响应。信号不同,恢复动作完全不同。
先确认现象:失败是单点还是持续性的
要准确判断一台主机是否需要执行恢复任务,不能只依赖单次检查。通常的实践是,监控端先对服务地址做若干次探测,并设置一个可容忍的失败率阈值。只有当连续失败次数超过阈值,且当前时间不在维护窗口内,才认定该主机处于异常状态。为避免网络抖动引发误触发,建议在触发前保存一次日志快照,方便后续核对。同时为每次判定写入临时标记文件,记录触发时间、失败原因和待执行的恢复任务名,这样可以在真正恢复前做人工review。
上面的判定流程看起来很繁琐,但它把“自动恢复”变成了“受控恢复”。我见过不止一次因为监控探针配置了短超时,业务高峰期出现一批误报,然后自愈脚本把所有节点轮流重启的案例。有了阈值和标记文件,至少能在事后追踪是哪次探测、哪个阈值导致动作发生。
容易误判的地方:环境不通和主机名不匹配
自愈playbook跑不通,很多时候不是恢复逻辑写错,而是ansible控制端和目标主机之间的连通性没有确认。比如监控机发出的SSH密钥没有被目标主机信任,或者控制端Python解释器版本不一致,都会让任务卡在第一步。我建议把恢复playbook固定运行在专用的跳板机或控制节点上,先用ansible -m ping验证连通性再谈其他。
另一个经常被忽略的是inventory主机名和监控上报的主机名不一致。监控侧可能上报的是带域名的主机名,而inventory里只写了短名称,于是--limit匹配不上。这种情况可以用add_host动态添加目标,或者执行时直接用IP指定。总而言之,先定位是哪一层断了,别一上来就怀疑恢复任务本身。
建议的处理顺序:诊断和修复分开
恢复动作建议拆成两个阶段。第一阶段先执行只读诊断任务,比如检查进程状态、磁盘剩余空间、最近系统日志,用register保存结果并生成一个简要报告;第二阶段根据报告决定是否执行重启服务、清理临时文件或重新拉取镜像。注意不要把诊断和修复放在同一个playbook里,否则一旦修复动作本身有语法错误,诊断数据也会丢失。在实际操作中,可以写一个wrapper脚本,用ansible-playbook执行恢复playbook,并在执行前复制当前运行目录的副本,确保有后悔药。
这个顺序最重要的价值是保留现场。如果第二阶段执行失败,至少第一阶段收集到的进程列表、磁盘占用、日志摘要还在,人工接手时可以快速判断系统当时处于什么状态。如果诊断和修复写在一起,一旦中途失败,输出混杂在任务流里,很难提取有用的上下文。
配置与命令示例:一个可落地的wrapper
下面是一个简化思路的示例,实际路径、主机列表和阈值要根据环境调整。它先读取标记文件判断冷却时间,然后调用恢复playbook,最后用退出码告诉监控端结果。
#!/usr/bin/env bash
HOST=$1
PLAYBOOK=/etc/ansible/selfheal.yml
LOCK=/tmp/selfheal_${HOST}.lock
if [ -f "$LOCK" ]; then
echo "冷却期内,跳过 ${HOST}"
exit 2
fi
# 记录本次尝试时间,作为冷却标记
touch "$LOCK"
ansible-playbook -i /etc/ansible/hosts -l "$HOST" "$PLAYBOOK" > /var/log/selfheal/${HOST}.log 2>&1
CODE=$?
# 结束后清理锁,让后续冷却逻辑由外层控制
rm -f "$LOCK"
exit $CODE写法上注意,冷却时间单独用一个外部锁文件或last_run文件控制,不要让playbook自己同时管重试和锁。外层监控系统可以读取退出码:0表示恢复成功,非0表示失败,然后决定是否升级告警。示例里的锁文件只防御了并发,真正的冷却时间判断还要结合mtime,可以视环境用find或stat实现。
验证方法:端口监听和健康检查接口都要看
恢复任务执行成功并不代表服务真的可用。建议在playbook末尾使用wait_for模块等待服务端口恢复监听,超时时间根据服务启动耗时来设置,不要使用固定值。同时用uri模块请求一个健康检查接口,判断返回码是否为200。如果服务是集群模式,还要检查调度器是否重新分配节点。所有检查结果应输出到统一日志文件,并以退出码表示最终状态:0为恢复成功,非0为失败。
这里特别提醒,wait_for只解决端口监听这一层,不解决应用逻辑层的问题。有些服务端口起来了,但内部依赖数据库连接没准备好,接口仍然返回500。所以健康检查接口要能覆盖到核心依赖,不然自愈会误判成功。不要只看端口,一定要看业务返回码。
回滚和风险:防重试、防并发、留后门
自愈机制最怕的就是反复触发、反复失败,导致资源耗尽。因此每次执行前需要检查冷却时间,例如在目标主机上维护一个last_run文件,若距上次运行不足3分钟,就直接跳过。同时要限制总重试次数,比如一个小时内最多尝试两次,超过后转为通知人工。还要注意防止并发:监控系统可能有多个节点同时探测,需要利用一个全局锁文件或consul等分布式锁,确保同一主机同一时间只有一个恢复任务在运行。若没有锁,可能出现两个进程同时重启服务,造成状态不一致。
除了冷却和锁,还要给自己留一条手动的路。比如恢复playbook里所有命令都加--check支持,或者单独保留一份只做诊断的精简playbook。一旦发现自愈动作本身有漏洞,可以快速切到人工模式。不要等出了故障才临时去写回滚脚本。
后续维护:日志和误触发要定期复盘
自愈上线后不是就结束了。我会定期翻一下日志目录,看看有多少次是真实故障触发,多少次是阈值设置太敏感。如果连续几周都是误触发,就需要调整探测次数和失败率阈值。同时,恢复playbook里的诊断项要随业务变化而更新,比如进程名改了、日志路径挪了,都要同步过来。
另外,恢复任务执行器的环境本身的密钥轮转和依赖更新也要纳入管理。一旦跳板机上的ansible版本升级或SSH配置变化,需要先跑一次连通性测试。否则自愈机制可能在一个平常的维护日之后静默失效。