モニタリング — Prometheus + Grafana フルスタックオブザーバビリティ
モニタリングは自動車のダッシュボードのようなもの - 速度計 (QPS), 油温 (レイテンシ), エンジン警告灯 (エラー率)が, 車 (システム)の健康状態を常に知らせてくれます。ダッシュボードがなければ, エンジンから煙が出るまで異常に気づかないでしょう。
1. 学ぶ内容
- Prometheus メトリック露出:
prometheus-fastapi-instrumentatorの統合 - カスタムビジネスメトリック:価格更新レート, APIレイテンシパーセンタイル, アクティブユーザー数
- Grafanaダッシュボード設計:リクエスト量/レイテンシ/エラー率 (REDメソッド)+ ビジネスメトリック
- アラートルール:P99レイテンシ閾値超過, エラー率急上昇, Redisキャッシュヒット率低下
- Aliceのシナリオ:CharlieのPriceTrackerモニタリングダッシュボード
2. Aliceの実話
(1) 悩み:問題が起きてから気づく
午前3時, PriceTrackerのデータベースコネクションプールが枯渇し, APIが500エラーを返し始めました。Aliceが気づいたのは午前8時, ユーザーからの苦情が届いてからでした。リアルタイムモニタリングがないため, Charlieは事後にログを確認するしかなく, コネクションプールが午前5時にはすでにアラートを出していたことを発見しました - モニタリングがあれば, 5分以内に検出して解決できたはずです。
(2) PrometheusとGrafanaによる解決策
Prometheusは毎秒メトリック (リクエスト数, レイテンシ, エラー率)を収集し, Grafanaがリアルタイムで可視化し, AlertManagerはメトリックが正常範囲を逸脱した際に自動的にアラートを送信します - 「ユーザーの苦情でしか問題を知る」から「5分以内の自動通知」へとプロセスが変わりました。
(3) 成果
P99レイテンシの急増は1分以内にアラートをトリガーし, データベースコネクションプール枯渇前に自動通知が送信され, 問題検出時間が「数時間」から「数分」に短縮され, Charlieはもう24時間連続で画面を見張る必要がなくなりました。
3. Prometheus メトリックの露出
(1) モニタリングアーキテクチャ
flowchart LR
App[FastAPI App] -->|/metrics| Prom[Prometheus]
Prom -->|Query| Grafana[Grafana Dashboard]
Prom -->|Alert| AlertMgr[AlertManager]
AlertMgr -->|Notify| Slack[Slack / Email]
Grafana -->|Visualize| Charlie[Charlie DevOps]
(1) ▶ サンプル:prometheus-fastapi-instrumentatorの統合
PYTHON
# インストール:uv add prometheus-fastapi-instrumentator
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
# デフォルトメトリックを追加:リクエスト数, 所要時間, サイズ, 例外
Instrumentator().instrument(app).expose(app)
# /metricsエンドポイントがPrometheusメトリックを露出
# 含む:http_requests_total, http_request_duration_secondsなど
出力:
TEXT
# 実行成功
(2) ▶ サンプル:カスタムメトリック
PYTHON
from prometheus_client import Counter, Histogram, Gauge
from fastapi import FastAPI
app = FastAPI()
# カスタムビジネスメトリック
PRICE_UPDATES = Counter(
"pricetracker_price_updates_total",
"Total number of price updates",
["category", "currency"],
)
REQUEST_LATENCY = Histogram(
"pricetracker_request_latency_seconds",
"Request latency in seconds",
buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
)
ACTIVE_USERS = Gauge(
"pricetracker_active_users",
"Number of active users in last 5 minutes",
)
CACHE_HIT_RATE = Gauge(
"pricetracker_cache_hit_rate",
"Redis cache hit rate percentage",
)
@app.post("/api/v1/prices")
async def create_price(price: PriceCreate):
result = await service.create_price(price)
# カスタムメトリックを記録
PRICE_UPDATES.labels(category="electronics", currency="USD").inc()
return result
出力:
TEXT
# 関数定義成功
(2) REDメソッドの主要メトリック
| メトリック | タイプ | 説明 | PromQL |
|---|---|---|---|
| Rate | Counter | リクエストレート (QPS) | rate(http_requests_total[5m]) |
| Errors | Counter | エラー率 | rate(http_requests_total{status=~"5.."}[5m]) |
| Duration | Histogram | レイテンシ分布 | histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) |
4. Grafanaダッシュボード設計
(1) ダッシュボードレイアウト
graph TD
Title[PriceTracker モニタリングダッシュボード]
Title --> Row1[Row 1: 概要]
Row1 --> QPS[QPS - Rate]
Row1 --> P50[P50 レイテンシ]
Row1 --> P99[P99 レイテンシ]
Row1 --> ErrRate[エラー率]
Title --> Row2[Row 2: ビジネス]
Row2 --> PriceUpd[価格更新数/分]
Row2 --> ActiveUsers[アクティブユーザー]
Row2 --> CacheHit[キャッシュヒット率]
Title --> Row3[Row 3: インフラ]
Row3 --> DBConns[DB接続数]
Row3 --> RedisConns[Redis接続数]
Row3 --> CeleryQ[Celeryキューサイズ]
(1) ▶ サンプル:GrafanaダッシュボードJSON設定 (抜粋)
JSON
{
"dashboard": {
"title": "PriceTracker Monitoring",
"panels": [
{
"title": "Request Rate (QPS)",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m]))",
"legendFormat": "Total QPS"
}
]
},
{
"title": "P99 Latency",
"type": "stat",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P99"
}
],
"thresholds": {
"steps": [
{ "value": 0, "color": "green" },
{ "value": 0.1, "color": "yellow" },
{ "value": 0.5, "color": "red" }
]
}
},
{
"title": "Cache Hit Rate",
"type": "gauge",
"targets": [
{
"expr": "pricetracker_cache_hit_rate",
"legendFormat": "Hit Rate %"
}
]
}
]
}
}
出力:
JSON
{
"dashboard": {
"title": "PriceTracker Monitoring",
"panels": [
{
"title": "Request Rate (QPS)",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m]))",
"legendFormat": "Total QPS"
}
]
},
{
"title": "P99 Latency",
"type": "stat",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_buck
5. アラートルール
(1) ▶ サンプル:Prometheusアラートルール
YAML
# prometheus/alert_rules.yml
groups:
- name: pricetracker_alerts
rules:
- alert: HighP99Latency
expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 0.5
for: 2m
labels:
severity: warning
annotations:
summary: "P99レイテンシが500msを超過"
description: "P99レイテンシは{{ $value }}s, 閾値は0.5s"
- alert: HighErrorRate
expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
for: 1m
labels:
severity: critical
annotations:
summary: "エラー率が5%を超過"
description: "エラー率は{{ $value | humanizePercentage }}"
- alert: LowCacheHitRate
expr: pricetracker_cache_hit_rate < 60
for: 5m
labels:
severity: warning
annotations:
summary: "キャッシュヒット率が60%を下回る"
description: "現在のヒット率は{{ $value }}%"
- alert: DatabaseConnectionPoolExhausted
expr: pricetracker_db_connections_in_use / pricetracker_db_connections_max > 0.9
for: 3m
labels:
severity: critical
annotations:
summary: "データベースコネクションプール使用率が90%を超過"
出力:
TEXT
モニタリング設定を読み込みました
Prometheus targets: 3 active
Grafana dashboard: ready
(2) ▶ サンプル:Docker Composeにモニタリングスタックを追加
YAML
# docker-compose.ymlに追加
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./docker/prometheus.yml:/etc/prometheus/prometheus.yml
- ./docker/alert_rules.yml:/etc/prometheus/alert_rules.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--alert.rule-files=/etc/prometheus/alert_rules.yml'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
alertmanager:
image: prom/alertmanager:latest
ports:
- "9093:9093"
volumes:
- ./docker/alertmanager.yml:/etc/alertmanager/alertmanager.yml
出力:
TEXT
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
(3) ▶ サンプル:prometheus.yml設定
YAML
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "pricetracker-api"
metrics_path: "/metrics"
static_configs:
- targets: ["api:8000"]
rule_files:
- "alert_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
出力:
TEXT
設定が反映されました。
❓ よくある質問
Q PrometheusとELKの違いは何ですか?
A Prometheusはメトリックモニタリング (数値時系列)用であり, ELKはログ分析 (テキスト検索)用です。両者は補完関係にあり - Prometheusはトレンドの監視とアラート生成に使用し, ELKは詳細なログの確認による問題のトラブルシューティングに使用します。
Q メトリック収集間隔はどのくらいに設定すべきですか?
A デフォルトの15秒が良いバランスです。短い間隔 (5秒)はよりリアルタイムなデータを提供しますがストレージコストが高くなり, 長い間隔 (60秒)はストレージを節約しますが短い変動を見逃す可能性があります。
Q カスタムメトリックが多すぎるとアプリケーションが遅くなりますか?
A 各メトリックにはわずかなCPUオーバーヘッドがあります。カスタムメトリックは100個以下にすることをお勧めします。ラベルのカーディナリティ (異なる値の数)は, メトリック数よりもパフォーマンスへの影響が大きいため, 高カーディナリティのラベル (
user_idなど)は避けてください。Q Grafanaダッシュボードはどう共有しますか?
A JSONファイルとしてエクスポートし, チームがインポートできます。Grafana.comには様々な既製テンプレートが用意されています。
Q アラートが多すぎる場合 (アラート疲れ)はどうしますか?
A 適切な「for」期間を設定し (一時的な変動によるトリガーを回避), アラートを「warning」または「critical」に分類し, 関連するアラートをグループ化し, 通知チャンネルのノイズを減らしてください。
Q Celeryタスクはどうモニタリングしますか?
A Flowerがタスクレベルのモニタリングを提供し, Prometheusメトリックを露出します。主要メトリックには, タスク成功率, キューサイズ, ワーカーのメモリ使用量などがあります。
📖 まとめ
prometheus-fastapi-instrumentator1行でデフォルトHTTPメトリックを露出- 3種類のカスタムビジネスメトリック:Counter (カウント), Histogram (レイテンシ分布), Gauge (現在値)
- GrafanaダッシュボードはREDメソッドで構成:Rate (QPS), Errors (エラー率), Duration (レイテンシ)
- AlertManagerアラートルール設定:P99 > 500 ms, エラー率 > 5%, キャッシュヒット率 < 60%
- Docker Composeでモニタリングスタックをワンクリックデプロイ:Prometheus + Grafana + AlertManager
📝 練習問題
- 基本問題 (難易度 ⭐):FastAPIに
prometheus-fastapi-instrumentatorを追加し,/metricsエンドポイントがデフォルトHTTPメトリック (http_requests_totalなど)を返すことを確認してください。ヒント:Instrumentator().instrument(app).expose(app) - 応用問題 (難易度 ⭐⭐):3つのカスタムメトリック - PRICE_UPDATES (カテゴリ別Counter), REQUEST_LATENCY (パーセンタイルバケット付きHistogram), CACHE_HIT_RATE (Gauge) - を追加し, エンドポイントでメトリック値を記録してください。ヒント:
Counter(..., ["category"])+.labels(category="electronics").inc() - チャレンジ (難易度 ⭐⭐⭐):Docker ComposeにPrometheus, Grafana, AlertManagerを追加し,
prometheus.ymlを設定してFastAPIメトリックを収集し, 2つのアラートルール (P99 > 500 ms と エラー率 > 5%)を作成し, ダッシュボードをGrafanaにインポートしてリアルタイムメトリックを確認してください。ヒント:docker/prometheus.yml+alert_rules.yml+ Grafanaデータソース設定
---|



