📈

AWS DVAのサンプル問題(#8)の正答にならってCloudWatchLogsを触ってみた

2021/10/08に公開

やったこと

AWS ディベロッパーアソシエイトのサンプル問題の正答にならってCloudWatchLogsを触ってみました。
参考としたのは下記のサンプル問題の(8)EC2インスタンスに関する監視の問題です。

参考:
https://d1.awsstatic.com/ja_JP/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Sample-Questions.pdf

EC2インスタンス上で動作するアプリケーションのパフォーマンス(リクエストの処理時間)を監視して、閾値を上回った場合に通知を送りたい、という内容です。
今回は正答の手順通りにアプリケーションと監視の仕組みの構築を通して、CloudWatchによる監視の構築に関する一連の操作をまとめました。

1. 応答時間をログファイルに書き込むよう、アプリケーションを構成する。

下記のようなサンプルアプリケーション(API)をEC2上に構築しました。

  • Node.js + Express.js + TypeScriptのサンプルアプリケーション
  • log4jsを使用してログファイルにログ出力する

config.ts (ログ出力に関する設定)

import log4js from "log4js";

log4js.configure({
  appenders: { app: { type: "file", filename: "./app.log" } },
  categories: { default: { appenders: ["app"], level: "info" } },
});
export const logger = log4js.getLogger("app");

export const TEST_ENV = process.env.TEST_ENV;

index.ts(サンプルAPI)

/**
 * クエリパラメータで指定した時間(ミリ秒)だけ待機するAPI。処理に要した時間(ミリ秒)をログ出力する 。
**/
  app.get(“/time”,  (req: Request, res: Response) => {
      const startDateTime = new Date().getTime();
      sleep(Number(req.query.mseq));  // 指定の時間(ミリ秒)だけ待機する関数。
      const time = `TIME: ${new Date().getTime() - startDateTime}`
      res.send(time); 
      logger.info(time); // ログ出力
});

クエリパラメータ(mseq)に指定した秒数(ミリ秒)だけ待機してログに出力します。
下記のような動作となります。

[ec2-user@xxxx log]$ curl -w "\n" localhost:3000/time -d mseq=3000 -G
TIME: 3000
[ec2-user@xxxx log]$ curl -w "\n" localhost:3000/time -d mseq=2000 -G
TIME: 2002
[ec2-user@xxxx log]$ curl -w "\n" localhost:3000/time -d mseq=3000 -G
TIME: 3000
[ec2-user@xxxx log]$ curl -w "\n" localhost:3000/time -d mseq=4000 -G
TIME: 4000
[ec2-user@xxxx log]$ curl -w "\n" localhost:3000/time -d mseq=10000 -G
TIME: 10000

[ec2-user@xxxx log]$ cat app.log
[2021-09-14T15:05:05.302] [INFO] app - TIME: 3000
[2021-09-14T15:05:12.075] [INFO] app - TIME: 2002
[2021-09-14T15:05:17.736] [INFO] app - TIME: 3000
[2021-09-14T15:05:24.688] [INFO] app - TIME: 4000
[2021-09-14T15:05:37.342] [INFO] app - TIME: 10000

2.Amazon CloudWatch エージェントをインスタンスにインストールする

次にCloudWatchAgentから CloudWatchLogs へログ出力するための権限を設定します。
ポリシーに「CloudWatchAgentServerPolicy」を選択して IAM Role を作成します。
作成したポリシーをEC2インスタンスにアタッチします。

続いてCloudWatchエージェントをインストールします。
System Manager からもインストール可能とのことですが、今回はEC2インスタンス上でインストールしました。

sudo yum -y install amazon-CloudWatch-agent

インストール完了後、アプリケーションログをCloudWatchLogsにストリーミングするための設定を行います。
設定は以下の1ファイルのみです。

/opt/aws/amazon-CloudWatch-agent/etc/amazon-CloudWatch-agent.d/amazon-CloudWatch-agent.json

{
  "logs": {
    "logs_collected": {
      "files": { 
        "collect_list": [
          {
            "file_path": "/opt/app/elastic-cache-test-app/app.log",
            "log_stream_name": "{instance_id}",
            "log_group_name": "/opt/app/elastic-cache-test-app/app.log",
            "timestamp_format": "%d/%b/%Y:%H:%M:%S %z",
            "timezone": "UTC”
          }
        ]
      }
    }
  }
}

CloudWatchエージェントを再起動します。

sudo systemctl restart amazon-CloudWatch-agent.service

CloudWatchLogsのメニューから設定したロググループを選択すると、アプリケーションログが出力されていることが確認できます。

3.ログから応答時間のメトリクスフィルタを作成する

メトリクスフィルタとは、CloudWatchLogsに出力したログを文字列などでフィルタリングできる機能で、文字列を抽出して計測対象(メトリクス)として扱うことができます。
文字列のパターンを定義して、抽出した値のうちどの値をメトリクスとして定義するかを設定します。

設定画面

出力されるログの末尾のカラム(2000や3000等)を抽出したかったので、下記のようにパターン定義してテストを実行しました。

定義したパターン

[ACCESSTIME, LEVEL, APPNAME, ..., TIME]

定義したパターンでの抽出結果も設定画面の中で直ぐに確認することができます。

フィルタ結果

「...」で不特定多数のカラムが定義できます。「ー」や「TIME:」が列番号とともにフィルタされています。
(今回の場合は末尾のカラムだけ欲しいので、[..., TIME]でも取得できると思われます。)
TIMEの列で処理に要した時間を取得できました。

次の画面でTIMEをメトリクスとして設定します。メトリクス値の入力欄に$TIMEと設定します。

4.メトリクスグラフを CloudWatch コンソールに表示する

事前準備として、1分毎に適当な値をログ出力するようにcronを設定して一晩放置しました。(グラフに表示するデータが少ないと寂しいので。。)

curl localhost:3000/time -d mseq=$(($RANDOM % 10000)) -G

全てのメトリクスのメニューから、作成したメトリクスの画面に遷移します。
実際に12時間置いてから表示したグラフが下記になります。

5. 応答時間メトリクスの平均値がしきい値を上回ったときにAmazon SNS 通知を送信する CloudWatch アラームを作成する

アラームの条件を指定します。今回は5分間の平均値をアラームのメトリクスに設定しました。

続いて通知方法にはSNSでメールアドレスをエンドポイントに指定しました。

設定が完了するアラームのグラフ上に、設定したしきい値を示す赤い線が引かれます。

これで全ての設定が完了しました。
しきい値を超えたタイミングでメール通知されることも確認できました。

6.所感

  • Zabbixやcacti等の自前で構築、設定が必要な監視の仕組みと比べて、圧倒的に楽。
  • ECS FargateやLambdaの場合は更に簡単(標準出力に流しておけばそのままCloudWatch Logsに流れてくる)
  • アラートが発生した際のアクションとして通知だけでなくAutoScalingも設定できるみたい。CloudWatchのコンソールから設定できるのは便利だと思いました。
GitHubで編集を提案

Discussion