Cloudflare Exporter、Prometheus、GrafanaによるCloudflareのトラフィックとセキュリティの一元監視

課題:エッジとオリジンサーバー間におけるデータの分断

現在、多くのシステムではCDN、DNS、DDoS攻撃を防ぐWAFを活用するために、最前線にリバースプロキシとしてCloudflareを配置しています。トラブルが発生しない限りは極めてスムーズに稼働しますが、Cloudflareのデフォルトダッシュボードは直感的である一方、バックエンドサーバー群とは完全に切り離されています。

よくあるシナリオを思い浮かべてみてください。ユーザーから「Webサイトが遅い」「502 Bad Gatewayエラーが出る」といった報告が上がります。調査のため、Cloudflareのタブを開いてリクエスト状況を確認し、別のタブでGrafanaを開いてCPU/RAM使用率をチェックし、さらにターミナルを開いてNginxのアクセスログをgrepする……。このように3〜4つのツールを行き来するだけで、初動対応が少なくとも10〜15分は遅れてしまいます。さらに無料プランでは詳細なアナリティクスが24〜72時間しか保持されません。先週発生したスクレイピングボットの挙動を調査しようとしても、過去データを確認することはほぼ不可能です。

最も効果的な解決策は、cloudflare-exporterを使用してCloudflare GraphQL Analytics APIからすべてのメトリクスをPrometheusに取り込むことです。エッジ側の指標とオリジンサーバーのリソース状況をひとつのGrafanaダッシュボードに統合すれば、Cloudflare側で詰まっているのか、バックエンドの負荷が高騰しているのかを数クリックで即座に切り分けられるようになります。

Cloudflare Exporterの導入

Cloudflare ExporterはGo言語で記述されたサービスであり、中継アダプターとして機能します。定期的にCloudflareのGraphQL APIを呼び出して統計データを取得し、/metricsエンドポイントでPrometheusメトリクス形式に変換して公開します。

ステップ1:Cloudflare APIトークンの作成

Exporterがデータを取得できるようにするには、過剰な権限を持つリスクの高いGlobal API Keyではなく、Analytics参照権限に絞ったトークンを発行します:

  1. Cloudflareダッシュボードにログインし、My Profile > API Tokensへ移動します。
  2. Create Tokenをクリックし、Read analytics and logsテンプレートを選択します(またはCreate Custom Tokenを選択)。
  3. トークンに付与する最小限の権限を設定します:
    • Zone > Zone > Read
    • Zone > Analytics > Read
  4. Zone Resourcesセクション:セキュリティを考慮し、Include > Specific zoneを選択して監視対象のドメインを指定します(複数ドメインを一括管理する場合はAll zones)。
  5. Continue to summary > Create Tokenをクリックし、発行されたトークン文字列をコピーしてパスワードマネージャー等に保存します。

ステップ2:Docker ComposeによるCloudflare Exporterの起動

Docker Composeを使用することで、コンテナを最もシンプルに管理できます。監視サーバー上に作業ディレクトリを作成します:

mkdir -p /opt/cloudflare-exporter && cd /opt/cloudflare-exporter
nano docker-compose.yml

docker-compose.ymlファイルに以下の設定を記述します:

version: '3.8'

services:
  cloudflare-exporter:
    image: ghcr.io/lablabs/cloudflare-exporter:latest
    container_name: cloudflare_exporter
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - CF_API_TOKEN=your_cloudflare_token_here
      - CF_ZONES=example.com,app.example.com
      - SCRAPE_INTERVAL=60s
      - METRICS_DENYLIST=cloudflare_zone_colocation_.*

Tips: METRICS_DENYLISTの行は、コロケーション/データセンター別のメトリクス収集を除外する設定です。この設定により不要な時系列(time-series)を最大80%削減し、Prometheusのメモリ肥大化を防止できます。

コンテナを起動します:

docker compose up -d

ExporterがCloudflareと正常に通信できているかを確認します:

curl -s http://127.0.0.1:8080/metrics | grep cloudflare_zone_requests_total

ターミナルにcloudflare_zone_requests_total{zone="example.com"} 15420のような行が返され、数値がカウントアップしていれば、Exporterの準備は完了です。

Prometheusのスクレイプ設定

Exporterの安定稼働を確認したら、Prometheusに新しいターゲットを登録して時系列データの保存を開始します。

prometheus.ymlへのスクレイプジョブの追加

Prometheusの設定ファイルを開きます:

sudo nano /etc/prometheus/prometheus.yml

scrape_configsセクションに以下のジョブを追加します:

scrape_configs:
  - job_name: 'cloudflare'
    scrape_interval: 60s
    scrape_timeout: 30s
    static_configs:
      - targets: ['127.0.0.1:8080']
        labels:
          env: 'production'

技術的な注意点: Cloudflare Analytics APIは1分単位のブロックでデータを集計します。scrape_intervalを60秒未満(例:15秒)に設定しても新しいデータは得られず、Cloudflare APIのレート制限(5分あたり1,200リクエスト)に抵触しやすくなるだけなのでご注意ください。

新しい設定を反映します(Prometheusが--web.enable-lifecycleフラグ付きで実行されている必要があります):

curl -X POST http://localhost:9090/-/reload
# lifecycle APIが未有効の場合はsystemdでリロード:
# sudo systemctl reload prometheus

押さえておくべき主要メトリクス一覧

特に注視すべき重要な6つのメトリクスは以下の通りです:

  • cloudflare_zone_requests_total: Cloudflareエッジに到達した総リクエスト数。
  • cloudflare_zone_requests_status: HTTPステータスコード別(200、301、403、502など)のリクエスト数。
  • cloudflare_zone_requests_cached: CDNキャッシュから直接返されたリクエスト数(キャッシュヒット数)。
  • cloudflare_zone_bandwidth_total: 消費された総帯域幅(バイト単位)。
  • cloudflare_zone_threats_total: WAFやIP Firewallによってブロックされた脅威リクエスト数。
  • cloudflare_zone_pool_health_status: Cloudflare Load Balancing利用時のオリジンサーバーの健全性ステータス。

Grafanaによる直感的なダッシュボードの構築

メトリクスがPrometheusに蓄積されるようになったら、システムの稼働状態をリアルタイムに把握するためのグラフを作成していきましょう。

1. 実務で役立つ主要PromQLクエリ

GrafanaのExplore画面を開き、以下の3つのPromQLクエリが正常に動作するか確認します:

キャッシュヒット率(%)— オリジンサーバーの負荷軽減効果を測定:

(
  sum(rate(cloudflare_zone_requests_cached[5m])) 
  / 
  sum(rate(cloudflare_zone_requests_total[5m]))
) * 100

ECサイトやメディアサイトの場合、この値は70%〜85%を維持することが望ましいとされます。もし40%を下回っている場合は、Cache RulesやNginxのCache-Controlヘッダーの設定を見直す必要があります。

バックエンドHTTPエラー発生頻度(502/504):

sum by (status) (rate(cloudflare_zone_requests_status{status=~"5.."}[5m]))

このクエリにアラートルールを設定します。502/504エラーが2分間継続して5 req/sを超えた場合に、TelegramやSlackへ即座に通知を飛ばしてバックエンド担当者が対応できるようにします。

WAFでブロックされた1時間あたりの脅威数:

sum by (zone) (increase(cloudflare_zone_threats_total[1h]))

通常時であれば、このグラフは数十から数百件程度の不正リクエストで推移します。もし1時間で5万〜10万件へと突発的に急増した場合は、脆弱性スキャンツールやレイヤー7(L7)攻撃を受けている可能性が極めて高いと判断できます。

2. 実戦的なダッシュボードレイアウト

実践的なモニタリングレイアウトは、主に4つのパネル領域に分割します:

  • 最上段: Cache Hit Ratio(キャッシュヒット率)とCurrent RPS(毎秒リクエスト数)を表示する2つのゲージパネル。
  • 2段目: Edge Bandwidth vs Origin Bandwidthを比較するグラフ。Cloudflareがどれだけの帯域(GB)を肩代わりしてくれたかが一目で分かります。
  • 3段目: HTTP Status Breakdown(2xx、4xx、5xx系に分類した時系列グラフ)。
  • 最下段: ドメイン別のブロックされたTop WAF Threatsを一覧表示するバーチャート。

ゼロからパネルを配置する手間を省きたい場合は、Grafana公式のテンプレート(ダッシュボードID 13133)をインポートし、Prometheusデータソースを紐付けるだけですぐに利用可能です。

この統合を完了させれば、Cloudflareでのリクエスト急増に伴ってバックエンドのCPU使用率も上昇しているのか、それともWAFがエッジ側で綺麗に遮断してくれているのかを単一の画面で瞬時に突き合わせることができ、障害の切り分けスピードが劇的に向上します。

Share: