深色模式
自动化排错机器人:自愈脚本
摘要:自愈是把「明确、高频、无歧义」的故障处理自动化。本文划定适用边界,演示 systemd 自动重启、磁盘清理脚本,以及带限流与熔断的自愈框架。
适用环境
- Linux 服务器,具备 systemd
- 已有监控/告警系统可触发 webhook(或用 cron 轮询兜底)
- Python 3 可用(框架示例)
操作步骤
1. 先划清哪些故障能自愈
适合(规则明确、动作安全):服务进程意外退出 → 重启;磁盘被日志打满 → 清理过期日志;证书将过期 → 自动续期。不适合(需判断或不可逆):数据不一致、主从切换、删库类操作、原因未明的性能劣化。
2. systemd 自动重启(最简单一层)
bash
sudo mkdir -p /etc/systemd/system/myapp.service.d
sudo tee /etc/systemd/system/myapp.service.d/override.conf > /dev/null <<'EOF'
[Service]
Restart=always
RestartSec=5s
StartLimitIntervalSec=300
StartLimitBurst=5
EOF
sudo systemctl daemon-reload && sudo systemctl restart myapp1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
StartLimitBurst=5 是关键:300 秒内最多重启 5 次,超过就放弃,避免无限重启风暴。
3. 磁盘清理自愈脚本
bash
sudo cat > /opt/scripts/heal-disk.sh <<'EOF'
#!/usr/bin/env bash
set -uo pipefail
readonly THRESHOLD=85 LOG_DIR="/var/log/app" KEEP_DAYS=7 LOG="/var/log/heal.log"
log() { printf '%s [%s] %s\n' "$(date '+%F %T')" "$1" "$2" >> "$LOG"; }
used=$(df -P / | awk 'NR==2 {print $5}' | tr -d '%')
(( used < THRESHOLD )) && exit 0
log WARN "disk used ${used}%, start cleanup"
before=$(df -P / | awk 'NR==2 {print $4}')
find "${LOG_DIR}" -type f -name '*.log' -mtime +${KEEP_DAYS} -delete
journalctl --vacuum-time=${KEEP_DAYS}d >/dev/null 2>&1
after=$(df -P / | awk 'NR==2 {print $4}')
log INFO "freed $(( (after - before) / 1024 )) MB"
EOF
sudo chmod +x /opt/scripts/heal-disk.sh1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
4. 带限流与熔断的自愈框架
python
#!/usr/bin/env python3
"""同一目标冷却期内不重复动作,连续失败达到阈值则熔断"""
import json, os, subprocess, sys, time
STATE_FILE = "/var/lib/heal/state.json"
COOLDOWN, MAX_FAIL = 300, 3
def heal(target):
now = time.time()
st = json.load(open(STATE_FILE)) if os.path.exists(STATE_FILE) else {}
rec = st.get(target, {"last": 0, "fails": 0, "tripped": False})
if rec["tripped"]:
print(f"{target}: circuit tripped, manual reset required"); return False
if now - rec["last"] < COOLDOWN:
print(f"{target}: in cooldown, skip"); return False
rc = subprocess.run(["ssh", f"ops@{target}", "sudo systemctl restart myapp"]).returncode
rec["last"] = now
rec["fails"] = rec["fails"] + 1 if rc != 0 else 0
if rec["fails"] >= MAX_FAIL:
rec["tripped"] = True
print(f"{target}: CIRCUIT OPEN, escalate to human")
st[target] = rec
os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
json.dump(st, open(STATE_FILE, "w"))
return rc == 0
if __name__ == "__main__":
sys.exit(0 if heal(sys.argv[1]) else 1)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
5. 触发方式
bash
sudo tee /etc/cron.d/heal > /dev/null <<'EOF'
PATH=/usr/local/bin:/usr/bin:/bin
*/5 * * * * root /opt/scripts/heal-disk.sh
EOF
# 更快:在 Alertmanager 的 receiver 里配 webhook 指向自愈服务1
2
3
4
5
2
3
4
5
危险
自愈必须有熔断。没有熔断的自愈在根因未解决时会反复重启/清理,把小故障放大成持续抖动并掩盖真实原因。
验证
bash
sudo systemctl kill -s SIGKILL myapp && sleep 8 && systemctl is-active myapp
/opt/scripts/heal-disk.sh && tail -5 /var/log/heal.log1
2
2
- [ ] 杀掉进程后服务在 5 秒内自动恢复
- [ ] 连续失败 3 次后进入熔断并输出升级提示
- [ ] 冷却期内重复触发不会重复执行
常见坑
自愈掩盖了真实故障
服务频繁重启说明根因未解。要把自愈次数做成指标并告警,异常时转人工排查。
清理脚本删错文件
find / -name '*.log' -delete 是灾难。清理路径必须写死在业务目录下,且先用 -print 演练。
自愈操作不可逆
删除、重建、主从切换不可自动执行。只做重启、清理临时数据、续期这类可逆动作。