Prometheus Blackbox Exporter: HTTP、DNS、TCP、SSL証明書の有効期限チェック

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

なぜBlackbox Exporterが必要なのか

自分の監視環境はPrometheus + Grafanaで15台のサーバーを管理していて、ユーザーから障害報告が来る前に問題を検知したことも何度かある。ただ、一つ大きな死角があった。サーバーのCPU・RAM・ディスクは正常だと分かっても、ウェブサイトが実際にレスポンスを返しているか、DNSが正しく名前解決できているか、SSL証明書があと何日で切れるかが把握できていなかった。

Blackbox Exporterはまさにその問題を解決してくれる。各サーバーにエージェントをインストールするホワイトボックス監視とは違い、外部からチェックする――つまり、ユーザーが実際にブラウザでURLを入力するのと同じ視点で確認できる。HTTP・HTTPS・DNS・TCP・ICMPでエンドポイントをプローブし、結果をPrometheusがスクレイプする仕組みだ。

実際に経験したケース:あるサブドメインのSSL証明書が期限切れになっても誰も気づかず、ブラウザでエラーが出てから発覚した。Blackboxで「残り14日」のアラートを設定してからは、Telegram通知を早めに受け取って余裕を持って更新できるようになり、深夜に慌てて対処する羽目にならなくなった。

Blackbox Exporterのインストール

シンプルな構成なのでDockerは使わず、Prometheusが動いているサーバーに直接インストールする。

バイナリのダウンロードとインストール

# 最新バージョンをダウンロード(github.com/prometheus/blackbox_exporterで確認)
wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.25.0/blackbox_exporter-0.25.0.linux-amd64.tar.gz

tar xvf blackbox_exporter-0.25.0.linux-amd64.tar.gz
cd blackbox_exporter-0.25.0.linux-amd64

# バイナリと設定ファイルをコピー
sudo cp blackbox_exporter /usr/local/bin/
sudo mkdir -p /etc/blackbox_exporter
sudo cp blackbox.yml /etc/blackbox_exporter/

systemdサービスの作成

sudo tee /etc/systemd/system/blackbox_exporter.service <<EOF
[Unit]
Description=Prometheus Blackbox Exporter
After=network.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/blackbox_exporter \
  --config.file=/etc/blackbox_exporter/blackbox.yml \
  --web.listen-address=:9115
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now blackbox_exporter
sudo systemctl status blackbox_exporter

完了したら http://<server-ip>:9115 にアクセスしてサービスが起動していることを確認する。設定済みモジュールの一覧を表示するシンプルなWebインターフェースが表示される。

プローブモジュールの設定

ファイル /etc/blackbox_exporter/blackbox.yml で「モジュール」を定義する。各モジュールは異なる種類のチェックを表す。以下は実際のプロダクション環境で使っている設定だ:

modules:
  # HTTP 2xxチェック — 一般的なWebサイト向け
  http_2xx:
    prober: http
    timeout: 10s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: [200, 201, 301, 302]
      method: GET
      follow_redirects: true
      preferred_ip_protocol: "ip4"

  # HTTPS + SSL証明書チェック
  http_2xx_tls:
    prober: http
    timeout: 10s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: [200]
      method: GET
      follow_redirects: true
      tls_config:
        insecure_skip_verify: false   # SSL検証を必須にする

  # TCPポートの開放チェック(MySQL、Redis、カスタムサービス)
  tcp_connect:
    prober: tcp
    timeout: 5s

  # DNS名前解決チェック
  dns_check:
    prober: dns
    timeout: 5s
    dns:
      query_name: "google.com"      # チェックするドメイン名
      query_type: "A"               # A、AAAA、MX、CNAMEなど
      valid_rcodes:
        - NOERROR

Prometheusスクレイプの設定

ここがBlackbox Exporterと通常のエクスポーターとの大きな違いだ。直接スクレイプする代わりに、Prometheusはリラベリングの仕組みを使ってスクレイプ時にターゲットURLを渡す。つまり、1つのBlackboxインスタンスで何十ものURLをプローブでき、複数のプロセスを起動する必要がない。MySQLやPostgreSQLを専用Exporterで監視している場合も、このrelabelの考え方は共通している:

# prometheus.ymlに追加
scrape_configs:

  # WebサイトのHTTP/HTTPSチェック
  - job_name: 'blackbox_http'
    metrics_path: /probe
    params:
      module: [http_2xx_tls]
    static_configs:
      - targets:
          - https://itfromzero.com
          - https://example.com
          - https://api.yourdomain.com/health
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: localhost:9115   # Blackbox ExporterのIPアドレス

  # TCPポートチェック
  - job_name: 'blackbox_tcp'
    metrics_path: /probe
    params:
      module: [tcp_connect]
    static_configs:
      - targets:
          - db-server:3306     # MySQL
          - cache-server:6379  # Redis
          - app-server:8080    # アプリポート
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: localhost:9115

  # DNSチェック
  - job_name: 'blackbox_dns'
    metrics_path: /probe
    params:
      module: [dns_check]
    static_configs:
      - targets:
          - 8.8.8.8    # チェック対象のDNSサーバー
          - 1.1.1.1
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: localhost:9115

設定変更後にPrometheusをリロードする:

curl -X POST http://localhost:9090/-/reload

結果の確認とアラート設定

Prometheusを待たずにcurlで素早くテスト

次のスクレイプサイクルを待つ必要はない――Blackboxのエンドポイントに直接リクエストしてすぐに確認できる:

# HTTPチェック
curl -s "http://localhost:9115/probe?target=https://itfromzero.com&module=http_2xx_tls" | grep probe_success
# 期待する結果: probe_success 1

# SSL証明書の残り有効秒数
curl -s "http://localhost:9115/probe?target=https://itfromzero.com&module=http_2xx_tls" | grep ssl_earliest_cert_expiry
# probe_ssl_earliest_cert_expiry 1.7XXXXXXXXX+09  ← 証明書の有効期限のUnixタイムスタンプ

# TCPチェック
curl -s "http://localhost:9115/probe?target=db-server:3306&module=tcp_connect" | grep probe_success

PrometheusのAlertingルール

アラートルールこそが、Blackboxを「眺めるだけのツール」から本格的な監視システムへと変える要素だ。管理しやすいよう別ファイルに分けている:

# /etc/prometheus/rules/blackbox.yml
groups:
  - name: blackbox_alerts
    rules:

      # サイトダウン
      - alert: WebsiteDown
        expr: probe_success{job="blackbox_http"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "サイトダウン: {{ $labels.instance }}"
          description: "{{ $labels.instance }} が2分以上応答していません。"

      # HTTPレスポンスタイムが遅い
      - alert: SlowResponseTime
        expr: probe_duration_seconds{job="blackbox_http"} > 3
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "レスポンス遅延: {{ $labels.instance }}"
          description: "レスポンスタイム {{ $value | humanizeDuration }} が3秒を超えています。"

      # SSL証明書の期限切れが近い(14日前)
      - alert: SSLCertExpiringSoon
        expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 14
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "SSL期限切れ間近: {{ $labels.instance }}"
          description: "証明書の残り有効期限: {{ $value | humanize }} 日。今すぐ更新してください!"

      # SSL証明書が期限切れ
      - alert: SSLCertExpired
        expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "SSL期限切れ: {{ $labels.instance }}"

      # TCPポートがダウン
      - alert: TCPPortDown
        expr: probe_success{job="blackbox_tcp"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "ポートダウン: {{ $labels.instance }}"
          description: "{{ $labels.instance }} に接続できません。"

prometheus.ymlにルールファイルを追加する:

rule_files:
  - "/etc/prometheus/rules/*.yml"

Grafanaダッシュボード

Grafana.comからダッシュボードID 7587 をインポートするだけでよい――ゼロから構築する必要はない。このダッシュボードにはプローブのステータス・レスポンスタイム・SSL残日数・HTTPステータスコードがすべて表示される。自分はこれをメインのオーバービュー画面として使っている。

カスタマイズが必要な場合に自分でパネルを作るためのPromQLクエリをいくつか紹介する:

# SSL証明書の残り日数
(probe_ssl_earliest_cert_expiry{job="blackbox_http"} - time()) / 86400

# 24時間のアップタイム率(%)
avg_over_time(probe_success{job="blackbox_http"}[24h]) * 100

# HTTPステータスコード
probe_http_status_code{job="blackbox_http"}

実運用での注意点

  • ファイアウォール: Blackboxを動かすサーバーはインターネットへの通信(ポート80・443・53)が必要だ。プライベートネットワーク内にあってNAT/プロキシがない場合、外部へのHTTPプローブが失敗する――サイトがダウンしていると誤認しやすいので注意。
  • DNSモジュール: デフォルト設定では query_namegoogle.com を使っている。内部DNSサーバーを確認したい場合は内部ドメインに変更すること――内部DNS経由でgoogle.comをチェックしてもあまり意味がない。
  • タイムアウトとスクレイプ間隔: タイムアウトはスクレイプ間隔より短く設定する必要がある。Prometheusのデフォルトスクレイプ間隔は30秒なので、HTTPのタイムアウトを30秒以上にするとプローブが重複してメトリクスが不正確になる。HTTPは10秒、TCP/DNSは5秒にしておけば問題ない。
  • TLS insecure設定: プロダクション環境では絶対に insecure_skip_verify: true を設定しないこと。BlackboxのコアミッションはSSL障害の検知なので、検証をスキップするのは自分の足を撃つようなものだ。
  • アラートルーティング: severity: critical はTelegramに、warningメールに振り分けている。深夜のcriticalアラートをメールだけで受け取っても翌朝まで気づかない――手遅れになってからでは対処が間に合わない。

Share: