Prometheus Blackbox ExporterでHTTP、TCP、DNS、ICMPをエージェントなしで能動的に監視する

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

半年前、午前2時にアラートで起こされた。重要なAPIエンドポイントがいつの間にか応答しなくなっていたのだが、当時の監視システムはプロセスが動いているかどうかしか確認していなかった——外部から実際に到達できるかどうかは見ていなかったのだ。この出来事をきっかけに本格的なブラックボックス監視を導入することになり、Prometheus Blackbox Exporterはそれ以来ずっと本番環境で稼働し続けている。

アプリケーションを内側から計測するエージェントベースの監視とは異なり、Blackbox Exporterはユーザーと同じように外部からサービスを確認する。コードの変更も不要、サービスごとのエージェントのデプロイも不要だ。URL、TCPソケット、DNSリゾルバー、またはpingするホストを指定すれば、外部のオブザーバーが見るものをそのまま報告してくれる。

クイックスタート:5分で動かす

最速の方法はDockerを使うことだ:

docker run -d \
  --name blackbox_exporter \
  -p 9115:9115 \
  prom/blackbox-exporter:latest

これで http://localhost:9115 で動作するExporterが立ち上がった。すぐにテストしてみよう:

# HTTPでgoogle.comをプローブする
curl "http://localhost:9115/probe?target=https://google.com&module=http_2xx"

レスポンスはPrometheusの生メトリクスだ——probe_success(1 = 稼働中、0 = ダウン)、レスポンスタイム、TLS証明書の有効期限、HTTPステータスコードが含まれる。アプリケーションのコードに手を加えることなく、必要な情報がすべて揃う。

本番環境では設定ファイルをマウントしよう:

# blackbox.yml
modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: [200, 201, 204]
      follow_redirects: true
      preferred_ip_protocol: "ip4"

  tcp_connect:
    prober: tcp
    timeout: 5s

  icmp:
    prober: icmp
    timeout: 5s

  dns_check:
    prober: dns
    timeout: 5s
    dns:
      query_name: "example.com"
      query_type: "A"
docker run -d \
  --name blackbox_exporter \
  -p 9115:9115 \
  -v ./blackbox.yml:/config/blackbox.yml \
  prom/blackbox-exporter:latest \
  --config.file=/config/blackbox.yml

詳細解説:各プローブタイプを理解する

Blackbox Exporterは4種類のプローブタイプに対応している。どれをいつ使うべきかを理解することは、思っている以上に重要だ。

HTTPプローブ

HTTPプローブは設定作業の約80%を占める。ポート80が開いているかどうかを確認するだけでなく、レスポンスコードの検証、最初のバイトまでの時間の計測、TLSチェーンの検証、ボディの正規表現マッチングを伴うPOSTリクエストなど、多くの機能を備えている。

modules:
  http_post_json:
    prober: http
    timeout: 10s
    http:
      method: POST
      headers:
        Content-Type: application/json
        Authorization: "Bearer your_token_here"
      body: '{"ping": "check"}'
      valid_status_codes: [200]
      fail_if_body_not_matches_regexp:
        - '"status":"ok"'

TCPレベルのチェックでは決して発見できないものを捕捉できる:HTTP 200を返しながらも、ボディにエラーページを返すサービスだ。このシナリオは思っているより一般的で、誤って設定されたロードバランサー、中途半端に壊れたデプロイ、古くなったヘルスエンドポイントなどで発生する。

TLS証明書の監視は特に言及する価値がある。メトリクス probe_ssl_earliest_cert_expiry は、チェーン内で最も早く期限が切れる証明書のUnixタイムスタンプを返す。Prometheusのアラートルール1つで対応できる:

groups:
  - name: ssl_expiry
    rules:
      - alert: SSLCertExpiringSoon
        expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "SSL証明書が30日以内に期限切れになります: {{ $labels.instance }}"

TCPプローブ

TCPプローブはHTTP以外のサービス——データベース、SMTP、LDAP、カスタムバイナリプロトコルをカバーする。probe_successprobe_duration_seconds の両方を追跡することで、ポートが技術的に接続を受け入れている場合でも、レイテンシのスパイクを検出できる。

modules:
  tcp_postgres:
    prober: tcp
    timeout: 5s
    tcp:
      preferred_ip_protocol: "ip4"

  tcp_smtp_starttls:
    prober: tcp
    timeout: 10s
    tcp:
      query_response:
        - expect: "^220 "
        - send: "EHLO prober\r\n"
        - expect: "^250-STARTTLS"

クエリ/レスポンス機能は本当に便利だ——ポート25が接続を受け入れているかどうかだけでなく、SMTPサーバーが正しいプロトコルを話しているかどうかを確認できる。

ICMPプローブ

昔ながらのpingを既存のPrometheusスタックに統合できる。ICMPプローブはルーター、ファイアウォール、VPNトンネルのエンドポイントなど、ネットワークインフラ向けに使用している。Linux上では、バイナリにrawソケットのパーミッションが必要だ:

# バイナリを直接インストールする場合
sudo setcap cap_net_raw+ep blackbox_exporter

# Dockerの場合は--privilegedを使う(スコープに注意)
docker run -d --privileged \
  -p 9115:9115 \
  prom/blackbox-exporter:latest

DNSプローブ

DNS監視は、CDNの伝播問題や誤って削除されたレコードが障害を引き起こすまで、多くのチームが見落としがちなものだ。DNSプローブは特定のレコードが期待通りに解決されるかどうかを確認する:

modules:
  dns_a_record:
    prober: dns
    timeout: 5s
    dns:
      transport_protocol: "udp"
      preferred_ip_protocol: "ip4"
      query_name: "api.yourapp.com"
      query_type: "A"
      validate_answer_rrs:
        fail_if_not_matches_regexp:
          - "api.yourapp.com.\t.*\tIN\tA\t203.0.113."

これはDNSが応答するかどうかだけでなく、正しいIPレンジを返すかどうかも検証する——ユーザーが問題に遭遇する前に設定ミスを検出できる。

高度な使い方:Prometheusへの組み込み

Blackbox Exporter単体でも便利だが、Prometheusにrelabelingと組み合わせて組み込むと強力になる。コツはターゲットURLをスクレイプアドレスとしてではなく、クエリパラメーターとしてExporterに渡すことだ:

# prometheus.yml
scrape_configs:
  - job_name: "blackbox_http"
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
          - https://yourapp.com/health
          - https://api.yourapp.com/v1/status
          - https://yourapp.com/login
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: blackbox_exporter:9115

  - job_name: "blackbox_tcp"
    metrics_path: /probe
    params:
      module: [tcp_connect]
    static_configs:
      - targets:
          - postgres-primary:5432
          - redis-cluster:6379
          - kafka-broker:9092
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: blackbox_exporter:9115

このrelabelingブロックは2つのことを行う:ターゲットアドレスをクエリパラメーターにコピーし、スクレイプアドレスをExporterに向けて書き換える。各結果は元のターゲットURLを instance ラベルとして保持する——ダッシュボードを読みやすく保つためだ。

Grafana向け便利なPromQLクエリ

コミュニティのGrafanaダッシュボードID 7587 は良い出発点だ。以下は私が常に手動で追加するクエリだ:

# 24時間の稼働率(パーセント)
avg_over_time(probe_success{job="blackbox_http"}[24h]) * 100

# HTTPレスポンスタイムのp95
histogram_quantile(0.95, rate(probe_http_duration_seconds_bucket[5m]))

# SSL証明書の期限切れまでの日数
(probe_ssl_earliest_cert_expiry - time()) / 86400

本番運用6ヶ月で学んだ実践的なヒント

6ヶ月間本番環境で運用して、ドキュメントには書かれていないことをいくつか学んだ。最初の自分に伝えるとしたら、こういうことだ。

タイムアウトをスクレイプ間隔より短く設定すること。 両方が30秒だと、遅いターゲットがスクレイプサイクル全体をブロックしてしまう。プローブのタイムアウトはスクレイプ間隔の60〜70%に設定しよう。

複数の場所から監視すること。 Blackbox Exporterのインスタンスが1つだと、その場所からサービスがダウンしているように見えるかどうかしかわからない。異なるリージョンに2〜3つのインスタンスがあれば、ルーティングの問題なのか、リージョナルな障害なのか、本当のサービス障害なのかがわかる。各スクレイプ設定に probe_location ラベルを追加すれば、Grafanaのラベルセレクターで簡単に分離できる。

プローブの頻度を上げすぎないこと。 最初の頃、50エンドポイントに10秒間隔のプローブを設定していた——それだけで監視レイヤーから毎分300の外部HTTPリクエストが発生する。ほとんどのエンドポイントでは、30〜60秒の間隔で十分だ。

セットアップ中はデバッグエンドポイントを活用すること。 プローブが予期しない動作をするときは、&debug=true を付け加えよう:

curl "http://localhost:9115/probe?target=https://yourapp.com&module=http_2xx&debug=true"

これにより各ステップの詳細なトレースが返される——DNS解決時間、TCPハンドシェイクの所要時間、TLSネゴシエーション、HTTPリダイレクトチェーン。問題を数時間ではなく数分で解決できるようになる。

probe_successだけでなく、プローブの所要時間にもアラートを設定すること。 8秒で応答するログインページは技術的には「稼働中」だが、機能的には壊れている。probe_duration_seconds > 3 にアラートを設定することで、報告された障害になる前にパフォーマンスの低下を検出できる。

2段階のSSL有効期限アラートを使うこと。 30日前と7日前の2回アラートを設定しよう。最初のアラートを見逃しても直接障害にはならないが、7日前のアラートを見逃すと障害になりかねない。

新しいサービスの追加は1分もかからない:Prometheusのスクレイプ設定に1行ターゲットを追加するだけだ。すぐに可用性、レスポンスタイム、証明書の有効性が監視される。エージェントのインストールも、アプリケーションの変更も、別チームとの調整も不要だ。

Share: