本文へスキップ
CoRISE

監視基盤の停止を誰が知るか — Kubernetesの外に観測点を置く

HAと障害領域の独立性を分け,外形監視・Watchdog・独立通知を組み合わせます.Probe欠落,検知時間,復旧確認まで保証範囲を明示します.

T. Asano公開 更新 約18分で読めます
  • Observability
  • Reliability
  • Operations
目次を開く

そのPrometheusが止まったことを,誰が観測するか

KubernetesにPrometheus,Alertmanager,Grafanaを配置する.Replicaを増やし,Anti-AffinityやPodDisruptionBudgetを設定する.個々のPodやNodeの障害に備えるうえで,有効な設計です.

しかし,監視対象と監視系が同じNetwork,電源,DNS,Cloud Accountに依存していれば,共通障害で観測と通知を同時に失う可能性があります.HAで残すReplicaと,障害領域の外へ残す観測点は,別の設計課題です.

Control Planeの停止だけで既存の全Podが直ちに止まるとは限りません.逆に,Control Planeが正常でもIngressや外部DNSの障害で利用者から見えなくなります.「ClusterがDown」という一語にまとめず,何が利用不能で,どこから観測できないのかを分けます.

本稿は監視から可観測性へに続き,観測基盤の共倒れを扱います.

設定・Review資料は,架空のProduction Clusterと外部VMを題材にしています.Prometheus・Blackbox Exporter・Alertmanagerの設定検証,Probe,通知,Heartbeat受信,障害注入は実行していません.すべてNOT_RUNです.外部ReceiverやVMをProvisionする完成品でも,実測した検知性能の報告でもありません.

1.HAと障害領域の独立性を分ける

Prometheusを別Nodeに置けば,片方のNode停止に耐えられる可能性が高まります.AlertmanagerのHAは,SilenceやNotification Log等を共有し,Partition時には通知喪失を避けるため重複を許容する設計です.PrometheusからはLoad Balancerで一台を選ぶのではなく,各AlertmanagerへAlertを送ります.[1]

これは,すべての外向き通信が失われたときにも通知できるという保証ではありません.PodDisruptionBudgetも主として自発的なDisruptionを制御するもので,Site全体の電源断を防ぎません.

検知したい共通障害分離を検討する対象
Pod / Node停止Placement,Host,Volume,Node上の通信
Cluster基盤の停止別ClusterまたはVM,独立した起動・管理経路
VPC / Site通信の停止Network,Upstream,VPN Gateway,配置Site
Accountや管理権限の問題Account,IAM,認証,請求・運用権限
通知Providerの停止Provider,Credential,配送・Escalation経路

「別VMなら独立」「別Providerなら独立」とは決めません.同じDNS,IdP,秘密情報の取得元,CI/CDを共有しているかもしれません.分離する対象は,検知したいFaultから逆算します.故障確率を独立と仮定して単純に掛け合わせる根拠も,配置先が違うだけでは得られません.

2.内部の診断系と,外部の観測系を持つ

Cluster内の監視は残します.Service Discoveryや内部Metricsは,Pod,Storage,Application,Dependencyの詳細を調べるために有用です.そこに小さなExternal Sentinelを加えます.

横にスクロールして図全体をご覧いただけます.

詳細診断,外形監視,Heartbeatの経路を分ける Production内は詳細診断,外部Sentinelは利用経路と監視EndpointをProbeし独立Pagerへ送る.両監視系は別StreamのHeartbeatを外部Receiverへ送る.配置だけで独立性は確定しない.
Fig. 01 — 詳細診断,外形監視,Heartbeatの経路を分ける
図を文章で読む

Production内は詳細診断,外部Sentinelは利用経路と監視EndpointをProbeし独立Pagerへ送る.両監視系は別StreamのHeartbeatを外部Receiverへ送る.配置だけで独立性は確定しない.

Sentinelの役割は,重要なEndpointに到達できるか,内部監視が応答するか,独立した通知先へAlertを送れるかを確かめることです.最初からLoki,Thanos,Grafanaをすべて複製する必要はありません.短いRetentionのPrometheus,Blackbox Exporter,Alertmanagerを別VMへ置く構成も候補です.

ただし一台のSentinelにも単一障害点があります.Sentinel自身のHeartbeatを,Productionのものとは別Streamで外部Receiverへ送ります.そのReceiverと通知経路を,どの障害領域へ委ねるかを記録します.Managed Serviceを選んでも,最後のProviderや人への配送という残余リスクはなくなりません.

IaCのRepositoryを共通にすることと,稼働時の依存を共通にすることは別です.NixOSやOpenTofuで再構築する場合も,Production停止中にCode,Artifact,Credentialへアクセスできるかを確認します.

3.Probeが通る経路と,確認する意味を定義する

Public ProbeはDNS,TLS,CDN,Load Balancer,Ingressを含む利用経路を見ます.Private Probeは管理経路からPrometheusやAlertmanager等を見ます.Kubernetes APIをProbeのためだけにInternet公開する必要はありません.

Private VPNを使うなら,VPN停止時に「全Private Probeが失敗する」という意味をRunbookへ残します.外部からのProbeでも,共通VPN GatewayやResolverに依存します.観測点とTargetだけでなく,その間の経路をInventoryへ記録します.

対象成功が示す範囲
ProcessのLivenessProcessが応答する
Prometheus / Alertmanagerの/-/readyそのComponentがReadyと報告する
PublicなRead-only Synthetic定義した利用経路と応答条件が満たされる
Watchdogの外部受信特定のAlert RouteでHeartbeatが到着する

Readinessは通知CredentialやRuleの内容を検証しません.Health Endpointの200も,すべてのBusiness機能が正常という意味にはなりません.外形監視用EndpointとKubernetesのLivenessを混同し,依存先の障害で大量の再起動を起こさないようにします.[9][10]

4.Blackbox Probeを利用者視点へ近づける

Blackbox ExporterはHTTP,DNS,TCP,ICMP,gRPC等をProbeし,結果をprobe_successとして公開します.PrometheusはTargetそのものではなく,Exporterの/probeをScrapeします.[2]

説明用のPublic HTTP Moduleは次です.

modules:
  http_public_ipv4:
    prober: http
    timeout: 5s
    http:
      method: GET
      valid_status_codes: [200]
      follow_redirects: false
      fail_if_not_ssl: true
      preferred_ip_protocol: ip4
      ip_protocol_fallback: false
      fail_if_body_not_matches_regexp: ['^ready\n?$']

ここではIPv4のみ,Redirectなし,HTTPS,200,BodyがreadyであることをContractにしています.対象の専用Endpointは未実装です.IPv4で成功してもIPv6の利用者経路は未確認なので,必要なら別Moduleと別系列を用意します.Redirect先のLogin PageやCDNのCacheが200を返しただけで,Originが正常だと判断しません.[3]

Prometheus側のScrape例は次です.Targetを__param_targetへ渡し,ExporterのAddressとTargetのIdentityを分けます.[4]

scrape_configs:
  - job_name: external-blackbox
    scrape_interval: 15s
    scrape_timeout: 10s
    metrics_path: /probe
    params:
      module: [http_public_ipv4]
    static_configs:
      - targets: [https://app.example.com/synthetic/ready]
        labels:
          target_role: public-application
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115

DNS・接続・TLS・HTTPのTimingは切り分けの手掛かりになりますが,一回のProbeだけでRoot Causeは確定しません.HostnameへのHTTP Probeは,その観測点のResolverとCacheを通った結果です.権威DNS全体の正しさを検証したことにはなりません.権威側の確認が必要なら,Resolverと質問内容を明示したDNS Probeを別に設計します.

Exporterの/probe?target=...は任意Targetへの通信を起こせます.例ではLoopbackへBindする運用を前提にし,公開Proxyとして利用できる状態にはしません.Private Moduleの証明書・鍵は指定Pathへ別途供給する前提で,配布物に秘密情報は含めません.

5.Probe失敗,Scrape失敗,系列欠落を分ける

probe_success == 0は,Probe結果を取得でき,その結果が失敗だった状態です.Exporterが停止して結果自体がない場合には,その式だけではAlertが出ません.

横にスクロールして図全体をご覧いただけます.

Target失敗と観測失敗を同じDownにしない 成功したScrapeからProbe成功・失敗を読む.upが0ならScrape障害,upが1で結果なしなら結果欠落,期待upがなければInventoryとの不一致である.評価器停止には外部Heartbeatが必要.
Fig. 02 — Target失敗と観測失敗を同じDownにしない
図を文章で読む

成功したScrapeからProbe成功・失敗を読む.upが0ならScrape障害,upが1で結果なしなら結果欠落,期待upがなければInventoryとの不一致である.評価器停止には外部Heartbeatが必要.

状態見るもの主な解釈
up=1,probe_success=1正常なProbe結果その経路・時刻でContractを満たす
up=1,probe_success=0TargetへのProbe失敗Targetまたは途中経路を調べる
up=0Scrape失敗Exporter,Scrape経路,Timeout,構成等を調べる
up=1,Probe系列なし結果の欠落ModuleやExporterの出力Contractを調べる
期待するup系列なしTarget / Jobの欠落Discovery,設定削除,評価対象の欠落を調べる

サンプルのRulesはこれらを分けます.本文では一部を示します.

groups:
  - name: external-observation
    rules:
      - alert: EndpointProbeFailed
        expr: probe_success{job=~"external-blackbox|management-blackbox"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: 'Probe contract failed from the external sentinel.'
      - alert: ProbeScrapeFailed
        expr: up{job=~"external-blackbox|management-blackbox"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: 'Probe result could not be scraped; target state is unknown.'
      - alert: ProbeResultMissing
        expr: |
          (up{job=~"external-blackbox|management-blackbox"} == 1)
          unless on (job, instance)
          probe_success{job=~"external-blackbox|management-blackbox"}
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: 'Scrape succeeded but the expected probe result is absent.'

同梱のRuleには既知Targetごとのabsent(up{...})もあります.Job全体のabsentだけでは,一つだけ消えたTargetを見逃すためです.InventoryとAlert Ruleを同じ変更で両方削除すれば検知できなくなるので,期待Targetの変更自体をReview対象にします.[6]

for: 2mは,式が評価時点で継続してActiveだった期間です.「必ず8回連続失敗」のCounterではありません.系列欠落や評価停止も考慮します.Prometheus自身が止まれば,これらのRuleも評価されないため,外部のDead-man検知が必要です.[5]

6.外部Alertの通知経路をProductionから分離する

外部Probeが失敗しても,そのAlertをProduction内のAlertmanagerだけへ送れば,共倒れ時に通知できません.Sentinel側にAlertmanagerを置き,Productionを経由しないReceiverへ送ります.

独立性は,通知ProcessのPlacementだけではありません.DNS,Proxy,NAT,Credential,Pager Provider,On-call設定,人が使う認証・端末まで経路を追います.同じProviderのEmailとSMSなら,Provider障害に対しては独立でない可能性があります.

複数経路を設ける場合も,何を二重化するかを決めます.すべてのAlertを無差別に複製すると,重要な通知がNoiseに埋まります.Monitoring FailureやSite Failureだけ別ProviderへEscalateする構成も候補です.

HA AlertmanagerはReplica数を増やすだけで完成しません.Prometheusから各Instanceへの送信,Network Partition時の重複,Replica LabelによるDedupの違いを確認します.このサンプルは外部VM一台を説明するもので,HA ClusterをProvisionしていません.[1]

7.Watchdogで「特定のAlert Routeが黙った」を検知する

Watchdogは,常にFiringするAlertをReceiverへ繰り返し送り,外部Systemが到着の停止を検知する仕組みです.Prometheus OperatorのRunbookも,Watchdogが止まった場合に外部Systemから通知することを説明しています.[7]

groups:
  - name: internal-watchdog
    rules:
      - alert: Watchdog
        expr: vector(1)
        labels:
          severity: none
          monitoring_system: production-cluster
        annotations:
          summary: 'Production alert-route heartbeat.'

内部Alertmanagerには,通常のPager Routeを置き換えず,Watchdog専用のRouteとReceiverを追加します.

# Merge into internal Alertmanager; preserve its normal receivers/routes.
route:
  routes:
    - matchers:
        - alertname="Watchdog"
        - monitoring_system="production-cluster"
      receiver: production-heartbeat
      group_by: [alertname, monitoring_system]
      group_wait: 0s
      group_interval: 1m
      repeat_interval: 1m
receivers:
  - name: production-heartbeat
    webhook_configs:
      - url_file: /run/secrets/production-heartbeat-url
        send_resolved: false
        http_config:
          authorization:
            credentials_file: /run/secrets/production-heartbeat-token

これはConfigへMergeする断片です.RuntimeのSecret FileにHTTPSの受信先とCredentialを置きます.ReceiverはAlertmanagerのWebhook Payloadを受け取る契約であり,任意のHeartbeat Serviceがそのまま互換という意味ではありません.必要なAdapterは配布物に含みません.[8]

send_resolved: falseとし,ReceiverでもFiring,Alert名,Stream Identity,認証を確認します.Resolvedや別Alert,任意のPOSTを「正常Heartbeat」として扱いません.SilenceやInhibitionでWatchdogを止めたときも欠落として検知するか,期限付きMaintenanceとして扱うかを決めます.

HA構成の一つのLogical Watchdogは,少なくとも一つの経路が生きているEvidenceです.すべてのPrometheus ReplicaやAlertmanager Replicaの正常性は,別のComponent監視で確認します.

8.Watchdogを「全通知の正常証明」にしない

Watchdogが確認するのは,そのRuleとRouteから特定Receiverまでの経路です.すべての業務Alert Ruleの正しさ,別ReceiverのCredential,Pagerの配送,人が通知を認識したことまでは確認しません.

例えばHeartbeat専用Credentialが正常でも,通常PagerのCredentialだけ期限切れかもしれません.この故障を専用Watchdogだけで検知できるとは書けません.実際のCritical通知経路を通るSynthetic Alertと,人のAcknowledgementを含む訓練を別に設けます.

横にスクロールして図全体をご覧いただけます.

専用Heartbeatと通常Pagerは別の契約である WatchdogはRule,専用Route,Receiver到着を観測する.通常PagerのCredentialと人のAcknowledgementは別のSyntheticで確認する.到着時刻は新しいRule評価の保証ではない.
Fig. 03 — 専用Heartbeatと通常Pagerは別の契約である
図を文章で読む

WatchdogはRule,専用Route,Receiver到着を観測する.通常PagerのCredentialと人のAcknowledgementは別のSyntheticで確認する.到着時刻は新しいRule評価の保証ではない.

またPrometheus停止直後にHeartbeatが必ず止まるわけではありません.Alertmanagerが既存のFiring Alertを扱う期間やRetry,遅延配送が,検知を遅らせ得ます.Prometheusが更新するAlertの有効期限とReceiverの受信時刻を区別します.resolve_timeoutだけでPrometheus由来AlertのExpiryを一律に決めるものではありません.[8]

通常のWebhook到着だけでは,新しいRule評価に由来する通知と遅延・Replayを完全には区別できません.startsAtは常時Firing Alertの開始時刻であり,毎回の発行時刻ではありません.単純な重複拒否も正当なRepeat通知を落とし得ます.Freshnessの強い保証が必要なら,別の時刻・Sequence契約等を設計し,実装と検証が必要な部分として残します.

9.検知時間をChain全体で見積もる

「Probeは15秒間隔」「Heartbeatは毎分」だけでは,何分以内に人へ届くかは決まりません.外形監視には,概念的に次の遅延があります.

Probe scheduling + probe timeout + rule evaluation alignment
+ alert for duration + Alertmanager grouping
+ notification delivery + human acknowledgement

Heartbeat側には,上流の沈黙が最終Heartbeatへ反映されるまでの遅れ,ReceiverのGrace,欠落判定周期,通知とEscalationが加わります.repeat_intervalはgroup_intervalとの関係を持つため,専用Routeで揃えます.例の1分Repeatや3分Grace案は測定済みSLOではありません.[8]

初回Heartbeatを一度も受け取らなかったStreamも検知対象にします.Receiverを「最初の到着後にだけ監視開始」にすると,最初から壊れたDeploymentが沈黙したままになります.期待Streamの登録,起動猶予,Maintenance終了,削除の承認を別に管理します.

SLOへするなら,条件成立,Rule Firing,Receiver到着,Paging到着,Acknowledgementのどこを測るかを明確にします.通知先が停止している期間まで含む無条件の最大検知時間は,この構成だけでは保証できません.

10.複数観測点の結果は,仮説として読む

Public Endpoint,Private管理Endpoint,Watchdogを合わせると,調査の入口が増えます.ただしどの組合せも原因の確定表ではありません.

Watchdog受信Public Probe調査の入口
継続成功この二つのContractは観測できている
停止成功Heartbeat Route,Receiver,内部監視を調べる
継続失敗Application,Edge,DNS,観測側経路を調べる
停止失敗共通障害を疑い,独立経路から切り分ける
不明データ欠落まず観測系の状態を確認する

複数地域で全Probeが失敗しても,共通Resolverや設定誤配布が原因かもしれません.一地域だけの失敗を無視すると,その地域の利用者影響を見逃す可能性もあります.

2-of-3等の判定を使うなら,期待観測点数,系列の鮮度,欠落をどう数えるか,集約系自身の可用性を定義します.別PrometheusのDataは自然には同じQueryに集まりません.中央の集約をProduction Clusterへ置けば,新しい共倒れ依存になります.欠落を暗黙の成功票にしないことが重要です.

11.障害注入と復旧確認を受入条件にする

安全に隔離した環境や承認されたMaintenanceで,次を別々に試す計画を作ります.本稿では実行していません.

故障期待する観測未確認のまま残してはいけない点
Synthetic Endpoint不応答Probe失敗AlertPager到着と担当者の確認
Blackbox Exporter停止Scrape失敗AlertTarget障害と誤分類しない
Target設定の削除期待系列欠落AlertInventory側まで同時に消していない
内部Prometheus停止Readiness失敗,後にHeartbeat欠落最終Repeatから欠落通知までの実測
Heartbeat Credential失効内部Firing継続,外部欠落Receiverが誤って正常扱いしない
通常Pager Credential失効Critical RouteのSyntheticが届かないWatchdog正常でも見逃さない
Sentinel停止Sentinel専用Streamの欠落Productionと同じ経路へ依存していない

全Replicaを止めるFaultと一台だけ止めるFaultを分けます.一台停止でWatchdogが継続するのは,HAの意図した挙動かもしれません.Cluster,DNS,VPN,Site,通知ProviderのFaultも,対象範囲と復旧方法を決めたうえで追加します.

復旧時は外部観測を残し,API,Ingress,Application,内部監視,通知が順に戻ったことを確認します.Readinessが戻ったことと,Alert Ruleや実通知が戻ったことを別に記録します.

横にスクロールして図全体をご覧いただけます.

独立性をReviewし,故障と復旧で確認する Faultごとに共通依存と観測・通知のOwnerを決め,隔離環境で故障を試す.最終正常観測から実通知・確認までを測定し,復旧後はReadinessと通知の両方を確認する.
Fig. 04 — 独立性をReviewし,故障と復旧で確認する
図を文章で読む

Faultごとに共通依存と観測・通知のOwnerを決め,隔離環境で故障を試す.最終正常観測から実通知・確認までを測定し,復旧後はReadinessと通知の両方を確認する.

12.観測不能を明示したFailure Modeにする

Monitoring Inventoryには,Componentごとに「誰が観測し,誰へ通知するか」を残します.同梱資料では,障害領域の対応表,期待TargetとHeartbeat Stream,Receiver契約,未実行の訓練計画とEvidence Recordを分けています.

Owner,通知Provider,VM配置,Credential供給,Secret保管,SLOは未採用の欄を空欄にしています.Architecture Diagramを描いたことを,独立性が実環境で成立したEvidenceにはしません.

内部のObservability Platformは,詳細を説明するためにある.外部のSentinelは,定義した経路がまだ応答し,観測・通知の経路が黙っていないかを確認するためにある.どちらも必要で,確認できる範囲は異なります.

監視基盤と同じ障害で失われない観測点と通知経路を置き,その独立性を実際の故障で確かめる. 「誰がこのPrometheusの停止を知るか」に答えられることを,Monitoring Architectureの完了条件へ加えます.

関連する可観測性基盤への刷新は設計判断と担当範囲の文脈です.本稿のSentinel,Config,未実行の訓練計画を,その案件で採用・検証済みのEvidenceとは扱いません.

参考資料

公式DocumentationのSource Revisionを固定しています.これは編集上の参照版であり,インストール済みBinaryや検証済みDeploymentの版ではありません.

  1. Alertmanager High Availability
  2. Blackbox Exporter
  3. Blackbox Exporter Configuration
  4. Prometheus Configuration
  5. Prometheus Alerting Rules
  6. PromQL Functions / absent
  7. Prometheus Operator: Watchdog Runbook
  8. Alertmanager Configuration
  9. Prometheus Management API
  10. Alertmanager Management API

T. Asano

T. Asanoの記事を読む

Contact

技術的な課題を、お聞かせください。

設計や実装、運用の課題について、CoRISEにご相談いただけます。

相談する