Amazon ECSのログストリームを見やすく階層的に整理できるawslogs設定

こんにちは。SREのid:do-su-0805です。普段はid:do_su_0805として生活しています。

この記事では、Amazon ECS(以下、ECS)でコンテナを動かすとき、ログドライバーとしてawslogsを利用してAmazon CloudWatch Logs(以下、CloudWatch Logs)にログを出力する際に、awslogs-stream-prefixというパラメータには何を設定するとよいかについて考察します。

結論から言うと、このパラメータに「コンテナのイメージタグ」を入れるようにしたところ、出力されるログストリームの/区切りの階層が見やすくなり、ログが世代別に扱いやすくなったよ、というお話です。

ECS+CloudWatch Logs構成時のロググループとログストリームについて

ECSのドキュメント「awslogs ログドライバーを使用する」に詳しく記載されていますが、awslogsログドライバーを利用してCloudWatch Logsにログを出力する際に、ロググループおよびログストリームの形式は以下のようになります。

  • ロググループ: 指定したロググループ
  • ログストリーム: ${prefix-name}/${container-name}/${ecs-task-id}

ログストリームのそれぞれは次の通りです。

  • prefix-name: awslogs-stream-prefixオプションで指定*1
  • container-name: タスク定義内で定義しているコンテナ名
  • ecs-task-id: ECS側で決定されたタスクのID
f:id:do-su-0805:20210803001752p:plain
図1. awslogsにおける各設定と実際のログストリームにおける対応

以上がつながって1つのログストリームを指し示すということは、可視性の高いログストリームを設計するには「ロググループの名前」「awslogs-stream-prefix」「コンテナ名」を決める際に、この事実を意識しておく必要があります。

なぜなら、awslogs-stream-prefix以外の2つは、ログ出力をする設定以外の場所で命名が行われるからです。

どのようなログストリームが構成されがちかを事例から考えてみる

ここで事例として、以下のような状況を想定してみます。

  • 1つのAWSアカウントで、各コンポーネントをprefixで区別することで、production環境とstaging環境を用意する
    • ただし、ECSタスクの中で動くコンテナについては、既にサービスレベルで分離されていることからprefixは付けない
  • ECSサービスでは、1種類のタスクが動作し、2タスク起動する
    • サービス単位でロググループを共有する
  • ECSタスク内では、次の2つのコンテナが動作する
    • アプリケーション
    • サイドカーとして監視用のAgent

この場合、前提として「prefixで区別する」ことを意識しながら作成していくと、以下のような構成になるかなと思います(細かい差はあると思いますが概念としてお読みください)。

項目 設定
ECSサービス Hoge-production
ロググループ Hoge-production-logs
コンテナ名 appもしくはmonitor
awslogs-stream-prefix Hoge-production

特にawslogs-stream-prefixは「結局どこに入るのか?」を意識しないと、上記のようにECSサービスの名前や、利用するコンテナの名前、あるいはecsなどと入れがちかなぁと思います。

この構成でappコンテナおよびmonitorコンテナからCloudWatch Logsに送られるログストリームは、以下のようになります。

appコンテナのとき:
Hoge-production-logs:Hoge-production/app/${ecs-task-id}
monitorコンテナのとき:
Hoge-production-logs:Hoge-production/monitor/${ecs-task-id}

いかがでしょうか? Hoge-productionが即座に2回登場し、目が滑り、かつ冗長であることがわかります。

言い換えると、awslogs-strema-prefix情報の区切りとして機能していないことが分かります。今回は、この観点から整理を進めてみます。

awslogs-stream-prefixにイメージタグを入れるとログストリームが整理できる

先ほど想定した事例では、ログストリームが次のようになりました。

Hoge-production-logs:Hoge-production/(app|monitor)/${ecs-task-id}

この全体を、それぞれの項目から得られる情報を指し示す日本語で書き換えると、次のようになります。

ロググループ名(サービス名を含む):サービス名/コンテナ名/タスクID

このように「サービス名」をawslogs-stream-prefixに入れてしまったため、情報が全く増えていません。何かもったいない気がします。それでは「何が入ると嬉しいかなぁ?」と考えてみたところ、アプリケーションの世代が分かると、情報の区分がさらに増せるなと気づきました。

そこで、当該タスクが利用するコンテナイメージのイメージタグを入れてみました(ただし、イメージ利用時にmaster stable latestなどと書いている場合は特に意味がない状況が続きます。固有のビルドタグなどが入ったユニークなものを指すタグを利用する場合のみ効果を発揮します)。

変更の前後で、先ほどの「得られる情報を指し示す日本語」を比較してみます。

before:
ロググループ名(サービス名を含む):サービス名/コンテナ名/タスクID
after:
ロググループ名(サービス名を含む):イメージタグ/コンテナ名/タスクID

いかがでしょうか? 「サービス」「イメージ」「コンテナ」「タスク」と情報の要素が階層的に整理されたように見えます。

f:id:do-su-0805:20210803000613p:plain
図2. ログストリームの階層イメージ

ECSのタスクIDはタスク自体に紐づくため、今回の場合はappmonitorそれぞれ同じタスクIDが発番されるわけですが、それでも

  • このタスクのログを調べたい
  • このイメージで起動したappもしくはmonitorのログを調べたい

という2つのケースには対応できるようになり、ログの利用や解析がしやすくなったのではないでしょうか。

このようにawslogs-stream-prefixにイメージタグを入れることで、世代別のログを見ることができるようになり、たいへん便利になったと思っています。

この記事のまとめ

この記事では次の内容を紹介しました。

  • awslogsログドライバーを利用したECS+CloudWatch Logs構成時は「ログ出力をする設定以外の場所で命名が行われる」コンポーネント名が、ログストリームに影響する
  • awslogs-stream-prefixprefix-name)にコンテナのイメージタグを入れると、情報の整理が進む

はてなでは、情報の整理を進める仲間を募集しています。

はてなでは、サービス開発の道標となるSREの仲間を募集しています

*1:EC2起動タイプの場合はオプションで、指定しないとDockerデーモンが割り振ったコンテナIDになる。Fargate起動タイプでは指定が必須