ENECHANGE Developer Blog

ENECHANGE開発者ブログ

【AWS ECS 小ネタ】CloudWatch Agentを使用してクラスター名、タスクIDのディメンション付きでディスク使用率のメトリクスを収集する

ENECHANGE所属のエンジニア id:tetsushi_fukabori こと深堀です。
急速に涼しくなった今日このごろ皆様いかがお過ごしでしょうか。

私はと言えばLDLコレステロールを抑え込むために3ヶ月間動物性脂質を断ったことで食生活が大きく変わり、なんだか食に対する意識に不可逆な変化が生じてしまった気がしています。
生き延びるために必要な変化だったかもしれませんが。


これからしばらくは最近のお仕事である「既存アプリケーション基盤のコンテナ化プロジェクト」で学んだことやTipsなどをブログの記事にまとめていきます。
全何回になるか、ひょっとしたら今回で終わるのか、予定を決めていないので分からないですがやっていきます。

今回のテーマは「CloudWatch Agentを使用してクラスター名、タスクIDのディメンション付きでディスク使用率のメトリクスを収集する」です。
ECS on Fargateでメトリクスを取る時CloudWatch Container Insightsは大変便利にクラスター名、サービス名のディメンション付きでメトリクスが収集できます。
ただディスクに関するメトリクスが収集できないのでそれに対応するためのTipsです。

最近のお仕事

2022年の第3クォーターを通じて「既存のアプリケーション基盤のコンテナ化」を押し進めていました。
ENECHANGEにはAWS Elastic Beanstalk(以降EB)をプラットフォームとして構築されたアプリケーションが多数存在するのですが、運用上の負荷増大を理由に別のプラットフォームでの稼働を模索していました。

aws.amazon.com

これに伴い試験的にAmazon Elastic Container Service(以降ECS)でコンテナ化したアプリケーションを稼働させるべく調査・実装・移行を行っていました。

aws.amazon.com

現代を生きるwebアプリケーションのソフトウェアエンジニアとしては未熟なことながらコンテナ技術にあまり触れてこなかったので大変学びと失敗の多い3ヶ月を過ごしました。

今回のTipsの背景

対象はECS on Fargateで稼働する商用のコンテナ環境です。
コンテナで利用できるディスクの容量はアプリケーションに対して十分にあります。
コンテナ環境において稼働中のコンテナのディスク使用量が急増することはまれですが、アプリケーションのタスク定義はアプリチーム管理であり、実装によってはディスク使用量の急増があり得るため、ディスクのメトリクスを収集したいと考えています。

やりたいこと

ECS on Fargateで稼働しているコンテナ環境のディスク使用率のメトリクスを取得し、CloudWatchで参照したいです。
ディスク容量はタスクで共有されているためタスク単位での使用率を取得できる必要があります。(クラスター内に複数のタスクが存在し、一部のタスクだけ使用率が急増したような状態を把握したい)
このため取得するメトリクスのカスタムディメンションとして「タスクID」を取得する必要があります。

システム構成

  • ECS on Fargate
  • クラスター : サービス : タスク = 1 : 1 : N
    • タスク数はスケールアウト・スケールインにより可変
  • CPU使用率、メモリ使用率などはCloudWatch Container Insightsで取得している

ECS on Fargateでのメトリクス収集

ECS on Fargateでのメトリクス収集については公式から紹介されている方法がいくつかあります。

CloudWatch Container Insights

ECSに組み込まれているCloudWatch Container Insightsでの収集です。

ECSにおいてはメトリクスの収集にCloudWatch Container Insightsが用意されていますが、ディスクに関するメトリクスは「StorageReadBytes」「StorageWriteBytes」の2つのみで、使用率が取得できません。

docs.aws.amazon.com

EC2であればインスタンスにCloudWatch Agentをインストールすることでインスタンスレベルのメトリクス収集が可能ですが、Fargateではその方法は案内されていません。

CloudWatch Agentサイドカー by 公式提供イメージ

CloudWatch Agentをサイドカーとして利用する方法もあり、公式のイメージが紹介されています。

docs.aws.amazon.com

このイメージは環境変数 CW_CONFIG_CONTENT を参照し、環境変数に設定されたCloudWatch Agentの設定JSONに従ってメトリクスを収集します。
この方法でECSタスクの環境変数にクラスター名やサービス名などをカスタムディメンションとして設定可能です。

この方式での問題点は事前に環境変数を指定しなければならないのでタスク起動時に決定されるタスクIDはカスタムディメンションに設定できないことです。
EC2で言うところの "ImageId": "${aws:ImageId}" のような文法で取得できないかサポートに確認しましたが、そのような文法はありませんでした。

今回やったこと

AWSが提供しているCloudWatch AgentイメージのDockerfileを使い、社内用にカスタムイメージを作成してサイドカーとして利用しました。

このDockerfileだと最終イメージはベースイメージであるscratchにコンパイルしたCloudWatch Agentのバイナリを乗せているだけで、何かあったときにbashを実行することもできません。 この状態はやや怖かったので、ベースイメージはdebian slimを利用するようにし、マルチステージビルドはしないように変更しました。
その上でランタイムで実行されるENTRYPOINTをシェルスクリプトに変更し、シェルスクリプト内でECSのメタデータを環境変数に仕込まれているメタデータエンドポイントから取得します。

docs.aws.amazon.com

メタデータエンドポイントのレスポンスにはクラスターARNとタスクARNが含まれており、これをパースするとクラスター名とタスクIDが取得できます。

$ curl "${ECS_CONTAINER_METADATA_URI_V4}" | jq
{
  # (省略)
  "Labels": {
    "com.amazonaws.ecs.cluster": "arn:aws:ecs:ap-northeast-1:012345678901:cluster/hoge-cluster",
    "com.amazonaws.ecs.task-arn": "arn:aws:ecs:ap-northeast-1:012345678901:task/hoge-cluster/b5454ec291241b6d7ba3f08fd0109ffd",
    # (省略)
  },
  # (省略)
}

ここで得られた「hoge-cluster」と「b5454ec291241b6d7ba3f08fd0109ffd」をそれぞれ「ClusterName」「TaskID」という名前でカスタムディメンションとして設定したCloudWatch Agentの設定JSONを作成すればCloudWatch Agentコンテナでメトリクスが収集可能になります。

今回は運用を楽にするため、GitHubでタグを打ったらGitHub→CodeBuild→ECRの連携でビルドするようにしました。
イメージのタグはGitHubのタグを引き回すことにしています。

このサイドカーをアプリチームのタスク定義に入れ込んでもらうことで…

クラスター名、タスクIDを含むメトリクスがCloudWatchメトリクスに増えている

タスクIDごとにディスク使用率が取得できている(タスクIDは一部マスクしてます)

無事メトリクスが取得できました!

まとめ

ECS on Fargateのコンテナ環境でタスク毎のディスク使用率を取得するため、CloudWatch Agentのカスタムイメージを作成しタスク起動時にメタデータからCloudWatch Agent設定JSONを生成することでメトリクスが収集できました!