CloudWatch RUMでサンプルアプリケーションをモニタリングしてみた

CloudWatch RUMでサンプルアプリケーションをモニタリングしてみた

Clock Icon2025.07.09

はじめに

こんにちは!最近とても暑いですね、バテ気味です。
クラウド事業本部コンサルティング部のぐっさんです。

RUMの設定方法を確認したかったため、簡単なログイン操作が可能なサンプルアプリケーションを作って動作検証をしてみました。

CloudWatch RUMとは

「リアルユーザーモニタリング」と呼ばれ、アプリケーションにおける実際のユーザー操作に関するデータの可視化・分析を実現するサービスです。
ユーザー体験向上のために活用していきましょう!

公式ドキュメント

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-RUM.html

コスト

このサービスを使用すると「RUM イベント」が発生します。
東京リージョンでは、リージョンにおける10万件のイベントごとにUSD1.00の課金となります。
その他オプションでログ保管やX-Ray等と連携する場合はそれらサービスの料金も追加でかかります。

https://aws.amazon.com/jp/cloudwatch/pricing/

検証用アプリケーション

https://github.com/cm-yu-y/demo-js-app/tree/main

今回Claude Codeに依頼してベースアプリケーションを作成してもらいましたが、ものの10分ほどで最低限のアプリが出来上がりローカルテストまで通してくれる優れものです。
※あくまで検証用のためアプリケーション上のサンプルログイン情報はハードコードされていますのでご留意ください。

ログイン機能を有するフロントエンドのアプリケーションで、色指定などは特にしていませんがいい感じにグラデーションに仕上がっていました!

sample_js_app

ローカル検証環境

項目 環境情報
OS macOS
Node.js v22.1.0
npm 10.7.0
Docker Rancher Desktop 1.18.2

イメージの準備とリソースのデプロイ

検証アプリケーションをECSコンテナで動かしてみます。
VPC・ALB・ECSクラスターをCDKで作成します。ECRとイメージ、タスク定義は手動作成しSSMパラメータストアからタスク定義のARNをCDKより取得する設計です。

注意点としては、コンテナアーキテクチャがX86_64であることに対しローカル(macOS)開発環境のアーキテクチャがARM64 (Apple Silicon)のためそのままデプロイするとエラーになりました。
この場合、イメージビルド時のオプションに--platform linux/amd64を付与することで回避可能です。

CDKのコードは以下のリポジトリに格納しています。
環境パラメータファイルも一応作成しましたが、最小構成ですので検証用で参考までにしていただけたらと思います!

https://github.com/cm-yu-y/aws-ecs-demo-js-app/tree/main

手動で設定した項目

  • ECR
項目 備考
ECRリポジトリ名 demo-js-app プライベートリポジトリ
  • SSM
項目 備考
SSMパラメータ名 /ecs/aws-ecs-demo-js-app/dev/task-definition-arn 種類: String
SSMパラメータ値 ECSのタスク定義ARN
  • ECSタスク定義(json)
{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:AWSアカウントID:task-definition/demo-js-app-task-definition:1",
    "containerDefinitions": [
        {
            "name": "nginx",
            "image": "AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/demo-js-app:latest",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "nginx-80-tcp",
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [],
            "environmentFiles": [],
            "mountPoints": [],
            "volumesFrom": [],
            "ulimits": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/demo-js-app-task-definition",
                    "awslogs-create-group": "true",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "ecs"
                },
                "secretOptions": []
            },
            "systemControls": []
        }
    ],
    "family": "demo-js-app-task-definition",
    "executionRoleArn": "arn:aws:iam::AWSアカウントID:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "revision": 1,
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.ecr-auth"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "ecs.capability.execution-role-ecr-pull"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
        },
        {
            "name": "ecs.capability.task-eni"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "512",
    "memory": "1024",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2025-07-07T08:08:58.626Z",
    "registeredBy": "arn:aws:sts::AWSアカウントID:assumed-role/IAMロール名",
    "enableFaultInjection": false,
    "tags": []
}

ベースアプリケーションのアクセス確認

今回、簡単な検証のみのためドメイン/証明書の作成やCloudFront等は作成せずALBのDNS名でアクセステストを行います。
そのためCDK上ではALBセキュリティグループでインバウンドルールに許可ルールを何も設定しておらず、アクセステスト実施時にマイIPからのアクセスのみを手動許可します。

  • ALBのセキュリティグループでマイIPからのHTTPアクセスのみ許可

ALB_SG

  • アクセス確認 ALBのDNS名をブラウザに入力しアクセス

sample_from_alb_1

  • ログイン確認のため認証情報入力

sample_from_alb_2

  • ログイン成功を確認 ※今回エラー時の挙動も確認したかったためエラー発生用のボタンも用意しています。

sample_from_alb_3

RUMの設定

下準備が整ったところで早速コンソールからCloudWatch RUMを設定していきます!

  • アプリケーションモニターを選択

RUM_1

  • 任意のモニター名を入力し、ドメイン名を追加(今回はALBのDNSパターンで登録) その他はデフォルト

RUM_2

  • 引き続き他の設定もデフォルトで進める

RUM_3

  • 認証オプション(今回は新規でCognitoのIDプールを作成)、ページ指定のオプション(パスによって収集対象のページを選択可能)、およびタグを設定

RUM_4

  • Resource Based Policyを設定しないため確認メッセージが出力されるが検証用途のためそのままContinue

RUM_5

  • モニターを追加すると各種サンプルコードスニペットを取得できる TypeScript版

RUM_6_ts

  • コードスニペット JavaScript版

RUM_7_js

  • コードスニペット HTML版

RUM_8_html

今回は手軽なHTMLへの組み込みを試してみます。
JavaScriptなどアプリケーションコードへ適用の場合は、RUM ウェブクライアントパッケージをnpmでインストールする必要があります。

npmインストールで実施する場合は以下の記事が参考になるかと思います!

https://dev.classmethod.jp/articles/using-cloudwatch-rum-with-nextjs-app/

サンプルアプリケーションのindex.htmlの<head>要素内に、取得した<script>タグを組み込みます。

index.html(例)

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログインアプリ</title>
    <link rel="stylesheet" href="styles.css">
    <script>
        (function(n,i,v,r,s,c,x,z){x=window.AwsRumClient={q:[],n:n,i:i,v:v,r:r,c:c};window[n]=function(c,p){x.q.push({c:c,p:p});};z=document.createElement('script');z.async=true;z.src=s;document.head.insertBefore(z,document.head.getElementsByTagName('script')[0]);})(
          'cwr',
          '<アプリケーションID>',
          '1.0.0',
          'ap-northeast-1',
          'https://client.rum.us-east-1.amazonaws.com/1.19.0/cwr.js',
          {
            sessionSampleRate: 1 ,
            identityPoolId: "ap-northeast-1:<Cognito ID>" ,
            endpoint: "https://dataplane.rum.ap-northeast-1.amazonaws.com" ,
            telemetries: ["performance","errors","http"] ,
            allowCookies: true ,
            enableXRay: false ,
            signing: true // If you have a public resource policy and wish to send unsigned requests please set this to false
          }
        );
    </script>
</head>

組み込んだコードを再度ECRリポジトリにプッシュし、ECSのサービスを更新して新たなイメージでコンテナを起動します。

  • 設定直後のモニターダッシュボード まだ組み込み後のアクセスがないためデータなし

RUM_9

アクセステスト

では実際にアクセスをしてダッシュボードを確認します。

  • パフォーマンスタブ

RUM_12

ロード時間を中心にデータが取れています!体感、割とリアルタイムに反映されました。
「ポジティブ」「許容できる」「イライラする」などのユーザー視点で評価をしてくれるようですね。今回のアクセスはポジティブ判定されました!

RUM_13

RUM_14

ウェブバイタルやページロードのステップデータ等、細かなデータも可視化してくれています。

  • エラーとセッションタブ
    Javascriptエラーに関する情報が表示されます。
    今回はアプリケーション上でエラー発生用のボタンをそれぞれクリックすると画面上では何も起きませんが、内部で各種エラーが発生するように設定しています。
    それぞれボタンを押した結果、エラーがカウントされました。エラーメッセージと共にグラフも提供されて見やすいです。

RUM_16

  • HTTP Requestsタブ
    エラーが最も多いリクエストURL、エラーが最も多いデバイスとブラウザなどのリクエストエラーに関する情報が表示されます。今回のアプリケーションの場合、ログイン情報をわざと間違えて401のステータスコードが返る場合に記録されました。

RUM_17

  • Sessionsタブ

セッション情報に関するデータです。

RUM_18

セッションIDを押下すると、そのセッションで発生したイベント情報などを確認できました。こちらは反映に少し時間がかかりそうです。

RUM_session

  • Eventsタブ

ここで収集されたRUM イベントの一覧を確認出来ます。イベント件数ベースで課金となるため、実装の際には要確認です。
今回のような簡素なアプリケーションで数回のアクセスでも、割と取得イベントが嵩む印象です。

RUM_19

  • ブラウザとデバイスタブ

アクセス元のブラウザの種類やデバイスに関する情報を確認出来ます。

RUM_20

Chromeアクセスのみ行っていましたが、Safariでもアクセスしてみます。

RUM_21

ブラウザの種類が追加されています!

その他、ブラウザに関する比較情報やデバイス情報も確認出来ました。

RUM_22

RUM_23

  • ユーザージャーニータブ

こちらはCookie利用を設定すると利用可能な項目です。
今回特にページを作り込んでいないためだいぶ簡素な結果ですが、ユーザーのパス遷移の動線を辿ることができます。

RUM_24

その他機能

  • X-Ray

オプションのX-Rayを有効化してみます。アクションで、後から追加可能でした。
コードスニペットが更新され、「enableXRay: true」となりました。

アプリケーションコードを再度ECRへプッシュしてECSサービスを更新します。

再度アクセスを行うと、トレース情報が取得できました!

RUM_X_Ray

RUM_X_Ray_t

  • CloudWatch Logsへのログ配信

通常、RUMで収集されたエンドユーザーデータは30日間保持された後で自動的に削除されます。
これよりも長くログを残したい場合は、イベントのコピーをCloudWatch Logsに送信するオプションを有効化することが可能です。

  • コスト調整

サンプリングレートの調整を行い、収集するデータの一部をサンプリングすることで、包括的にデータ量を削減してコストを抑えることができます。
具体的には以下の設定で、パーセンテージを調整します。

RUM_3

最後に

実際に試してみると、割と手軽に実装できる割に細かいダッシュボードが提供されて反映も案外すぐで使いやすそうな機能に感じました。
リクエスト数によっては想定以上の課金となる可能性もあるので、様子を見つつ導入できれば良いのかと思います。

※検証で作成したリソースは忘れず綺麗に削除しましょう!

この記事が少しでも検証のお役に立てれば幸いです。お読みいただきありがとうございました!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.