1. はじめに
- アラームが鳴ったら DevOps Agentが自動で調査して、その結果を自動で送ってくれるようにしたい。
- 初回記事「AWS DevOps Agent を使ってみる (Getting started with AWS DevOps Agent の実施)」で DevOps Agentの基本機能を確認後、2回目の記事「AWS DevOps Agent を使ってみる (2) : CloudWatchアラームをトリガに調査を自動開始する」で、CloudWatchアラームをトリガに自動調査開始できることを確認した。
- 今回は調査完了後に結果を自動で外部送信する方法を確認する。
2. やったこと
- DevOps Agentの調査が完了すると、EventBridgeに通知が出ることを確認する。
- それを契機にLambdaを起動してDevOps AgentのAPIをたたいて調査結果を取得し、内容をSNSでメール送信する。
3. 構成図
- ごちゃごちゃしてきてしまったが、今回は赤字の⑨~⑫のところを実装する。
4. 先輩の手順
- 調査結果完了後にEventBridge経由でLambdaを起動し、DevOps AgentのListJournalRecords APIをたたいて調査結果を取得し、Claude Haikuで要約や対策立案をさせる実装。
- 自分の検証ではそこまでは不要とした。ListJournalRecords APIで取得できる調査結果を全部取得すると調査プロセスなども含まれて大量になるため、今回はDevOps Agentがまとめた結果概要のところだけを抜き出して外部送信することにした。
- Slackと連携し、結果を出力する仕組みはDevOps Agentの標準機能として実装されているため、EventBridgeやLambdaの設定は不要。ただ自分の現場だとSlackは見ないことがあるためメール通知を検証することとした。
5. 手順
5.1 前提条件
- DevOps Agent のエージェントスペースが作成済であること。
- 特定のインスタンスでCPUが70%以上になるとアラームになるCloudWatchアラームが作成済であること。
- CloudWatchアラーム発生時に自動でDevOps Agentの調査が開始されるように設定済であること。
5.2 EventBridgeの設定
- イベントバス(default)に対して以下のイベントパターン(DevOps Agentの調査完了)を設定し、次項で作成するLambda関数を実行するように指定する。(「調査失敗」(Investigation Failed)など、別のイベントも発生しうるようだが、今回は正常完了時のみを対象にする)
{
"source": ["aws.aidevops"],
"detail-type": ["Investigation Completed"]
}
5.3 SNS Topic/Subscriptionの作成
- Subscriptionに宛先メールアドレスを設定したSNS Topicを作成する。
5.4 Lambda関数の作成
-
調査完了通知をトリガに起動されるLambda関数を作成する。
-
機能
- EventBridge通知から以下の値を取得する(調査結果の取得に必要)。
- agent_space_id (DevOps Agentのスペース名)
- execution_id (調査結果毎に付与されるID)
- agent_space_idとexecution_idを指定して、DevOps Agentの ListJournalRecords API をたたいて、調査結果(JSON)を取得する。
- 調査結果の中の「"recordType": "investigation_summary_md"」の箇所に DevOps Agentがまとめた結果概要があるため、その部分を抜き出してSNSでメール送信する。
- EventBridge通知から以下の値を取得する(調査結果の取得に必要)。
-
備考
- 実行ロールにはSNSとDevOpsAgentの権限追加が必要。
-
具体的なPythonコードは以下(Kiroで自動作成したものであまり精査はできていない)。
Lambdaコード(押すと表示)
send-result-to-sns.py
import json
import os
import boto3
def lambda_handler(event, context):
sns_topic_arn = os.environ["SNS_TOPIC_ARN"]
sns_client = boto3.client("sns")
devops_client = boto3.client("devops-agent")
detail = event.get("detail", {})
metadata = detail.get("metadata", {})
data = detail.get("data", {})
execution_id = metadata.get("execution_id", "")
agent_space_id = metadata.get("agent_space_id", "")
# ListJournalRecords APIでinvestigation_summary_mdを取得
investigation_summary = get_investigation_summary(devops_client, agent_space_id, execution_id)
# CloudWatch Logsにジャーナルレコードを出力
print(f"=== Investigation Summary for execution_id: {execution_id} ===")
print(investigation_summary)
subject = f"[{data.get('status', 'UNKNOWN')}] AI DevOps Task: {data.get('task_type', 'unknown')}"
subject = subject[:100]
message = (
"=== AI DevOps Agent Event ===\n"
f"\n"
f"Detail Type: {event.get('detail-type', '')}\n"
f"Source: {event.get('source', '')}\n"
f"Time: {event.get('time', '')}\n"
f"Region: {event.get('region', '')}\n"
f"Account: {event.get('account', '')}\n"
f"\n"
f"--- Metadata ---\n"
f"Agent Space ID: {agent_space_id}\n"
f"Task ID: {metadata.get('task_id', '')}\n"
f"Execution ID: {execution_id}\n"
f"\n"
f"--- Task Data ---\n"
f"Task Type: {data.get('task_type', '')}\n"
f"Priority: {data.get('priority', '')}\n"
f"Status: {data.get('status', '')}\n"
f"Created At: {data.get('created_at', '')}\n"
f"Updated At: {data.get('updated_at', '')}\n"
f"Summary Record ID: {data.get('summary_record_id', '')}\n"
f"\n"
f"--- Investigation Summary ---\n"
f"{investigation_summary}\n"
)
# SNSメッセージサイズ上限(256KB)対策
if len(message.encode("utf-8")) > 250000:
message = message[:250000] + "\n\n[truncated due to size limit]"
response = sns_client.publish(
TopicArn=sns_topic_arn,
Subject=subject,
Message=message,
)
print(f"SNS publish response: {response['MessageId']}")
return {
"statusCode": 200,
"messageId": response["MessageId"],
}
def get_investigation_summary(devops_client, agent_space_id, execution_id):
"""investigation_summary_md レコードを取得して返す"""
next_token = None
while True:
params = {
"agentSpaceId": agent_space_id,
"executionId": execution_id,
"recordType": "investigation_summary_md",
"order": "DESC",
"limit": 1,
}
if next_token:
params["nextToken"] = next_token
response = devops_client.list_journal_records(**params)
records = response.get("records", [])
if records:
return records[0].get("content", "No content available.")
next_token = response.get("nextToken")
if not next_token:
break
return "No investigation summary found."
5.4 動作確認
-
DevOps Agentの調査完了時、EventBridge経由でLambdaが起動し、調査結果を含むメールが送信されることを確認する。
-
DevOps Agentのオペレーターアクセス画面では、「調査タイムライン」のタブに詳細な調査プロセスが、また、「根本原因」タブのほうに調査結果概要が表示される(今回は私がスクリプト実行したことに気づけていないが、、)。

-
オペレーターアクセス画面で表示される調査結果概要と同じ内容がSNS経由でメール送信されていることを確認する。
メール内容.txt
[COMPLETED] AI DevOps Task: INVESTIGATION
=== AI DevOps Agent Event ===
Detail Type: Investigation Completed
Source: aws.aidevops
Time: 2026-06-07T14:51:51Z
Region: ap-northeast-1
Account: XXXXXXXXXXXXX
--- Metadata ---
Agent Space ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Task ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Execution ID: exe-xxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
--- Task Data ---
Task Type: INVESTIGATION
Priority: MEDIUM
Status: COMPLETED
Created At: 2026-06-07T14:46:35.709Z
Updated At: 2026-06-07T14:51:51.92Z
Summary Record ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
--- Investigation Summary ---
# Investigation Summary
## Symptoms
### EC2インスタンスi-08e631ab0d0265f29のCPU使用率が起動直後に閾値超過
**Description:** EC2インスタンスi-08e631ab0d0265f29(t3.micro)が2026-06-07T14:32:16Zに起動後、約13分後の14:44:00にCPU使用率が76.72%に達し、70%の閾値を超えてアラームが発生。インスタンスは2026-06-02からも同様のCPUスパイクパターンを繰り返している(履歴: 2026-06-02 01:30に99.88%, 06:05に76.76%, 06:55に100%, 07:30に99.99%, 2026-06-06 02:38に94.40%)。
**Time:** 2026-06-07T14:44:00Z
## Findings
### Root Cause: 起動直後のSSM自動管理タスクによるCPU集約的処理
**Description:** インスタンス起動わずか26秒後の2026-06-07T14:32:42Zに、3つのCPU集約的なSSM自動管理タスクが同時実行されました:(1) AWS-RunPatchBaseline(システム全体のパッチスキャン)、(2) AmazonInspector2-ConfigureInspectorSsmPluginLinux(プラグインインストール)、(3) AmazonInspector2-InvokeInspectorSsmPlugin(脆弱性評価実行)。これらのタスクはt3.micro(1vCPU、2GB RAM)の限られたリソース上で同時に実行され、約11-12分後の14:44:00にCPU使用率が76.72%に達してアラームが発生し、その後99.99-100%の完全飽和状態が5分以上継続しました。パッチスキャンとInspector評価は、システム全体をスキャンするディスクI/OとCPU集約的な処理であり、小規模インスタンスでは処理能力を超える負荷となりました。CPUクレジットは十分に残っており(197+クレジット)、バースト制限ではなくアプリケーション層の処理負荷が原因です。
**Cascades to:** cpu-spike-on-instance-startup
#### Supporting Observations
- **起動直後のSSM自動管理タスクによるCPU負荷上昇:** EC2インスタンス i-08e631ab0d0265f29 は2026-06-07T14:32:16に起動し、わずか26秒後の14:32:42に複数のCPU集約的なSSM自動管理タスクが実行されました。これらには、AWS-RunPatchBaseline(パッチスキャン)、AmazonInspector2プラグインのインストールと実行が含まれます。これらのタスクは起動直後の14:44:00のCPUスパイクと時間的に一致しており、CPU使用率上昇の主要因である可能性が高いです。特にパッチベースラインスキャンとInspector評価は、システム全体をスキャンするため、t3.microのような小さなインスタンスタイプではCPUリソースを大量に消費します。
- **EC2インスタンス起動後のCPU使用率急上昇パターン(76.72%→100%):** t3.microインスタンスi-08e631ab0d0265f29は14:32:16起動後、14:33に初期化処理で37%のCPU使用、その後正常レベル(0.1-0.3%)に低下。14:44にアラーム閾値を超える76.72%に急上昇し、14:45-14:47の3分間は99.99-100%の完全飽和状態が継続。起動から12分後の突然の高負荷であり、通常の初期化パターンではなく、アプリケーション層の処理が原因と推定。ベースライン(前日同時刻)にはデータポイントが存在せず、この期間にインスタンスは存在していなかったため、比較不可能。
6. 所感
- 実際にきちんと活用するにはまだ課題があるが、アラーム発報⇒自動調査⇒自動結果送付の流れは確認できた。アラームが鳴ってから5分後くらいに結果が送られてくるので、初動調査時に少しは役に立つかな?と期待できる。実環境的なところに導入して有効性を確認したい。
- 引き続き、datadog, Grafana などのよく使用される外部接続を検証したい。
