フランス国内の小規模チーム(開発5人、運用兼務)で、障害対応の一次切り分けに使うRunbookをNotionからローカルツールに移した。環境はUbuntu 22.04、Python 3.10、Slack Bolt。当時のRunbookは約40項目、1項目あたり平均15ステップ。障害発生時に誰が何をしたかが追えず、同じ確認を2人が並行してやる事故が月1回ほど起きていた。
まずRunbookをYAMLに落とし、各ステップにownerとexpectedを持たせた。実行ログはJSON Linesで1行1ステップ、時刻はUTCで保存し、表示だけEurope/Parisに変換する。
- id: check-db-conn
cmd: "pg_isready -h {{db_host}} -p 5432"
expected: "accepting connections"
timeout_sec: 5
owner: oncall
実行側はSlackのスラッシュコマンドから起動し、/runbook start incident-2024-xxでセッションIDを発行。各ステップの結果をスレッドに追記するだけにした。
import json, datetime, subprocess
def run_step(step, ctx):
cmd = step["cmd"]
for k, v in ctx.items():
cmd = cmd.replace("{{%s}}" % k, v)
try:
p = subprocess.run(cmd, shell=True, capture_output=True,
text=True, timeout=step.get("timeout_sec", 10))
ok = step["expected"] in (p.stdout + p.stderr)
rec = {"ts": datetime.datetime.utcnow().isoformat() + "Z",
"id": step["id"], "ok": ok, "out": (p.stdout + p.stderr)[:2000]}
except subprocess.TimeoutExpired:
rec = {"ts": datetime.datetime.utcnow().isoformat() + "Z",
"id": step["id"], "ok": False, "out": "timeout"}
with open("runbook.jsonl", "a") as f:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
return rec
仕組みとしては、Runbookを「読む文書」から「実行可能な手順」に変えただけで、判断は人がやる。自動修復は入れていない。理由は、この規模で自動化すると失敗時の影響範囲が読みにくく、当直が1人の時間帯に危険な修復が走るリスクの方が大きいと判断したから。代わりに、各ステップのexpectedを満たさない場合にSlackでメンションを飛ばすだけにした。
効かなかったのは、expectedの文字列が言語やバージョンで変わるコマンド。例えばsystemctl statusの出力はロケールで変わり、CIでは英語、手元ではフランス語の環境があった。結局、判定は終了コードと正規表現の併用に落ち着いた。また、タイムアウト5秒はVPN越しのDB確認では短すぎ、誤検知が週2回出たので15秒に延ばした。
この方式は、手順が定型でコマンドが安定している障害には向くが、初動の探索的な対応には向かない。記録が残る分、後から「なぜその手順を飛ばしたか」を説明するコストは増える。
同じようにRunbookをコード化しているチームは、expectedの判定を何で担保しているのか知りたい。終了コードだけか、出力の正規表現か、それとも人の目か。
0 条评论
还没有评论,写下第一条。