0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

監視25件を入れていたのに、7時間気づけなかった ── デッドマンスイッチのすすめ

0
Posted at

この記事は Zenn にも投稿しています。内容は同じです。

結論

「その瞬間、生きているか」を見る監視だけでは、定期ジョブの死を検知できません。

HTTPで叩いて200が返るか、ポートが開いているか、pingが返るか。これらは全部「今」しか見ていません。

そして、バックアップが取れていないことは、失われて初めて気づきます。 その「初めて」は最悪のタイミングで来ます。復元が必要になったときです。

対処は Push型のデッドマンスイッチ です。

# ジョブが成功したときだけ、ハートビートを送る
30 4 * * * root /opt/job.py >> /var/log/job.log 2>&1 \
  && curl -fsS https://uptime.example.com/api/push/aBcD1234EfGh

以下は、そこに至った経緯と、実装するときに踏んだ罠の記録です。


何が起きたか

自宅サーバーで、台帳(NetBox)とその自動化が 7時間停止していました。

私は監視を25件入れていました。それでも気づけませんでした。

原因は、Docker Compose に restart ポリシーが書かれていなかったことです。ホストを再起動したとき、コンテナが Exited (255) のまま復帰しませんでした。

厄介なのは、Dockerサービス自体は enabled かつ active だったことです。一見すると正常に見えます。

これ自体も罠なのですが、本題はここではありません。「もし鳴らないタイプの死に方だったら、永久に気づけなかった」 という点です。


25件すべてが「今」しか見ていなかった

自分の監視設定を見直して、気づきました。

  • HTTPで叩いて200が返るか
  • TCPポートが開いているか
  • pingが返るか

全部「今この瞬間、生きているか」です。

これで検知できないものがあります。

死に方 検知できるか
サービスが落ちた ✅ できる
ホストが落ちた ✅ できる
定期ジョブが失敗し続けている できない
バックアップが取れていない できない

定期ジョブは、動いていなくても何も落ちません。

Webサービスは元気にHTTP 200を返し続けます。監視は全部グリーンです。ただ、バックアップだけが取られていない。

そして次にバックアップが必要になったとき——ディスクが飛んだとき、操作を間違えたとき——そこで初めて気づきます。


デッドマンスイッチ

考え方を逆転させます。

Pull型(従来): 監視側が「生きてるか?」と聞きに行く
Push型        : 監視される側が「生きてます」と定期的に報告する
                → 一定時間報告が来なければ異常と判断する

デッドマンスイッチは、鉄道の運転士が握り続けていないと自動的にブレーキがかかる装置のことです。「何もしない」ことが異常の合図になります。

監視に置き換えると、ジョブが成功したときだけハートビートを送り、途絶えたら鳴らすという形になります。

実装

Uptime Kuma には Push型のモニターがあります。指定されたURLを叩くとハートビートとして記録され、一定時間来なければDOWN判定になります。

cronの末尾に足すだけです。

# /etc/cron.d/example
30 4 * * * root /opt/nb2kuma/sync.py >> /var/log/sync.log 2>&1 && /opt/push.sh nb2kuma-sync

ポイントは && です。

成功 → ハートビート送信 → 監視側は正常と判断
失敗 → ハートビート無し → 一定時間後にDOWN判定 → 通知

コマンドが失敗すれば、ハートビートは飛びません。「失敗を検知する」のではなく 成功が報告されないことを検知する という設計です。


⚠️ curl はHTTPエラーでも成功を返す

ここで踏みました。

# ❌ これだと失敗しても && が成立する
... && curl -sS https://uptime.example.com/api/push/aBcD1234EfGh

デフォルトの curl は、HTTPが500を返しても終了コード0(成功)を返します。

つまり、Uptime Kuma 側が落ちていても、ハートビートを送ったつもりになります。 さらに悪いことに、ジョブ本体が成功していれば && は成立するので、壊れていることに気づけません

# ✅ -f を付けるとHTTPエラーを失敗として扱う
... && curl -fsS https://uptime.example.com/api/push/aBcD1234EfGh

監視の仕組み自体が壊れていても気づけない、というのが最も危険な状態です。
デッドマンスイッチを入れるときは、その通知経路が本当に生きているかを一度は確かめてください。私は意図的にジョブを失敗させて、通知が来ることを確認しました。


許容時間は「周期の2倍強」にする

「何秒ハートビートが来なければ鳴らすか」の設定が重要です。

ジョブ 周期 許容時間
新規端末の検知 30分 4,200秒(1.2時間)
ログの書き出し 3時間 23,400秒(6.5時間)
DBダンプ 24時間 100,800秒(28時間)

1回コケた程度では鳴らさず、2回連続で落ちたら鳴るという設計です。

理由は単純で、ネットワークの一時的な不調で毎回鳴ると、通知そのものを無視するようになるからです。

鳴ったら必ず何かある、という状態を保つこと。

これが監視で一番大事だと思っています。オオカミ少年になった監視は、無いのと同じです。


「バックアップがある」と「使える」は違う

デッドマンスイッチを入れると、ジョブが走ったことは保証されます。しかし成果物が正しいことは保証されません。

実際に踏みました。DBダンプのスクリプトが、失敗して0バイトのファイルを作り、それが前回の正常なダンプを上書きしていました。

pg_dump -Fc ... > "$DUMP"     # 失敗しても空ファイルはできる
cp "$DUMP" "$NAS/"            # 空ファイルで上書き

バックアップは存在します。中身がないだけです。

対処

pg_dump -Fc ... > "$DUMP"

# サイズが小さすぎたら破棄して、ジョブごと失敗させる
if [ "$(stat -c%s "$DUMP")" -lt 10000 ]; then
    rm -f "$DUMP"
    exit 1        # ← ハートビートも送られない
fi

cp "$DUMP" "$NAS/"

失敗したときに、前の成果物を壊さない。 これがバックアップスクリプトの最低条件です。

そして exit 1 にすることで、デッドマンスイッチが検知してくれます。成果物の検証とハートビートを繋げるのがポイントです。


運用ルールにした

この仕組みを作ったあと、ルールを決めました。

cronを1つ足したら、必ず3点セットで用意する。

  1. ジョブ本体(失敗時に前の成果物を壊さない作り)
  2. Push型の監視&& push.sh <名前>
  3. logrotate(新しくログを吐くなら)

3つ目も実際に必要でした。放置すると、気づいたときにはディスクが埋まっています。しかもログでディスクフルになると、他のサービスまで巻き込みます。

ジョブを追加した日に設定するのが一番安いタイミングです。後から「そういえばログが」となる頃には、既に肥大化しています。


実際に何を監視対象にしたか

新規端末の検知           30分ごと
攻撃アラートの差分通知    30分ごと
ルーターログの書き出し    3時間ごと
台帳のDBダンプ           1日1回
台帳→監視の同期          1日1回
台帳の乖離検出           1日1回
syslogのNAS転送          1日1回

バックアップと攻撃検知を最優先で入れました。 どちらも「失われて初めて気づく」類だからです。

逆に、Webサービスの死活は従来のPull型のままです。落ちれば即座に分かるので、Push型にする必要がありません。

すべてをPush型にする必要はありません。
「落ちたら分かるもの」はPull型のまま、「黙って死ぬもの」だけPush型にする、という使い分けです。


監視ツールを2つ入れるなら、役割を分ける

余談ですが、関連するので書いておきます。

Uptime Kuma のあとに Prometheus + Grafana も入れました。リソースの傾向を見たかったからです。

ここで問題になるのが、両方ともアラート機能を持っていることです。

ツール 役割
Uptime Kuma 通知専任。すべてのアラートはここから出す
Grafana 可視化専任。アラートは一切持たせない

両方にアラートを持たせると、こうなります。

  • 同じ障害で通知が2回来る
  • どちらの設定を直せばいいか分からなくなる
  • 片方だけ設定して、もう片方を忘れる

通知の入口は1つにする。 規模が小さいうちに決めておかないと、後から統合するのが非常に面倒です。

Grafanaのアラート機能は強力ですが、強力だから使うという判断は危険です。「この機能が無いと困るか?」を先に問うと、私の場合は「Kumaで足りている」でした。


まとめ

問題 「その瞬間の生死」を見る監視では、定期ジョブの死を検知できない
対処 Push型のデッドマンスイッチ。成功時だけハートビートを送り、途絶えたら鳴らす
注意① curl-f を付ける(HTTPエラーで成功を返してしまう)
注意② 許容時間は周期の2倍強(オオカミ少年にしない)
注意③ 成果物の検証と exit 1 を繋げる(「ある」と「使える」は別)
注意④ 全部をPush型にしない。黙って死ぬものだけ

「監視を入れている」ことと「異常に気づける」ことは、別の話でした。


同じような失敗を57件ぶん書きました

この件も含めて、自宅ラボを組むなかで踏んだ地雷を57件、症状から逆引きできる形で本にまとめました。

📕 自宅ラボ 0→100 ── Proxmoxで作る、止まらない自宅インフラ

この記事の話は、本の付録では 型②「エラーにならないのに動いていない」 に分類しています。他にも「型①設定済みに見えるのに機能しない」「型③半分だけ動く」「型⑥検証方法そのものが間違っていた」といった分け方で整理しました。

第0章(設計の5原則)と更新履歴は無料で読めます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?