コンテキストのない監視はノイズでしかない

開発技術部の柴田です。 先日、AWS Summit New York Cityのキーノートをライブストリーミングで視聴している中で、聞いた一言が、凄く良いなと思ったので、その紹介とそこから少し考えたことを紹介します。

www.youtube.com

AWS Summit New York Cityで刺さった一言

2026年6月のAWS Summit New York Cityのキーノート内で、セキュリティ・オブザーバビリティ部門のVPであるChet Kapoorがこんなことを言っていました。

"Telemetry without context is noise, but telemetry with context is signal, and when you have signal and you can reason through it, you have actionable insights."

テレメトリはコンテキストがなければノイズだが、コンテキストがあればシグナルになる。シグナルを推論できれば、行動につながるインサイトが得られる、と。

この発言の背景を少し説明しますと、Summitの基調講演のテーマは "compounding momentum"(複利的に加速する勢い)でした。AIエージェントが正しく構築されると、使えば使うほど良くなる。インタラクションが増えればコンテキストが溜まり、コンテキストが溜まればアウトプットが良くなり、信頼が積み上がり、さらに多くのことを任せるようになる。この好循環を加速させるために何をしているか、という話がKeynote全体の骨格でした。

Chetの発言はSecurityパートの冒頭で出てきたもので、直後にAWS Continuumというセキュリティ製品群を紹介しています。脆弱性のテレメトリ(検出結果)に対して、インフラ構成・権限・ネットワークトポロジ・コードといったコンテキストを付与することで、優先順位付けや自動修復を可能にする、という文脈でした。

元の文脈は「AIエージェントに渡すコンテキスト」の話なのですが、この言葉を聞いたときに思ったのは「人間に対しても全く同じだ」でした。 コンテキストがなければ人間にとってもノイズでしかなく、AIだろうが人間だろうが、テレメトリの受け手がそこから行動を起こせなければ意味がありません。

「とりあえず監視」というアンチパターン

入門監視』(Mike Julian著)に、メトリクスを監視することとサービスを監視することは違う、という考え方が出てきます。CPU使用率が80%を超えたからといって、それがサービスの異常を意味するとは限らない。CPUが高くてもレスポンスが正常なら、ユーザーから見たサービスは健全です。逆にCPUが低くても、デッドロックでリクエストが詰まっていればサービスは壊れてます。

しかし、実際には監視を追加するときには「まずCPUとメモリのアラームを入れよう」となりがちです。それ自体は悪くないのですが、そこで「よし、監視できている」と満足してしまう。『入門監視』ではこれを「チェックボックス監視」と呼んでいて、「監視しているか → はい」で思考が止まるアンチパターンとしています。

このアンチパターンは意外とよく発生します。CloudWatchアラームの数は増えていく。Slackの通知チャンネルに毎日何かしら流れてくる。でも誰もアクションしない。通知を見た人が、自分は何をすればいいのか分からないからです。分からないから無視する。無視が常態化すると、本当に重要なアラートが来ても埋もれる。

表面上は「監視してます」と言っているのに、実際には監視が機能していない。これはまさに "Telemetry without context is noise." そのものだなと感じました。

テレメトリにコンテキストがあるとはどういう状態か

そもそも「テレメトリにコンテキストがある」とはどういう状態なのか。

テレメトリとは、システムから収集されるメトリクス、ログ、トレースといったデータの総称です。これ単体は事実の断片にすぎなくて、「CPU使用率が82%」「HTTPステータス503が返った」「レスポンスタイムが1200ms」と言われても、それだけでは何も判断できません。

コンテキストとは、その断片に「システム全体の中での位置づけと関係性」を与えることだと思っています。

  • このメトリクスが異常を示したとき、影響を受けるのはどのサービスのどの部分なのか: ECSタスクのCPUが高いのと、RDSのCPUが高いのでは影響範囲がまるで違う
  • このコンポーネントは他のどのコンポーネントと依存関係があるか: あるマイクロサービスが落ちたとき、それに依存している下流のサービスはどれか。連鎖的に何が壊れるのか
  • この異常はユーザー影響にどういう経路で到達するか: DBのスロークエリが増えた→APIのレスポンスタイムが上がった→ユーザーのリクエストがタイムアウトする、という因果の経路が見えているか

Chetの元の発言でも、コンテキストとして挙がっていたのはインフラ構成、権限、ネットワークトポロジ、コードでした。つまりシステムの構造と関係性です。数値そのものではなく、「その数値がシステム全体の中で何を意味しているか」が分かる状態にすることが、コンテキストを与えるということなんだと思います。

私たちがテレメトリを利用する場面の大部分は監視です。この流れの中で「コンテキストが抜けている」と何が起きるのかを、もう少し具体的に見ていきます。

監視でコンテキストが抜けるとどうなるか

影響範囲が分からない

ECSタスクのCPU使用率が90%に達してアラームが鳴ったとします。でもそのタスクがシステム全体のどこに位置しているか分からなければ、これが「ユーザーに見えているAPIサービスの問題」なのか「バックグラウンドの非同期バッチ処理で、ユーザーには影響しない」のか判断できません。

結果として全部のアラームが同じ緊急度で通知されることになります。深夜にバッチ処理のCPUアラームで叩き起こされて、調べてみたらユーザー影響ゼロだった、みたいなことが起きます。どのコンポーネントがユーザー影響に直結していて、どれが間接的なのか。この位置づけがアラームに紐づいていないのは、コンテキストがない状態です。

関係性が見えない

ALBの5xxエラーが増加している。同時にECSタスクのUnHealthyHostCount(ヘルスチェック失敗)も増加している。一見すると、ECSタスクの不調に見えるので、ECSから調べ始める。

ところが実際には、NAT Gatewayのポート枯渇が起きていたとします。ECSタスクから外部APIへの通信が詰まってアプリケーションのスレッドが枯渇し、タスクがヘルスチェックにすら応答できなくなった結果、ルーティング先を失ったALBが5xxエラーを返していた、という1本の因果関係かもしれません。

もしNAT Gatewayのポート枯渇アラームが個別に鳴っていたとしても、インフラとアプリでレイヤーが離れているため、初見で結びつけるのは難しいです。「このECSタスクはNAT経由で外部通信しており、ヘルスチェックでは外部APIとの疎通も確認している」という構造が見えていて初めて、NAT Gatewayのポート枯渇が原因と気づくことができます。

CloudWatchで「コンテキストのある監視」を作る

では、CloudWatchで具体的にどう実現するかの例を紹介します。

Composite Alarmsで相関を表現する

前のセクションで挙げた「相関が見えない」問題を解決するのがComposite Alarmです。複数のメトリクスアラームを組み合わせて、「状況」として1つの通知にまとめることができます。

たとえば「ALBの5xx増加」と「ECSのUnHealthyHostCount増加」が同時に起きている場合、個別に通知が2つ来るよりも「サービスがダウンしている可能性がある」という1つの通知として届く方が、受け手にとってのコンテキストは明確になります。

CloudFormationで書くとこうなります。

Resources:
  ALB5xxAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: api-alb-5xx-high
      MetricName: HTTPCode_ELB_5XX_Count
      Namespace: AWS/ApplicationELB
      Dimensions:
        - Name: LoadBalancer
          Value: !Ref ALBFullName
      Statistic: Sum
      Period: 60
      EvaluationPeriods: 3
      Threshold: 10
      ComparisonOperator: GreaterThanThreshold
      TreatMissingData: notBreaching

  UnHealthyHostAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: api-unhealthy-hosts
      MetricName: UnHealthyHostCount
      Namespace: AWS/ApplicationELB
      Dimensions:
        - Name: TargetGroup
          Value: !Ref TargetGroupFullName
        - Name: LoadBalancer
          Value: !Ref ALBFullName
      Statistic: Maximum
      Period: 60
      EvaluationPeriods: 2
      Threshold: 0
      ComparisonOperator: GreaterThanThreshold
      TreatMissingData: notBreaching

  # Composite Alarm: 両方ALARMのとき「サービスダウンの可能性」として通知
  ServiceDownAlarm:
    Type: AWS::CloudWatch::CompositeAlarm
    Properties:
      AlarmName: api-service-down
      AlarmRule: "ALARM(api-alb-5xx-high) AND ALARM(api-unhealthy-hosts)"
      AlarmDescription: |
        ALBの5xxエラーとUnHealthyHostが同時に発生しています。
        サービスがダウンしている可能性があります。
        Runbook: https://wiki.example.com/runbooks/api-service-down
      AlarmActions:
        - !Ref OnCallSNSTopic

ポイントはAlarmDescriptionにRunbookのURLを含めていることです。CloudWatch → SNS → AWS Chatbot → Slackで通知すると、このDescriptionがSlackメッセージに表示されます。アラートを受けた人が「次に何を見ればいいか」をすぐに分かる状態にできます。

Anomaly Detectionでベースラインを自動学習する

「閾値をいくつにすればいいか分からない」というメトリクスには、CloudWatch Anomaly Detectionが使えます。過去のパターンから自動的にベースラインを学習して、そこから外れたときにアラームを発報させます。

たとえばAPIのレスポンスタイムは曜日や時間帯によって変動するので、固定の閾値だと「月曜朝は毎回鳴る」みたいなことになりがちです。Anomaly Detectionはそのパターンを学習してくれるので、「いつもと違う」ときだけ鳴らすことができます。

Resources:
  ResponseTimeAnomalyDetector:
    Type: AWS::CloudWatch::AnomalyDetector
    Properties:
      Namespace: AWS/ApplicationELB
      MetricName: TargetResponseTime
      Dimensions:
        - Name: LoadBalancer
          Value: !Ref ALBFullName
      Stat: Average

  ResponseTimeAnomalyAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: api-response-time-anomaly
      AlarmDescription: |
        レスポンスタイムが通常のパターンから逸脱しています。
        Runbook: https://wiki.example.com/runbooks/response-time-anomaly
      ComparisonOperator: GreaterThanUpperThreshold
      EvaluationPeriods: 3
      Metrics:
        - Id: responseTime
          MetricStat:
            Metric:
              Namespace: AWS/ApplicationELB
              MetricName: TargetResponseTime
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref ALBFullName
            Period: 300
            Stat: Average
        - Id: anomalyBand
          Expression: "ANOMALY_DETECTION_BAND(responseTime, 2)"
      ThresholdMetricId: anomalyBand
      TreatMissingData: notBreaching
      AlarmActions:
        - !Ref OnCallSNSTopic

ANOMALY_DETECTION_BANDの第2引数(ここでは2)は標準偏差の倍数です。大きくすれば感度が下がり(鳴りにくくなり)、小さくすれば感度が上がります。最初は2で始めて、誤検知が多ければ3に上げる、という調整をしていくのが良いと思います。

メトリクスフィルタでサービスレベルの指標を作る

CloudWatch Logsに出力されるアプリケーションログから、カスタムメトリクスを作ることができます。たとえばアプリケーションのログから、特定のAPIのエラー率をメトリクス化する。

Resources:
  Api5xxMetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: /ecs/api-service
      FilterPattern: '{ $.status_code = 5* }'
      MetricTransformations:
        - MetricName: Api5xxCount
          MetricNamespace: Custom/ApiService
          MetricValue: "1"
          DefaultValue: 0

ALBのHTTPCode_ELB_5XX_Countは「ALBが5xxを返した回数」ですが、アプリケーションログから取ったメトリクスは「どのエンドポイントでエラーが起きているか」まで追えます。ディメンションにAPIパスを含めれば、「決済APIだけエラー率が上がっている」といったサービスレベルの判断ができるようになります。

ただし、高カーディナリティのフィールド(ユーザーIDやリクエストIDなど)をディメンションにすると、カスタムメトリクスの数が爆発してコストが跳ね上がるので注意してください。ディメンションに使うのは、エンドポイントのパスやHTTPメソッドなど、取りうる値が有限のフィールドに限定するのがポイントです。

コンテキストを設計するのは結局人間の仕事

ここまで書いてきたのは、人間がテレメトリを受け取って判断するための設計です。でもこの設計は、AIエージェントが自動で調査する時代にもそのまま効いてきます。コンテキストを考えて監視に埋め込むのは、どちらにしても人間の仕事だからです。

AWS DevOps Agentは、CloudWatchアラームをトリガーにしてインシデント調査を自動で開始します。アラームが発報するとWebhook経由でAgentに渡り、Agentはリソースの依存関係を辿りながら根本原因を特定し、対応策を提示します。

このときAgentが受け取る入力は、私たちが設計したアラームそのものです。AlarmDescriptionに書いた内容、Composite Alarmで表現した相関関係、リソースタグで整理したサービスの境界。これらはすべてDevOps Agentの推論の材料になります。

もちろんDevOps Agentは自身でもTopologyを構築します。AWSアカウントをスキャンしてリソースを検出し、CloudFormationスタックやリソースタグから関係性を分析し、CloudTrailのAPI履歴を追って変更を特定する。人間が全部お膳立てしなくてもある程度は自力で辿れます。

ただ、人間のためにコンテキストを整理しておくことは、Agentの推論の精度と速度を上げます。Composite Alarmで「この3つのアラームは同じ障害の側面だ」と表現しておけば、Agentも最初から相関を認識した状態で調査を始められます。Descriptionに影響範囲やRunbookが書いてあれば、Agentはそこから推論のヒントを得られます。

人間のために設計したコンテキストが、そのままAIエージェント時代の基盤にもなる。Summitの話に戻れば、これもまた "compounding momentum" の一つの形なのだと思います。

まとめ

"Telemetry without context is noise."

監視を追加すること自体は簡単です。CloudWatchアラームを1つ作るのに5分もかかりません。でもそのアラームにコンテキストがなければ、それはノイズを1つ増やしただけです。

監視を追加する前に問いかけるべきことは、結局こういうことだと思っています。

  • このメトリクスが異常を示したとき、影響を受けるサービスはどれか分かるか?
  • 他のコンポーネントとの依存関係は表現できているか?
  • ユーザー影響への因果経路が見えるようになっているか?

「とりあえず監視しておけば安心」は、コンテキストのない安心です。監視は追加した瞬間ではなく、それがシグナルとして機能したときに初めて意味が出てくるものだと思っています。

参考