はじめに
2026年9月に、Amazon CloudWatchの新機能として Amazon CloudWatch Omni が発表されました。10月には東京リージョンでも使えるようになっています。
障害が起きたときに AWS DevOps Agent がシグナルを突き合わせて、根本原因と復旧手順を提案してくれるような機能ができないかなと思いまして。運用保守をしている身としては、ここが一番気になるところ。
まずは「Omniを有効化して、自分のアプリが見える状態にする」までを整理しておきたいと思います。この記事はそのセットアップ編です。
この記事でやること
- CloudWatch Omniの概念(ドメイン、Space)を整理する
- Lambda+DynamoDBの小さな構成をCloudFormationでデプロイする
- 東京リージョンでOmniのドメインとSpaceを作る
- Omniにサインインして、アプリケーションマップとメトリクスを確認する
- 自然言語でテレメトリについて質問してみる
この記事でやらないこと
- IAM Identity Centerを使ったSSOでのサインイン(今回はIAMのままディープリンクで入ります)
- AIエージェントの観測・評価機能
- DevOps Agentによる障害調査(次の記事でやろうかなと思ってます)
CloudWatch Omniとは
CloudWatch Omniは、アプリケーションとAIエージェントをまとめて観測するための、AIを活用したオブザーバビリティ環境です。
押さえておきたいポイントは次のとおりです。
- CloudWatchを置き換えるものではない:Omniは、CloudWatchの上に作られたインターフェースとワークフローです。既存のアラームやダッシュボード、APIはそのまま使えます
- データはCloudWatchのまま:OmniはCloudWatchのログ・メトリクス・トレースを読みに行くだけで、既存の計装(CloudWatchエージェント、OTLP、AWS SDKなど)も変更不要です
- AWSコンソールの外で使う:組織専用のURLから使う独立したWebアプリです。IAM Identity Centerと連携すれば、AWSコンソールの権限がないメンバーでもSSOで入れます
- AIによる調査:自然言語でテレメトリに質問できるほか、障害時にはDevOps Agentが原因調査に参加します
ドメインとSpace
Omniには、2段階の入れ物があります。
| 単位 | 役割 |
|---|---|
| ドメイン | 組織の入口。ドメイン名がサインイン用URL(https://<ドメイン名>.cloudwatch-omni.global.app.aws)になる。IdPの接続もドメイン単位で行う |
| Space | チームの作業場所。チームが持つアプリと、それに紐づくテレメトリをまとめる単位。既存のCloudWatchデータを参照するだけで、データの移動は発生しない |
イメージとしては、「データはCloudWatchに置いたまま、チームごとの見え方を切り出したビュー」です。
Application Signalsとの関係
アプリのテレメトリを集める部分は、CloudWatchに以前からある Application Signals が担います。Application Signalsは、OpenTelemetryベースの自動計装で、サービスごとのリクエスト数・エラー・レイテンシを集める機能です。
Lambdaの場合は、AWS Lambda Layer for OpenTelemetryを追加して環境変数を1つ設定するだけで、コードを書かずに有効化できます。Omniは、こうして集まったデータを読んで、アプリケーションマップやサービスの詳細として表示します。
検証構成
なるべくシンプルに、Lambda関数1つとDynamoDBテーブル1つだけの構成にしました。
- リージョン:東京(ap-northeast-1)
- Lambda:
omni-demo-order(Python 3.14)。注文を1件書き込み、すぐに読み戻すだけの処理 - DynamoDB:
omni-demo-orders(オンデマンド)
Lambdaは1回の呼び出しで、DynamoDBにPutItemとGetItemの2回アクセスします。この「2回」が、後でアプリケーションマップを見るときに効いてきます。
なお、今回はAWS Organizations配下のメンバーアカウントで検証しています。
サンプルアプリのデプロイ
CloudFormationテンプレート
Application Signalsを有効化した状態で、LambdaとDynamoDBを作るテンプレートです。
AWSTemplateFormatVersion:"2010-09-09"
Description:>
CloudWatch Omni 検証用のサンプル構成(Lambda + DynamoDB)。
Lambda は Application Signals を有効化した状態でデプロイする。
Parameters:
OtelLayerArn:
Type: String
Description:>
AWS Lambda Layer for OpenTelemetry(Python)の ARN。
最新のバージョンは公式ドキュメントの ADOT Lambda Layer ARNs で確認すること。
Default: arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroPython:29
CreateDiscovery:
Type: String
Description:>
アカウントで Application Signals をまだ有効にしていない場合は true。
既に有効化済みの場合は false にする。
AllowedValues:["true","false"]
Default:"true"
Conditions:
ShouldCreateDiscovery: !Equals [!Ref CreateDiscovery, "true"]
Resources:
# Application Signals がサービスを検出するための権限(サービスリンクロール)を作成する
ApplicationSignalsDiscovery:
Type: AWS::ApplicationSignals::Discovery
Condition: ShouldCreateDiscovery
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: omni-demo-orders
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
-AttributeName: orderId
AttributeType: S
KeySchema:
-AttributeName: orderId
KeyType: HASH
OrderFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version:"2012-10-17"
Statement:
-Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/CloudWatchLambdaApplicationSignalsExecutionRolePolicy
- arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
Policies:
-PolicyName: OrdersTableAccess
PolicyDocument:
Version:"2012-10-17"
Statement:
-Effect: Allow
Action:
- dynamodb:PutItem
- dynamodb:GetItem
Resource: !GetAtt OrdersTable.Arn
OrderFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/omni-demo-order
RetentionInDays:7
OrderFunction:
Type: AWS::Lambda::Function
DependsOn: OrderFunctionLogGroup
Properties:
FunctionName: omni-demo-order
Runtime: python3.14
Handler: index.handler
Role: !GetAtt OrderFunctionRole.Arn
Timeout:10
MemorySize:256
Layers:
- !Ref OtelLayerArn
TracingConfig:
Mode: Active
Environment:
Variables:
TABLE_NAME: !Ref OrdersTable
AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument
OTEL_SERVICE_NAME: omni-demo-order
Code:
ZipFile:|
import json
import os
import uuid
from datetime import datetime, timezone
import boto3
table = boto3.resource("dynamodb").Table(os.environ["TABLE_NAME"])
def handler(event, context):
order_id = str(uuid.uuid4())
item = {
"orderId": order_id,
"item": event.get("item", "coffee"),
"quantity": int(event.get("quantity", 1)),
"createdAt": datetime.now(timezone.utc).isoformat(),
}
# 注文を書き込み、すぐに読み戻す(DynamoDB への依存を2回発生させる)
table.put_item(Item=item)
saved = table.get_item(Key={"orderId": order_id}).get("Item")
print(json.dumps({"message": "order created", "orderId": order_id}))
return {
"statusCode": 200,
"body": json.dumps({"orderId": order_id, "saved": saved is not None}),
}
Outputs:
FunctionName:
Value: !Ref OrderFunction
TableName:
Value: !Ref OrdersTable
Application Signalsを有効化するために必要なのは、次の4点です。
| 設定 | テンプレートでの対応箇所 |
|---|---|
| サービス検出の有効化(アカウントで1回だけ) | AWS::ApplicationSignals::Discovery |
| 実行ロールへのマネージドポリシー付与 | CloudWatchLambdaApplicationSignalsExecutionRolePolicy |
| AWS Lambda Layer for OpenTelemetryの追加 | Layers |
| 自動計装を有効にする環境変数 | AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument |
あわせて、Lambdaのアクティブトレースも有効にしています。また、OTEL_SERVICE_NAMEで、Omni上に表示されるサービス名を指定しています。
レイヤーのARNはリージョンごとに異なります。
最新のARNは、公式の「ADOT Lambda Layer ARNs」で確認してください。執筆時点(2026年10月)の東京リージョンのPython用は、バージョン29でした。Python 3.14のランタイムでも問題なく動作しました。
アカウントでApplication Signalsを既に有効化している場合は、パラメータCreateDiscoveryをfalseにしてください。
スタックの作成
CloudFormationコンソールで、東京リージョンにスタックを作成します。
- 「スタックの作成」のプルダウンから、**「新しいリソースを使用(標準)」**を選ぶ
- テンプレートファイルとして
omni-demo-template.yamlをアップロードする - スタック名(例:
omni-demo)を入力する - 「スタックオプションの設定」は、次の2点を確認する
- 「権限」のIAMロールは空欄のままにする(作業ユーザー自身の権限で作成される)
- デプロイモードは標準を選ぶ
- IAMリソースの作成を承認して、スタックを作成する
「既存のリソースを使用(リソースをインポート)」を選ぶと、各リソースの識別子の入力を求められます。今回はリソースを新しく作るので、「標準」を選んでください。
ドメインとSpaceの作成
CloudWatchコンソールのOmniの画面から、ドメインとSpaceを作成します。
ドメインの作成
「開始方法」から進むと、ドメインの設定画面になります。
ドメインタイプは、組織レベルとアカウントレベルの2種類があります。
- 組織レベルドメイン:Organizationsの管理アカウントから作成し、組織全体で使う
- アカウントレベルドメイン:単一のアカウントに紐づき、そのアカウントでSpaceを管理する
今回はOrganizations配下のメンバーアカウントで操作したため、組織レベルドメインはグレーアウトしていて、自動的にアカウントレベルドメインが選ばれていました。
ドメイン名は、次のルールを満たす必要があります。
- 3〜63文字の小文字の英字・数字・ハイフン
- 先頭と末尾は英字か数字。ハイフンの連続は不可
-
aws-、amazon-、cloudwatch-、omni-で始まる名前や、AWSのサービス名・リージョン名に似た名前は使えない - すべてのCloudWatchドメインの中で一意
今回は「IAM アイデンティティセンターでのサインインを有効化」をオフのまま進めます。
IAM Identity Centerでサインインさせる場合は、Identity Centerのインスタンスとドメインが同じリージョンにある必要があります。リージョンが異なる場合は、Identity Centerのマルチリージョンレプリケーションが必要です。
Spaceの作成
ドメインの作成に続けて、同じ流れでSpaceを作成します。Space名(今回はomni-demo)を入力し、アカウントとリージョンが東京になっていることを確認します。
「デフォルト設定を変更」を開くと、Spaceで有効になる機能を確認できます。すべて推奨値が入っているので、そのままで問題ありません。
| 機能 | 内容 |
|---|---|
| データセット統合 | ログとトレースをOmniで横断検索できるように転送する。ロググループなど既存の設定は変わらない |
| OTelスパンの取り込み | トランザクション検索を有効にし、トレースをサンプリングせずに全件記録する |
| AWSメトリクスのOTelエンリッチメント | AWSの標準メトリクスに「どのリソースのものか」という属性を付けて、PromQL/SQLで検索できるようにする |
| アラートによるテレメトリの読み取りと通知の送信 | アラートとOmniエージェントが、スペースのデフォルトのアクセスプロファイルでテレメトリを読めるようにする |
| AWS Configのサービスリンクされたレコーダー | サービスの自動検出に使う |
今回の環境では、OTelスパンの取り込みとOTelエンリッチメントは、既に有効化済みでした。
初回のSpace作成時は、既存のログとトレースを最大7日分さかのぼって取り込みます。
セットアップが完了すると、ドメインとSpaceの情報が表示されます。
テレメトリを流す
CloudShellから、Lambdaを100回呼び出します。
#!/usr/bin/env bash
# omni-demo-order を繰り返し呼び出して、テレメトリを発生させるスクリプト
# 使い方: ./invoke.sh [回数] (省略時は 100 回)
set -euo pipefail
COUNT="${1:-100}"
FUNCTION_NAME="omni-demo-order"
REGION="ap-northeast-1"
ITEMS=("coffee" "tea" "sandwich" "cake")
for i in $(seq 1 "$COUNT"); do
ITEM="${ITEMS[$((RANDOM % ${#ITEMS[@]}))]}"
QTY=$((RANDOM % 5 + 1))
aws lambda invoke \
--region "$REGION" \
--function-name "$FUNCTION_NAME" \
--cli-binary-format raw-in-base64-out \
--payload "{\"item\":\"${ITEM}\",\"quantity\":${QTY}}" \
/dev/null > /dev/null
echo "[$i/$COUNT] invoked (${ITEM} x${QTY})"
sleep 1
done
chmod +x invoke.sh
./invoke.sh
1秒間隔なので、2分ほどで終わります。テレメトリが届くまで、数分待ちます。
Omniにサインインして確認する
サインイン
セットアップ完了画面の「サインイン」から、Omniを開きます。IdPを接続していないので、CloudWatchコンソールにログインしているIAMの権限のまま、ディープリンクで入れます。
初回はウェルカム画面が表示されるので、閉じて進みます。
ホーム画面では、Spaceに含まれるサービス数とエラーの有無が表示されます。サービスが1つ検出され、エラーなしになっています。
アプリケーションマップ
「Application map」を開いて、検索窓でomni-demo-orderを選ぶと、依存関係が描かれます。
マップには、3つのノードが表示されました。
| ノード | 種類 | 数字 | 意味 |
|---|---|---|---|
| omni-demo-order | Service | 100 | Application Signalsが検出した論理的なサービス |
| omni-demo-orders | Resource | 200 | DynamoDBテーブル。サービスからの呼び出し(実線:Calls) |
| omni-demo-order | Resource | 100 | Lambda関数そのもの。サービスが動いている場所(点線:Runs on) |
注目したいのは数字です。サービスの100は、invoke.shで呼び出した回数と一致しています。DynamoDBの200は、1回の処理でPutItemとGetItemの2回アクセスしているため、100回×2回でちょうど200になっています。
Omniは、「アプリの論理的な単位(サービス)」と「それが動いているインフラ(リソース)」を分けて描いていることが分かります。
パンくずリストに「(none) · Standalone Services」と表示されています。サービスがどのアプリケーションにもまとめられていないため、独立したサービスとして扱われているという意味です。ホーム画面でアプリケーション数が0と表示されていたのは、このためです。
サービスのメトリクス
マップ上でomni-demo-order(Service)を選び、「View details」を開くと、サービスの詳細が表示されます。
- Requests:invoke.shを流した時間帯に、リクエストが立ち上がっている
- Request latency(P99):最初に150ms前後まで上がり、その後下がっている。最初の呼び出しはコールドスタートの影響と考えられる
- Errors:0のまま
- Traces:エラーなし。平均59ms
トレースの件数は95件で、呼び出し回数とほぼ同じ数が記録されていました。
自然言語で質問してみる
画面下部の入力欄から、日本語で質問してみました。
omni-demo-orderはどのリソースに依存していますか?
日本語で回答が返ってきました。依存先としてLambda関数とDynamoDBテーブルの2つを挙げ、サービス自体が100リクエスト・エラーなしで正常であることも答えています。
一方で、気になる点もありました。
- Lambda関数(Runs onのリソース)を「別インスタンス」として、依存先と同じように扱っている
- 依存先のステータスを「不明(データなし)」とし、インフラのノードが「検出されていない」とも書いている。マップには表示されているので、説明が少し矛盾している
依存関係そのものは正しく答えていますが、マップの細かい意味づけまでは正確ではない、という印象です。次の記事では、DevOps Agentの障害調査がどこまで正確かを検証していきます。
料金
Omniの料金は、次の3つの要素で決まります。
- テレメトリの取り込み:CloudWatchの通常料金
- データの保存:CloudWatchの通常料金
- Omniでの分析:PromQLのクエリなど、Omniの機能の利用に応じた料金
あわせて、次の点にも注意が必要です。
- Omniを有効化すると、30日間有効な1,000ドル分のクレジットが付与される
- OTelスパンの取り込み(トランザクション検索)とOTelエンリッチメントは、それぞれの通常料金がかかる
- DevOps Agentは別料金
最新の料金は、公式の料金ページで確認してください。
後片付け
検証が終わったら、次の順でリソースを削除します。
- CloudFormationのスタック(
omni-demo)を削除する - CloudWatchコンソールのOmniの画面で、Spaceとドメインを削除する(各カードの「アクション」から操作)
まとめ
- CloudWatch Omniは、CloudWatchのデータをそのまま使う、新しい見方と調べ方の入口
- Organizations配下のメンバーアカウントでは、アカウントレベルドメインで手軽に始められる
- LambdaはApplication Signalsを有効にするだけで、サービスと依存先がアプリケーションマップに自動で描かれる
- 自然言語での質問は、依存関係の把握には十分使えるが、細部の説明はもう少し検証が必要
次の記事では、この環境に障害を意図的に仕込み、DevOps Agentが原因と復旧手順をどこまで正しく出せるかを検証します。







