目次

AWSオブザーバビリティ運用ランブック

周辺資料: 学習インデックス · AWSサービス一覧(2026) · セキュリティ設計ハンドブック · Well-Architectedチェックリスト


メトリクス・ログ・トレースの設計から障害対応手順まで、AWS オブザーバビリティの実践ランブック。


1. オブザーバビリティの3本柱

[Metrics(メトリクス)]
  何が起きているか(数値の変化)
  ツール: CloudWatch Metrics / Embedded Metrics Format
  対象: CPU/メモリ/レイテンシ/エラー率/スループット

[Logs(ログ)]
  なぜ起きているか(イベントの記録)
  ツール: CloudWatch Logs / OpenSearch Service
  対象: アクセスログ / アプリログ / 監査ログ

[Traces(トレース)]
  どこで起きているか(リクエストの経路)
  ツール: AWS X-Ray / CloudWatch ServiceLens
  対象: マイクロサービス間のレイテンシ / エラー伝播

[ビジネス KPI]
  ビジネス影響の把握
  ツール: QuickSight / CloudWatch ダッシュボード(カスタムメトリクス)
  対象: DAU / 購入完了率 / 決済成功率

2. CloudWatch 監視設計

アラーム設計の優先順位

[アラーム優先度の判断軸]
  P1(即座に対応): ユーザーへの影響が継続している
  P2(30分以内に対応): 影響が局所的または潜在的
  P3(翌営業日に対応): 警告・予防的な通知

[優先度 P1 の例]
  - ALB 5XX エラー率 > 1%(5分間継続)
  - API レスポンスタイム P99 > 5秒(3分間継続)
  - RDS 接続数 > 最大接続数の 80%
  - ECS サービスの Running タスク数 = 0

[優先度 P2 の例]
  - Lambda エラー率 > 5%(10分間)
  - SQS DLQ メッセージ数 > 0
  - EC2 CPU 使用率 > 85%(15分間継続)
  - Aurora フェイルオーバー発生

[優先度 P3 の例]
  - S3 バケット容量 > 1TB
  - Lambda コールドスタート率上昇
  - Savings Plans カバレッジ低下

CloudWatch アラーム設定パターン

import boto3

class ObservabilitySetup:
    def __init__(self):
        self.cw = boto3.client('cloudwatch')
        self.sns_arn = 'arn:aws:sns:ap-northeast-1:123456789:alerts'

    def create_api_latency_alarm(self, api_name: str, threshold_ms: float = 2000):
        """API レイテンシアラーム(P99)"""
        self.cw.put_metric_alarm(
            AlarmName=f'{api_name}-latency-p99-high',
            AlarmDescription='API P99 レイテンシが閾値超過',
            MetricName='TargetResponseTime',
            Namespace='AWS/ApplicationELB',
            Statistic='p99',
            Period=300,
            EvaluationPeriods=2,
            Threshold=threshold_ms / 1000,
            ComparisonOperator='GreaterThanThreshold',
            ExtendedStatistic='p99',
            AlarmActions=[self.sns_arn],
            OKActions=[self.sns_arn],
            TreatMissingData='notBreaching'
        )

    def create_error_rate_alarm(self, service_name: str, threshold_pct: float = 1.0):
        """5XX エラー率アラーム(Metric Math)"""
        self.cw.put_metric_alarm(
            AlarmName=f'{service_name}-error-rate-high',
            AlarmDescription=f'5XXエラー率が{threshold_pct}%超',
            Metrics=[
                {
                    'Id': 'errors',
                    'MetricStat': {
                        'Metric': {
                            'Namespace': 'AWS/ApplicationELB',
                            'MetricName': 'HTTPCode_Target_5XX_Count',
                        },
                        'Period': 300,
                        'Stat': 'Sum'
                    }
                },
                {
                    'Id': 'requests',
                    'MetricStat': {
                        'Metric': {
                            'Namespace': 'AWS/ApplicationELB',
                            'MetricName': 'RequestCount',
                        },
                        'Period': 300,
                        'Stat': 'Sum'
                    }
                },
                {
                    'Id': 'error_rate',
                    'Expression': 'errors / requests * 100',
                    'Label': 'ErrorRate'
                }
            ],
            ComparisonOperator': 'GreaterThanThreshold',
            Threshold=threshold_pct,
            EvaluationPeriods=2,
            DatapointsToAlarm=2,
            AlarmActions=[self.sns_arn],
            TreatMissingData='notBreaching'
        )

    def create_composite_alarm(self, service_name: str):
        """複合アラーム(複数条件のAND/OR)"""
        self.cw.put_composite_alarm(
            AlarmName=f'{service_name}-service-degraded',
            AlarmDescription='サービス劣化検知(レイテンシ上昇 AND エラー率上昇)',
            AlarmRule=(
                f'ALARM("{service_name}-latency-p99-high") '
                f'AND ALARM("{service_name}-error-rate-high")'
            ),
            AlarmActions=[self.sns_arn]
        )

    def create_anomaly_detector_alarm(self, metric_name: str, namespace: str):
        """異常検知アラーム(機械学習ベースの動的しきい値)"""
        # まず異常検知モデルを作成
        self.cw.put_anomaly_detector(
            Namespace=namespace,
            MetricName=metric_name,
            Stat='Average',
            Configuration={
                'ExcludedTimeRanges': [],
                'MetricTimezone': 'Asia/Tokyo'
            }
        )

        # 異常検知アラームを設定(バンドの外側でアラーム)
        self.cw.put_metric_alarm(
            AlarmName=f'{metric_name}-anomaly',
            AlarmDescription='異常検知: 通常範囲を逸脱',
            Metrics=[
                {
                    'Id': 'actual',
                    'MetricStat': {
                        'Metric': {'Namespace': namespace, 'MetricName': metric_name},
                        'Period': 300,
                        'Stat': 'Average'
                    }
                },
                {
                    'Id': 'anomaly_band',
                    'Expression': 'ANOMALY_DETECTION_BAND(actual, 2)'
                }
            ],
            ComparisonOperator='GreaterThanUpperThreshold',
            ThresholdMetricId='anomaly_band',
            EvaluationPeriods=3,
            AlarmActions=[self.sns_arn],
            TreatMissingData='notBreaching'
        )

CloudWatch ダッシュボード設計

[ゴールデンシグナルダッシュボード(Google SRE 4指標)]

  行1: サービスの健全性サマリ
    ├── Latency P50/P95/P99(折れ線グラフ)
    ├── Error Rate(面グラフ、赤色)
    ├── Traffic(リクエスト数/分)
    └── Saturation(CPU/メモリ使用率)

  行2: アプリケーション詳細
    ├── Lambda: Duration / Error / Throttle / ConcurrentExecutions
    ├── ECS: CPUUtilization / MemoryUtilization / TaskCount
    └── ALB: ActiveConnectionCount / HealthyHostCount

  行3: データ層
    ├── RDS: CPUUtilization / DatabaseConnections / ReadLatency / WriteLatency
    ├── ElastiCache: CurrConnections / CacheHits / CacheMisses / Evictions
    └── DynamoDB: SuccessfulRequestLatency / ThrottledRequests

  行4: インフラ
    ├── SQS: NumberOfMessagesSent / ApproximateNumberOfMessagesVisible / NumberOfMessagesSentToDLQ
    └── CloudFront: Requests / ErrorRate / OriginLatency

3. CloudWatch Logs 活用

ログ収集設計

[ログの種類と収集先]

  アプリケーションログ:
    ECS/Fargate → awslogs ドライバー → CloudWatch Logs
    Lambda → 自動で /aws/lambda/<function-name> に送信
    EC2 → CloudWatch Logs Agent(CWAgent)でストリーム

  アクセスログ:
    ALB → S3(バケット policy で強制)+ CloudWatch Logs(Firehose経由)
    CloudFront → S3 + Kinesis Firehose → OpenSearch

  監査ログ:
    CloudTrail → S3(全リージョン)+ CloudWatch Logs(アラーム連携)
    VPC Flow Logs → S3 or CloudWatch Logs

[ログフォーマット標準化]
  推奨: JSON 形式(CloudWatch Logs Insights でクエリ可能)
  必須フィールド: timestamp / level / requestId / userId / action / message
  
  例:
  {
    "timestamp": "2026-04-20T10:30:00Z",
    "level": "ERROR",
    "requestId": "abc-123",
    "userId": "user-456",
    "action": "CreateOrder",
    "message": "Payment service timeout",
    "latencyMs": 5002
  }

CloudWatch Logs Insights クエリ集

-- エラー率の時系列(1時間ごと)
fields @timestamp, @message
| filter level = "ERROR"
| stats count() as errorCount by bin(1h)
| sort @timestamp desc

-- レイテンシのパーセンタイル分布
fields @timestamp, latencyMs
| filter ispresent(latencyMs)
| stats
    avg(latencyMs) as avgLatency,
    pct(latencyMs, 50) as p50,
    pct(latencyMs, 95) as p95,
    pct(latencyMs, 99) as p99
  by bin(5m)

-- ユーザーIDごとのエラー数(上位10)
fields userId, @message
| filter level = "ERROR"
| stats count() as errorCount by userId
| sort errorCount desc
| limit 10

-- 特定のアクションに対するレスポンスタイム
fields action, latencyMs
| filter action = "CreateOrder"
| stats avg(latencyMs), max(latencyMs), min(latencyMs), count() by bin(5m)

-- 最近30分のエラーメッセージを集約
fields @timestamp, message
| filter level = "ERROR" and @timestamp > ago(30m)
| stats count() as cnt by message
| sort cnt desc
| limit 20

-- Lambda コールドスタートの検出
fields @timestamp, @message
| filter @message like /Init Duration/
| parse @message "Init Duration: * ms" as initDuration
| stats avg(initDuration), max(initDuration), count() by bin(1h)

メトリクスフィルターの設定例

def setup_metric_filters(log_group_name: str):
    logs = boto3.client('logs')

    # エラー率のカスタムメトリクス
    logs.put_metric_filter(
        logGroupName=log_group_name,
        filterName='ErrorCount',
        filterPattern='{ $.level = "ERROR" }',
        metricTransformations=[{
            'metricName': 'ErrorCount',
            'metricNamespace': 'MyApp/Errors',
            'metricValue': '1',
            'defaultValue': 0,
            'unit': 'Count'
        }]
    )

    # 特定のビジネスイベント(注文完了)をメトリクス化
    logs.put_metric_filter(
        logGroupName=log_group_name,
        filterName='OrderCompleted',
        filterPattern='{ $.action = "OrderCompleted" }',
        metricTransformations=[{
            'metricName': 'OrderCompletedCount',
            'metricNamespace': 'MyApp/Business',
            'metricValue': '1',
            'defaultValue': 0,
            'unit': 'Count'
        }]
    )

    # レイテンシのカスタムメトリクス(Embedded Metrics Format 推奨)
    logs.put_metric_filter(
        logGroupName=log_group_name,
        filterName='RequestLatency',
        filterPattern='[timestamp, level, ..., latencyMs]',
        metricTransformations=[{
            'metricName': 'RequestLatency',
            'metricNamespace': 'MyApp/Performance',
            'metricValue': '$latencyMs',
            'unit': 'Milliseconds'
        }]
    )

4. AWS X-Ray によるトレース

X-Ray 設計パターン

[分散トレースの流れ]

  Client → API Gateway(X-Ray Tracing有効)
               │ TraceID ヘッダーを付与
             Lambda(X-Ray SDK)
               │ サービスマップにセグメント追加
             ECS / EC2(X-Ray デーモン起動)
               │
             DynamoDB / RDS / S3(サブセグメント)

[X-Ray サンプリングルール]
  本番環境:
    固定レート: 5%(コスト削減)
    上限: 5 req/s(最初の5件は必ずサンプル)
    
  開発環境:
    固定レート: 100%(全トレースを収集)
    
  エラー/スロースタート:
    エラー: 常にサンプル(SampledCount > 0 の条件)
    遅延 > 5秒: 常にサンプル

X-Ray SDK の実装例

from aws_xray_sdk.core import xray_recorder, patch_all
from aws_xray_sdk.core.context import Context
import boto3

# AWS SDK のすべての呼び出しを自動計測
patch_all()

@xray_recorder.capture('process_order')
def process_order(order_id: str, user_id: str):
    """X-Ray でトレースするビジネスロジック"""
    
    # カスタムアノテーション(フィルタリング可能)
    xray_recorder.current_subsegment().put_annotation('order_id', order_id)
    xray_recorder.current_subsegment().put_annotation('user_id', user_id)
    
    # カスタムメタデータ(詳細情報、フィルタ不可)
    xray_recorder.current_subsegment().put_metadata('request', {
        'order_id': order_id,
        'timestamp': '2026-04-20T10:30:00Z'
    })
    
    # サブセグメントで処理を分割
    with xray_recorder.in_subsegment('validate_inventory'):
        inventory_check = check_inventory(order_id)
    
    with xray_recorder.in_subsegment('process_payment'):
        payment_result = process_payment(user_id)
    
    return {'order_id': order_id, 'status': 'completed'}

# Lambda ハンドラー
def lambda_handler(event, context):
    xray_recorder.configure(service='order-service')
    
    order_id = event.get('order_id')
    user_id = event.get('user_id')
    
    result = process_order(order_id, user_id)
    return result

CloudWatch ServiceLens の活用

[ServiceLens = X-Ray トレース + CloudWatch メトリクス/ログの統合ビュー]

  サービスマップ:
    → 各サービス間の依存関係と健全性を可視化
    → ノードの色: 緑(正常)/ 黄(警告)/ 赤(エラー)

  トレース分析:
    → エラーになったリクエストのみフィルタ
    → レイテンシ上位 5% のリクエストを特定
    → 特定のユーザーのリクエスト経路を追跡

[実践的なX-Ray クエリ]
  フィルタ式:
    エラーのみ:    error = true
    高レイテンシ:  duration > 5
    特定ユーザー:  annotation.user_id = "user-123"
    特定サービス:  service("order-service") { error = true }

5. SLO(サービスレベル目標)の設計と監視

SLI / SLO / SLA の定義

[SLI(Service Level Indicator): 測定する指標]
  可用性: 成功レスポンス数 / 全リクエスト数 × 100
  レイテンシ: P99 レスポンスタイム < 2秒の割合
  エラー率: 5XX / 全レスポンス × 100

[SLO(Service Level Objective): 内部目標値]
  可用性: 99.9%(月次 43分のダウンタイム許容)
  レイテンシ: P99 < 2秒 を 99% の時間維持
  エラー率: < 0.1% を 99.9% の時間維持

[Error Budget(エラーバジェット)]
  99.9% SLO の場合:
  月間エラーバジェット = (1 - 0.999) × 30日 × 24時間 × 60分 = 43.2分

  バジェット消費率が 50% を超えたら警告
  100% 消費 → 新機能リリース凍結・信頼性改善にリソース集中

SLO モニタリングの実装

def calculate_slo_compliance(service_name: str, period_days: int = 30):
    """SLO 達成率を計算する"""
    cw = boto3.client('cloudwatch')
    
    end_time = datetime.now()
    start_time = end_time - timedelta(days=period_days)
    
    # 成功リクエスト数
    success_response = cw.get_metric_statistics(
        Namespace='AWS/ApplicationELB',
        MetricName='HTTPCode_Target_2XX_Count',
        StartTime=start_time,
        EndTime=end_time,
        Period=period_days * 86400,
        Statistics=['Sum']
    )
    
    # 全リクエスト数
    total_response = cw.get_metric_statistics(
        Namespace='AWS/ApplicationELB',
        MetricName='RequestCount',
        StartTime=start_time,
        EndTime=end_time,
        Period=period_days * 86400,
        Statistics=['Sum']
    )
    
    if total_response['Datapoints'] and success_response['Datapoints']:
        total = total_response['Datapoints'][0]['Sum']
        success = success_response['Datapoints'][0]['Sum']
        slo_pct = success / total * 100
        
        slo_target = 99.9
        error_budget_used = max(0, (slo_target - slo_pct) / (100 - slo_target) * 100)
        
        return {
            'service': service_name,
            'availability': round(slo_pct, 4),
            'slo_target': slo_target,
            'compliant': slo_pct >= slo_target,
            'error_budget_used_pct': round(error_budget_used, 1)
        }

6. 障害対応ランブック

インシデント重大度分類

重大度 定義 対応時間 対応者
P1(Critical) 本番サービス全停止 / 重大データ漏洩 即時(5分以内) オンコール + エスカレ
P2(High) 本番機能の一部停止 / 著しい性能劣化 30分以内 オンコール
P3(Medium) 非重要機能の停止 / 予兆アラート 翌営業日 担当チーム
P4(Low) 警告・最適化機会 1週間以内 担当チーム

障害初動フロー

[Step 1: アラート受信(0〜5分)]
  1. アラートの重大度(P1/P2/P3)を確認
  2. 同一アラートが重複していないかチェック
  3. 他のオンコールへの事前連絡(P1のみ)
  4. インシデント管理ツール(PagerDuty/Opsgenie)でチケット作成

[Step 2: 影響範囲確認(5〜15分)]
  □ CloudWatch ダッシュボードでゴールデンシグナル確認
  □ X-Ray サービスマップでエラー伝播の経路確認
  □ 直近のデプロイ履歴確認(CodePipeline/CodeDeploy)
  □ AWS Health Dashboard で AWS 側障害がないか確認
  □ 影響ユーザー数・影響機能を特定

[Step 3: 初期対応(15〜30分)]
  □ ロールバック可能か判断(直近デプロイが原因なら即ロールバック)
  □ フェイルオーバー可能か判断(AZ / リージョン)
  □ 緊急スケールアウト(EC2 Auto Scaling / ECS タスク数増加)
  □ ステータスページ更新(ユーザーへの通知)

[Step 4: 根本原因分析(30分〜解決)]
  □ CloudWatch Logs Insights でエラーログを分析
  □ X-Ray トレースで高レイテンシ / エラーの経路を特定
  □ RDS Performance Insights でスロークエリを確認
  □ Detective で異常な API 呼び出しパターンを調査(セキュリティ起因の場合)

[Step 5: 復旧確認]
  □ ゴールデンシグナルが正常値に戻ったか確認
  □ エラーバジェットの消費を記録
  □ ステータスページをクリア
  □ ポストモーテム(事後分析)のスケジュールを設定(24時間以内)

よくある障害パターンと診断手順

症状 原因候補 確認コマンド / CloudWatch メトリクス
ALB 5XX 急増 ECS タスク不足 / アプリエラー ECS: RunningTaskCount / CloudWatch Logs: エラーログ
RDS 応答遅延 スロークエリ / 接続数上限 Performance Insights: Top SQL / DatabaseConnections
Lambda タイムアウト 外部API遅延 / メモリ不足 X-Ray: 外部呼び出しのレイテンシ / Duration P99
SQS メッセージ滞留 コンシューマ処理不足 / エラーループ ApproximateNumberOfMessagesVisible / NumberOfMessagesSentToDLQ
EC2 CPU 100% メモリリーク / バースト処理 CloudWatch: CPUUtilization / CWAgent: mem_used_percent
CloudFront 5XX オリジン障害 / WAF ブロック Origin5xxErrorRate / WAF ブロックカウント
DynamoDB スロットリング WCU/RCU 不足 / Hot Partition ConsumedWriteCapacityUnits / ThrottledRequests
ElastiCache 高ミス率 TTL 期限切れ / キャパシティ不足 CacheMisses / Evictions / CurrConnections

7. ポストモーテム(事後分析)テンプレート

## インシデント事後分析

### 概要
- インシデントID: INC-YYYY-NNN
- 発生日時: YYYY-MM-DD HH:MM JST
- 解決日時: YYYY-MM-DD HH:MM JST
- 重大度: P1 / P2 / P3
- 影響ユーザー数: N件
- 影響継続時間: N分

### タイムライン
| 時刻 | 出来事 |
|---|---|
| HH:MM | アラート発火(CloudWatch → PagerDuty) |
| HH:MM | オンコールが対応開始 |
| HH:MM | 原因特定(XXX) |
| HH:MM | 対応策を適用(ロールバック / スケールアウト) |
| HH:MM | サービス復旧確認 |

### 根本原因
(例: DynamoDB の WCU 不足によるスロットリングが連鎖して Lambda タイムアウトが発生)

### 影響
- サービス: 注文APIが5分間応答不能
- ユーザー: 約200件の注文失敗
- 売上影響: 推定 N万円

### 何が良かったか
- X-Ray トレースで根本原因の特定が5分以内にできた
- ロールバック手順が整備されており素早く実行できた

### 何が悪かったか / 改善すべき点
- DynamoDB の Auto Scaling の反応が遅く、スパイクに追いつかなかった
- 最初のアラートから対応開始まで10分かかった(深夜帯)

### 再発防止策
| アクション | 担当 | 期限 |
|---|---|---|
| DynamoDB を On-Demand モードに変更 | バックエンドチーム | 1週間 |
| DLQ アラームを P1 に格上げ | SREチーム | 3日 |
| 深夜帯のエスカレーション手順を整備 | マネージャー | 2週間 |

8. オブザーバビリティ設計チェックリスト

[メトリクス]
□ ゴールデンシグナル(レイテンシ・エラー率・スループット・飽和度)を全サービスで収集
□ ビジネス KPI をカスタムメトリクスで計測
□ 複合アラームで誤検知を削減
□ 異常検知アラームで動的しきい値を使用

[ログ]
□ JSON 形式で構造化ログを出力
□ 全ログに requestId / userId を含める
□ CloudWatch Logs の保持期間を設定(無期限を避ける)
□ 重要なエラーをメトリクスフィルターでカウント

[トレース]
□ X-Ray を全 Lambda / ECS に有効化
□ サンプリングルールを環境ごとに設定(本番 5% / 開発 100%)
□ サービスマップで依存関係を定期確認

[アラート・対応]
□ アラームに重大度(P1/P2/P3)のラベルを付与
□ SNS → PagerDuty/Opsgenie で適切にルーティング
□ SLO と Error Budget を定義・可視化
□ ポストモーテムを全 P1/P2 インシデントで実施

[ダッシュボード]
□ ゴールデンシグナルダッシュボードを全本番サービスに用意
□ オンコール開始時にすぐ開けるブックマークを整備
□ CloudWatch ServiceLens でサービスマップを常時表示