この記事では、CloudWatch Logs を CWL と略記します。
🔰 はじめに
ECS は 複雑な要件での CWL ログ転送 を得意としていません。
- ファイル出力したログを、CWL に転送したい
- アプリログとアクセスログを、別々のロググループに転送したい
これらは ECS 標準機能で実現することが出来ず、 追加の実装が必要になります。
そこで本記事では、「ECS Fargate でファイル出力しているログを CWL の複数ロググループに振り分ける方法」 について解説します。
アプリケーションのログファイルを CWL 転送したい、というのはクラウドリフトあるあるだと思いますので、多くの方のお役に立てば幸いです。
(クラウドネイティブで構築するのであれば、ログは標準出力するのが良いと思います)
📦目指す姿
最終的に目指す姿を以下に示します。
ECS タスク内に、アプリケーションコンテナとは別のサイドカーコンテナ(Fluent Bit)を作成し、アプリケーションコンテナと同じストレージにアタッチさせたうえで、firelens ログドライバを利用して CWL に転送させます。
補足:ログ出力先について
ECS は永続ボリュームとして EFS または EBS をアタッチすることが出来ます。
このうち EFS は共有ファイルサーバですので、EC2 などをアタッチしてあげれば、ECS 以外からもストレージの中身を確認することが可能です。
一方で、EBS は基本的に1台のサーバからしかアタッチすることが出来ないため、アタッチ元の ECS タスクからしか中身を確認できません。
そのため ECS + EBS 構成はログの確認や転送が煩雑になりやすく、したがって本記事では 「EBS 内のログファイルを ECS タスクから CWL 転送する方法」 について紹介します。
🔨実装
上記の姿を実現するために、変更すべきポイントは2点です。
-
ECS タスク定義
- Fluent Bit サイドカーコンテナの定義を追加します
-
Fluent Bit 設定ファイル
- ログファイルごとに、異なる CWL ロググループに転送するよう設定します
以下、サンプル実装を示します。
ECS タスク定義
{
"containerDefinitions": [
{ // アプリケーションコンテナの設定
"name": "app",
"image": "your-app-image",
"mountPoints": [
{
"sourceVolume": "ebs-volume",
"containerPath": "/mnt/logs"
}
]
},
{ // サイドカーコンテナの設定(新規)
// ここで配信先 CWL の設定はしない
"name": "log-router",
// ECR Public Gallery から AWS が公式に提供しているイメージ
"image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:latest",
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"config-file-type": "file",
// EBS の /fluent-bit/etc 配下に conf ファイルを配置しておく
"config-file-value": "/fluent-bit/etc/fluent-bit.conf"
}
},
"mountPoints": [
{
"sourceVolume": "ebs-volume",
"containerPath": "/mnt/logs"
}
]
}
]
}
参考 : Amazon ECS タスク定義の例: FireLens にログをルーティングする
Fluent Bit 設定ファイル
# アプリログとアクセスログが出力されていると仮定
[INPUT]
Name tail # ログファイルをtailして取得
Path /mnt/logs/app.log
Tag app # 識別子([OUTPUT]で使用する)
[INPUT]
Name tail
Path /mnt/logs/access.log
Tag access
[OUTPUT]
Name cloudwatch_logs # CWL 転送
Match app # [INPUT] の tag
region ap-northeast-1
log_group_name /ecs/app # CWL ロググループ名
log_stream_prefix app- # ログストリームの prefix
auto_create_group true # ロググループを自動で作成する
[OUTPUT]
Name cloudwatch_logs
Match access
region ap-northeast-1
log_group_name /ecs/access
log_stream_prefix access-
auto_create_group true
この設定により、
| ファイル | ロググループ |
|---|---|
| app.log | /ecs/app |
| access.log | /ecs/access |
のようにログファイルごとにロググループを分離可能です。
補足 : ロググループを分離すると何が良いのか
ロググループを分離することで、以下のメリットを得ることが出来ます。
-
ログの保持期間を、ログ種別ごとに設定できる
CWL は、ロググループごとにログ保持期間を設定されます。ログ保持期間が最適化されることで、コスト最適化につながります -
ログ種別ごとに、権限を設定できる
IAM ポリシーもロググループごとに設定可能です。 -
ログの検索性が向上する
特に(JSONなどの構造化ログではない)プレーンテキストのログを CWL 転送する場合、CWL Insights で実行するクエリは複雑になりがちです。ログ種別ごとにロググループを分けることでクエリを単純化し、また検索速度も向上させることが可能です。
おまけ...ログ内容で転送先ロググループを振り分ける
これまでの例では、ログファイルごとにロググループを振り分けるような設定でした。
以下では、ログ内容に従ってロググループを振り分けるような Fluent Bit 設定のサンプルも共有します。
# ログレベルに応じて、転送先ロググループを変更
[INPUT]
Name tail
Path /mnt/logs/app.log # tailでアプリログを取得
Tag app # [FILTER] で使用
[FILTER]
Name rewrite_tag # ログ内容に沿って、Tag を上書きする
Match app # [INPUT] の Tag
# Tag 上書きルールを設定
# Rule <key> <regex> <new_tag> <keep>
Rule $level ^ERROR$ app.error true
Rule $level ^INFO$ app.info true
[OUTPUT]
Name cloudwatch_logs
Match app.error
log_group_name /ecs/app/error
region ap-northeast-1
[OUTPUT]
Name cloudwatch_logs
Match app.info
log_group_name /ecs/app/info
region ap-northeast-1
