アラートが途切れる三つの境界を修復する
目標
実際のk3s・Prometheus Operatorで、選択されていないルール、発火だけしたアラート、通知しないAlertmanagerを、順に直します。 現在のPodの障害・復旧のWebhookを実際に受信したあと、証拠の不足と矛盾を区別する診断関数を実装します。
なぜ重要なのか
Kubernetes APIがルールを保存してくれたからといって、Prometheusがそのルールを読んだという意味ではありません。 発火したからといって、人に届いたわけでもありません。公式の構成の各境界を自分で直し、APIで 参照してみて初めて、誤った設定、まだ反映中の状態、実際の配信の失敗を区別できます。
個人VMの中に、Operator v0.94.0・Prometheus v3.14.0・Alertmanager v0.34.0と、払い出しアプリが 用意されます。実行プロセスがあるk3sであり、KWOKではありません。インストールには数分かかることがあります。 作業は55分を想定しています。もっと必要なら、有効期限が切れる前に時間を延長し、終了前に必要な記録は ダウンロードしてください。VMとファイルは、セッション終了時に回収されます。VMの外の本番クラスターには適用しません。
用意されているもの
- ネームスペースplatform-monitoring、team-lab。ルールprovision-ready、Prometheus/Alertmanager platform
- ServiceMonitor workbench: Serviceラベルapp=workbenchと、Serviceのポート名webを選択
- ルール: platform_provision_ready == 0、for 10s、警告ラベルと説明。式とspecは維持します
- メトリクス・プロセスは正常でも、払い出しの依存関係は故障させられる、仮想のアプリ
- 観測ツール: python3 /opt/fixtures/cnpe_operator_lab.py
- VM内部のポート: アプリ30088、Prometheus 30090、Alertmanager 30093
observeは1回だけ参照します。captureにステップ番号を渡すと、最大150秒待ってから、決まったJSONを 保存します。どちらも、設定を直しません。timeoutになったら、再インストールせず、現在の設定と 動作を調べてから、もう一度観測してください。gradeにステップ番号を渡しても、保存したファイルは修正されません。
ステップ
- 用意されているServiceMonitor・PrometheusRuleを参照し、capture 1で/root/cnpe-alerts/baseline.jsonを保存してください。メトリクスの対象のupと、ルールのAPIへの保存を確認します。最初は、ルールが選択されていないため、ロードされません。あとでもう一度収集すると、そのときの実際の状態を記録することになり、最初の失敗を偽って記録することはしません。
- team-labネームスペースのlabhub.io/rulesラベルだけを、allowedに直してください。Prometheusのセレクターを、全ネームスペースに広げないでください。capture 2でnamespace.jsonを保存し、ほかのルール選択の条件がまだ残っていないかを確認してください。
- team-labのPrometheusRule provision-readyに、platform=trainingラベルを付けます。ルールのspecは変えません。capture 3でselected.jsonを保存し、Prometheus APIにprovisionルールグループが実際にロードされたかを確認してください。
- VM内部の払い出しアプリのPOST /modeに、JSON {"ready":false}を送って、依存関係の障害を作ってください。capture 4でfiring.jsonを保存します。払い出しのレスポンスは503、Prometheusはfiringですが、Alertmanager・Webhookにはアラートがない状態を確認します。外部のサービスには、障害を注入しません。
- platform-monitoringのPrometheus platformに、spec.alerting.alertmanagersを追加してください。namespace=platform-monitoring、name=alertmanager、port=webの項目が1つです。用意されている参照のRBACは維持します。capture 5でreceived.jsonを保存し、Alertmanagerの受信と、Webhookの未受信を分離してください。
- /root/cnpe-alerts/alertmanager.yamlに、内部のreceiverを書きます。route.receiver=local-webhook、group_by=[alertname]、group_wait=1s、group_interval=5s、repeat_interval=1hです。receiversのlocal-webhookは、URL(http://workbench.team-lab.svc:8080/hook과)とsend_resolved=trueを使います。同じネームスペースのSecret alertmanager-platform、キーalertmanager.yamlで適用したあと、capture 6でnotified.jsonを保存してください。このURLの外には送信しません。
- 現在のPodのfiringのWebhookを、先に確認してください。POST /modeに{"ready":true}を送って復旧し、capture 7でrecovered.jsonを保存します。201レスポンス、アクティブなアラートの解消、同じPodのfiring・resolvedの受信を、すべて確認します。以前のPodのresolvedを、今回の復旧として認めません。
- /root/cnpe-alerts/diagnose.pyにdiagnose(e)を実装してください。以下の診断契約に沿って、文字列を1つ返します。観測なし・矛盾・欠落・数値や文字列で書かれたboolは、unknownとして扱い、保存・発火・受信を1つの成功にまとめません。
ステップ8の診断契約
eには、observed・selected・loaded・firing・received・notified・recoveredの7つの実際のboolが、 すべて必要です。received・notifiedは、同じ障害がその境界を通過した履歴で、 firingは現在の発火の有無です。この関数は、すでに突き合わせ済みの証拠の要約を入力として受け取ります。
- 型・必須フィールドが誤っているか、observed=Falseなら、unknown
- selected=False: 以降の5つのフィールドがすべてFalseならnot_selected、そうでなければunknown
- selected=True、loaded=False: 以降の4つのフィールドがすべてFalseならnot_loaded、そうでなければunknown
- selected・loaded=Trueでrecovered=True: firing=False、received・notified=Trueのときだけrecovered
- 同じ条件でrecovered=False、notified=True: firing・received=Trueのときだけnotified
- notified・recovered=False、received=True: firing=Trueのときだけreceiver
- received・notified・recovered=False: firing=Trueならdelivery、Falseならinactive
- 上に書かれた「のときだけ」の条件を満たせなかった組み合わせは、unknown
参考
正常なメトリクス収集のupは、業務の成功の指標ではありません。このラボのgaugeは、依存関係の状態を 再現する値であり、本番のSLOではありません。グループのstatusだけで、個々のアラートの状態を決めないでください。 今回のステップの記録は、rule UID・pod UID・ルールの内容と突き合わせます。Podを作り直したなら、 観測もやり直す必要があります。このファイルの比較は、暗号学的なリモート証明でも、不正防止の仕組みでもありません。 ステップ1・4・5・6は、保存した過去の観測を、ステップ2・3・7は、現在の状態も確認します。正常に復旧したあとも、 過去の障害の記録を正常なJSONで上書きしないでください。受信記録はアプリのメモリにだけあり、再起動すると消えます。 ステップの準備は、前の構成だけを要求し、現在の答えや観測ファイルは作りません。ステップ7は、 復旧する前に、firingの受信を先に待ってください。ステップ8は、独立したコードの課題です。
外部Webhook・メール・チャットの送信、高可用性のアラート経路、実ユーザーのSLO検証は扱いません。 公式ドキュメント: ルール選択・ServiceMonitorのトラブルシューティング・アラートの状態。
メトリクスと保存されたルールを区別する
用意されているServiceMonitor・PrometheusRuleを参照し、capture 1で/root/cnpe-alerts/baseline.jsonを保存してください。メトリクスの対象のupと、ルールのAPIへの保存を確認します。最初は、ルールが選択されていないため、ロードされません。あとでもう一度収集すると、そのときの実際の状態を記録することになり、最初の失敗を偽って記録することはしません。
targetsがupでも、rulesにprovisionがないことがあります。2つのAPIは、別の問いに答えます。
ネームスペースの選択を狭く直す
team-labネームスペースのlabhub.io/rulesラベルだけを、allowedに直してください。Prometheusのセレクターを、全ネームスペースに広げないでください。capture 2でnamespace.jsonを保存し、ほかのルール選択の条件がまだ残っていないかを確認してください。
ruleNamespaceSelectorは、Namespaceのラベルを読みます。PrometheusRuleのラベルは、次の条件です。
ラベルを直して、実際のロードを確認する
team-labのPrometheusRule provision-readyに、platform=trainingラベルを付けます。ルールのspecは変えません。capture 3でselected.jsonを保存し、Prometheus APIにprovisionルールグループが実際にロードされたかを確認してください。
kubectlが成功した直後から、実際にロードされるまでには、時間がかかります。宣言ファイルだけを見ず、Prometheus API(/api/v1/rules를)を確認してください。
発火したのに配信されない障害
VM内部の払い出しアプリのPOST /modeに、JSON {"ready":false}を送って、依存関係の障害を作ってください。capture 4でfiring.jsonを保存します。払い出しのレスポンスは503、Prometheusはfiringですが、Alertmanager・Webhookにはアラートがない状態を確認します。外部のサービスには、障害を注入しません。
for: 10sは、実際の評価で条件が続く必要があります。/healthzの成功は、/provisionの成功ではありません。
Alertmanagerに接続して受信を確認する
platform-monitoringのPrometheus platformに、spec.alerting.alertmanagersを追加してください。namespace=platform-monitoring、name=alertmanager、port=webの項目が1つです。用意されている参照のRBACは維持します。capture 5でreceived.jsonを保存し、Alertmanagerの受信と、Webhookの未受信を分離してください。
receiverを直す前に、Prometheusがどのalertmanagerへ送っているかと、Endpointsの参照権限を見てください。
内部のWebhookへ実際に送信する
/root/cnpe-alerts/alertmanager.yamlに、内部のreceiverを書きます。route.receiver=local-webhook、group_by=[alertname]、group_wait=1s、group_interval=5s、repeat_interval=1hです。receiversのlocal-webhookは、URL(http://workbench.team-lab.svc:8080/hook과)とsend_resolved=trueを使います。同じネームスペースのSecret alertmanager-platform、キーalertmanager.yamlで適用したあと、capture 6でnotified.jsonを保存してください。このURLの外には送信しません。
Secretの名前だけでなく、キーの名前とrouteのreceiver参照が合っている必要があります。構成の反映時間も待ってください。
今回の障害の復旧かどうかを突き合わせる
現在のPodのfiringのWebhookを、先に確認してください。POST /modeに{"ready":true}を送って復旧し、capture 7でrecovered.jsonを保存します。201レスポンス、アクティブなアラートの解消、同じPodのfiring・resolvedの受信を、すべて確認します。以前のPodのresolvedを、今回の復旧として認めません。
グループのstatus 1つの値だけを見ず、alerts内の個々のstatusとpodラベルを比較してください。
空の証拠を成功に変えない診断ツール
/root/cnpe-alerts/diagnose.pyにdiagnose(e)を実装してください。以下の診断契約に沿って、文字列を1つ返します。観測なし・矛盾・欠落・数値や文字列で書かれたboolは、unknownとして扱い、保存・発火・受信を1つの成功にまとめません。
すべての必須フィールドが実際のboolであることを確認してから、前の境界から見ます。下流の成功と上流の失敗が一緒にあれば、矛盾です。