mcpField
M
monitoring-setup

monitoring-setup

github.com/hieutrtrクラウド・DevOps842 回インストール38 週間前に更新
リポジトリ

monitoring-setup について

monitoring-setupは、Python/FastAPIサービスの可観測性設定を案内するスキルです。メトリクス、構造化ログ、トレース、アラート、ヘルスチェック、ダッシュボードを扱い、設定概要をmonitoring-config.mdに記録するよう指定します。

主な機能

  1. 01Prometheusメトリクスの設定
  2. 02structlogによる構造化ログ
  3. 03OpenTelemetryによる分散トレース
  4. 04アラートルールとしきい値の設計
  5. 05ヘルスチェックエンドポイントの指針
  6. 06Grafanaダッシュボードの構成

活用シーン

  1. 01FastAPIサービスの可観測性を計画する
  2. 02APIエンドポイントのREDメトリクスを設計する
  3. 03本番サービスのアラートを設定する
  4. 04監視設定を文書化する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/hieutrtr/ai1-skills --skill monitoring-setup

インストール後、Claude Code で/monitoring-setupを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
alert-rules-template.yml
dashboard-template.json
logging-config-template.py
metrics-config-template.py

詳細

モニタリングの設定

使用するタイミング

以下の場合にこのスキルを有効にします:

  • Python/FastAPI アプリケーションの構造化ログを設定する
  • Prometheus のメトリクス収集とカスタムカウンター/ヒストグラムを設定する
  • ヘルスチェックエンドポイント(ライブネスとレディネス)を実装する
  • 本番サービスのアラートルールとしきい値を設計する
  • サービスモニタリング用の Grafana ダッシュボードを作成する
  • エラートラッキングとパフォーマンスモニタリングのために Sentry を統合する
  • OpenTelemetry で分散トレーシングを実装する
  • 既存のオブザーバビリティのカバレッジを確認または改善する

出力: 設定した内容(メトリクス、アラート、ダッシュボード、ヘルスチェック)を記録したオブザーバビリティ設定の概要を monitoring-config.md に書き込みます。

以下の場合、このスキルは使用しないでください:

  • 本番環境で発生中のインシデントに対応する(incident-response を使用)
  • モニタリングインフラストラクチャをデプロイする(deployment-pipeline を使用)
  • アプリケーションのビジネスロジックを記述する(python-backend-expert を使用)
  • Docker コンテナーを設定する(docker-best-practices を使用)

手順

オブザーバビリティの4つの柱

すべての本番サービスで、4つの柱すべてを実装する必要があります。

┌─────────────────────────────────────────────────────────────────┐
│                    OBSERVABILITY                                │
├────────────────┬───────────────┬──────────────┬────────────────┤
│    METRICS     │   LOGGING     │   TRACING    │   ALERTING     │
│                │               │              │                │
│  Prometheus    │  structlog    │ OpenTelemetry│  Alert rules   │
│  counters,     │  structured   │ distributed  │  thresholds,   │
│  histograms,   │  JSON logs,   │ trace spans, │  notification  │
│  gauges        │  context      │ correlation  │  channels      │
├────────────────┴───────────────┴──────────────┴────────────────┤
│                    DASHBOARDS (Grafana)                         │
│        Visualize metrics, logs, and traces in one place        │
└─────────────────────────────────────────────────────────────────┘
柱1:メトリクス(Prometheus)

リクエスト駆動型サービスにはREDメソッド、リソースにはUSEメソッドを使用します。

REDメソッド(すべてのAPIエンドポイント向け):

  • Rate -- 1秒あたりのリクエスト数
  • Errors -- 1秒あたりの失敗リクエスト数
  • Duration -- リクエストレイテンシの分布

USEメソッド(インフラストラクチャリソース向け):

  • Utilization -- 使用中のリソースの割合(CPU、メモリ、ディスク)
  • Saturation -- キューに入っている、または待機中の作業(コネクションプール、キューの深さ)
  • Errors -- エラーイベント(OOMによる強制終了、接続失敗)

計測する主なメトリクス:

from prometheus_client import Counter, Histogram, Gauge, Info

# RED metrics
REQUEST_COUNT = Counter(
    "http_requests_total",
    "Total HTTP requests",
    labelnames=["method", "endpoint", "status_code"],
)

REQUEST_DURATION = Histogram(
    "http_request_duration_seconds",
    "HTTP request duration in seconds",
    labelnames=["method", "endpoint"],
    buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
)

# USE metrics
DB_POOL_USAGE = Gauge(
    "db_connection_pool_usage",
    "Database connection pool utilization",
    labelnames=["pool_name"],
)

DB_POOL_SIZE = Gauge(
    "db_connection_pool_size",
    "Database connection pool max size",
    labelnames=["pool_name"],
)

REDIS_CONNECTIONS = Gauge(
    "redis_active_connections",
    "Active Redis connections",
)

# Business metrics
ACTIVE_USERS = Gauge(
    "active_users_total",
    "Currently active users",
)

APP_INFO = Info(
    "app",
    "Application metadata",
)

メトリクスを自動収集する FastAPI ミドルウェア:

import time
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

class PrometheusMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        method = request.method
        endpoint = request.url.path
        start_time = time.perf_counter()

        response = await call_next(request)

        duration = time.perf_counter() - start_time
        status_code = str(response.status_code)

        REQUEST_COUNT.labels(
            method=method, endpoint=endpoint, status_code=status_code
        ).inc()

        REQUEST_DURATION.labels(
            method=method, endpoint=endpoint
        ).observe(duration)

        return response

完全な設定については、references/metrics-config-template.py を参照してください。

柱2:ロギング(structlog)

コンテキスト情報を含む構造化JSONログを使用します。本番環境では print() や非構造化ログを使用しないでください。

ロギングの原則:

  1. 構造化 -- 機械で解析可能なJSON形式
  2. コンテキスト付き -- すべてのログにリクエストID、ユーザーID、トレースIDを含めます
  3. 適切なレベル -- 適切なログレベル(DEBUG、INFO、WARNING、ERROR、CRITICAL)を使用します
  4. 実用性 -- WARNING/ERRORログには、調査すべき内容を必ず示します

ログレベルと使用するタイミング:

レベル使用するタイミング例
DEBUG詳細な診断情報。本番環境では無効にしますProcessing item 42 of 100
INFO通常の操作、重要なイベントUser created、Payment processed
WARNING想定外だが処理された状況Retry attempt 2 of 3、Cache miss
ERROR操作が失敗し、対応が必要な場合Database query failed、External API timeout
CRITICALシステムレベルの障害、即時対応が必要な場合Cannot connect to database、Out of memory

structlog の設定:

import structlog

structlog.configure(
    processors=[
        structlog.contextvars.merge_contextvars,
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer(),
    ],
    wrapper_class=structlog.stdlib.BoundLogger,
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    cache_logger_on_first_use=True,
)

リクエストコンテキストの追加:

from starlette.middleware.base import BaseHTTPMiddleware
import structlog
import uuid

class LoggingContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
        structlog.contextvars.clear_contextvars()
        structlog.contextvars.bind_contextvars(
            request_id=request_id,
            method=request.method,
            path=request.url.path,
        )
        response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        return response

完全な設定については、references/logging-config-template.py を参照してください。

柱3:トレーシング(OpenTelemetry)

分散トレーシングによって、サービス境界を越えてログとメトリクスを関連付けます。

FastAPI のトレース設定:

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

def setup_tracing(app, service_name: str = "backend"):
    resource = Resource.create({"service.name": service_name})
    provider = TracerProvider(resource=resource)

    exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
    provider.add_span_processor(BatchSpanProcessor(exporter))

    trace.set_tracer_provider(provider)

    # Auto-instrument FastAPI, SQLAlchemy, Redis
    FastAPIInstrumentor.instrument_app(app)
    SQLAlchemyInstrumentor().instrument()
    RedisInstrumentor().instrument()

ビジネスロジック向けのカスタムスパン:

tracer = trace.get_tracer(__name__)

async def process_order(order_id: str):
    with tracer.start_as_current_span("process_order") as span:
        span.set_attribute("order.id", order_id)

        with tracer.start_as_current_span("validate_order"):
            await validate_order(order_id)

        with tracer.start_as_current_span("charge_payment"):
            result = await charge_payment(order_id)
            span.set_attribute("payment.status", result.status)

        with tracer.start_as_current_span("send_confirmation"):
            await send_confirmation(order_id)
Pillar 4:アラート

アラートは実行可能なものでなければなりません。すべてのアラートで、何が壊れているのか、何をすべきかを示す必要があります。

アラート設計の原則:

  1. ユーザーに影響する問題のみ通知する -- 緊急性のない警告では通知しないでください
  2. SLO に基づいてしきい値を設定する -- 恣意的な数値を使わないでください
  3. アラート疲れを避ける -- アクションにつながらず頻繁に発生するアラートは、修正するか削除してください
  4. ランブックへのリンクを含める -- すべてのアラートに、修復ガイドへのリンクを付けてください
  5. 複数ウィンドウのバーンレートを使用する -- 誤検知を避けながら、問題をより早く検出できます

一般的な FastAPI アプリケーションのアラートしきい値:

アラート条件重大度アクション
エラー率が高いhttp_requests_total{status=~"5.."} が 5 分間、合計の 5% を超えるSEV2ログを確認し、ロールバックを検討する
レイテンシーが高いhttp_request_duration_seconds の p99 が 5 分間、2s を超えるSEV3DB クエリと依存関係を確認する
サービス停止ヘルスチェックが 2 分間失敗するSEV1再起動し、ログを確認してエスカレーションする
DB 接続数が多いプール使用率が 5 分間、80% を超えるSEV3接続リークがないか確認する
DB 接続数が危険水準プール使用率が 2 分間、95% を超えるSEV2アプリを再起動して調査する
メモリ使用量が多いコンテナーのメモリ使用量が 10 分間、85% を超えるSEV3メモリリークがないか確認する
ディスク容量が少ないディスク使用率が 85% を超えるSEV3ログを整理し、ボリュームを拡張する
証明書の有効期限切れが近いSSL 証明書の有効期限が 14 日未満SEV4証明書を更新する

Prometheus のアラートルールについては、references/alert-rules-template.yml を参照してください。

ヘルスチェックエンドポイント

すべてのサービスは、2 つのヘルスエンドポイントを公開する必要があります。

ライブネス(/health): プロセスは実行中ですか?アプリケーションが稼働している場合は 200 を返します。

レディネス(/health/ready): サービスはリクエストを処理できますか?すべての依存関係を確認します。

from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from datetime import datetime, timezone

router = APIRouter(tags=["health"])

@router.get("/health")
async def liveness():
    """Liveness probe -- is the process running?"""
    return {
        "status": "healthy",
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "version": settings.APP_VERSION,
    }

@router.get("/health/ready")
async def readiness(db: AsyncSession = Depends(get_db)):
    """Readiness probe -- can we handle traffic?"""
    checks = {}

    # Check database
    try:
        await db.execute(text("SELECT 1"))
        checks["database"] = {"status": "ok", "latency_ms": 0}
    except Exception as e:
        checks["database"] = {"status": "error", "error": str(e)}

    # Check Redis
    try:
        start = time.perf_counter()
        await redis.ping()
        latency = (time.perf_counter() - start) * 1000
        checks["redis"] = {"status": "ok", "latency_ms": round(latency, 2)}
    except Exception as e:
        checks["redis"] = {"status": "error", "error": str(e)}

    all_ok = all(c["status"] == "ok" for c in checks.values())
    return JSONResponse(
        status_code=200 if all_ok else 503,
        content={
            "status": "ready" if all_ok else "not_ready",
            "checks": checks,
            "timestamp": datetime.now(timezone.utc).isoformat(),
        },
    )
Sentry によるエラー追跡

Sentry は未処理の例外とパフォーマンスデータを取得します。

セットアップ:

import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration

sentry_sdk.init(
    dsn=settings.SENTRY_DSN,
    environment=settings.APP_ENV,
    release=settings.APP_VERSION,
    traces_sample_rate=0.1,  # 10% of requests for performance monitoring
    profiles_sample_rate=0.1,
    integrations=[
        FastApiIntegration(),
        SqlalchemyIntegration(),
    ],
    # Do not send PII
    send_default_pii=False,
    # Filter out health check noise
    before_send=filter_health_checks,
)

def filter_health_checks(event, hint):
    """Do not send health check errors to Sentry."""
    if "request" in event and event["request"].get("url", "").endswith("/health"):
        return None
    return event
ダッシュボードの設計

Grafana ダッシュボードでは、一貫したレイアウトパターンに従ってください。

標準的なダッシュボードのセクション:

  1. 概要の行 -- 主要な SLI を一覧で確認(エラー率、レイテンシー、スループット)
  2. RED メトリクスの行 -- 各エンドポイントのレート、エラー、継続時間
  3. インフラストラクチャの行 -- CPU、メモリ、ディスク、ネットワーク
  4. 依存関係の行 -- データベース、Redis、外部 API の稼働状況
  5. ビジネスメトリクスの行 -- アプリケーション固有の KPI

ダッシュボードのレイアウト:

┌─────────────────────────────────────────────────────────┐
│  Service Overview                                       │
│  [Error Rate %] [p99 Latency] [Requests/s] [Uptime]    │
├────────────────────────┬────────────────────────────────┤
│  Request Rate          │  Error Rate                    │
│  (by endpoint)         │  (by endpoint, status code)    │
├────────────────────────┼────────────────────────────────┤
│  Latency (p50/p95/p99) │  Active Connections            │
│  (by endpoint)         │  (DB pool, Redis)              │
├────────────────────────┴────────────────────────────────┤
│  Infrastructure                                         │
│  [CPU %] [Memory %] [Disk %] [Network IO]              │
├─────────────────────────────────────────────────────────┤
│  Dependencies                                           │
│  [DB Latency] [Redis Latency] [External API Status]    │
└─────────────────────────────────────────────────────────┘

Grafana ダッシュボードの完全なテンプレートについては、references/dashboard-template.json を参照してください。

稼働時間の監視

外部からの稼働時間監視により、ユーザーの視点からサービスを検証できます。

外部から監視する項目:

  • 複数の地理的リージョンからの /health エンドポイント
  • ユーザー向けの主要ページ(ログイン、ダッシュボード、API ドキュメント)
  • SSL 証明書の有効性と有効期限
  • DNS 解決時間

推奨チェック間隔:

チェック間隔タイムアウトリージョン
ヘルスエンドポイント30 秒10 秒3 つ以上のリージョン
主要ページ1 分15 秒2 つ以上のリージョン
SSL 証明書6 時間30 秒1 リージョン
DNS 解決5 分5 秒3 つ以上のリージョン
クイックリファレンス

完全なテンプレートについては、references/ を参照してください:logging-config-template.py、metrics-config-template.py、alert-rules-template.yml、dashboard-template.json。

新しいサービスの監視チェックリスト
  • JSON 出力で structlog を設定済み
  • リクエスト ID の相関に対応したリクエストロギングミドルウェア
  • /metrics で Prometheus メトリクスエンドポイントを公開済み
  • RED メトリクス(リクエスト数、エラー、継続時間)を計測済み
  • ヘルスチェックエンドポイント(/health、/health/ready)を実装済み
  • 環境タグとリリースタグを設定して Sentry SDK を初期化済み
  • エラー率、レイテンシー、可用性のアラートルールを定義済み
  • 標準セクションを含む Grafana ダッシュボードを作成済み
  • 外部の稼働時間監視を設定済み
  • ログの保持ポリシーを定義済み(デフォルト:30 日)
出力ファイル

監視設定の概要を monitoring-config.md に書き込みます:

# Monitoring Configuration: [Service Name]

## Metrics

| Metric | Type | Labels | Purpose |
|--------|------|--------|---------|
| http_requests_total | Counter | method, endpoint, status | RED: Request rate |
| http_request_duration_seconds | Histogram | method, endpoint | RED: Latency |

## Alerts

| Alert | Condition | Severity | Runbook |
|-------|-----------|----------|---------|
| HighErrorRate | error_rate > 5% for 5m | SEV2 | docs/runbooks/high-error-rate.md |

## Health Checks

- `/health` — Liveness probe
- `/health/ready` — Readiness probe (checks DB, Redis)

## Dashboards

- Grafana: Service Overview (imported from references/dashboard-template.json)

## Next Steps

- Run `/deployment-pipeline` to deploy with monitoring enabled
- Run `/incident-response` if alerts fire

コメント