🕊️

FireLens で収集するログのサイズに関する考慮

2024/02/11に公開

はじめに

大きなシステムでログ収集を行う場合、ログの流量(一定期間内に何件のログを処理しないといけないか)とログのサイズ(一件あたりのログがどの程度のサイズか)の考慮が必要です。これらが想定を大きく超えるとクラウド利用料が大きく膨らんだり、ログを集めきれずに欠損してしまうような状況が起こります。

今回は FireLens を使用して CloudWatch Logs にログを送信する場合のサイズ上限と、超過した場合の挙動を検証したので記録しておきます。

FireLens のサイズ上限

FireLens というよりはコンテナロガーの仕様のようですが、ソケット経由で Fluent Bit が受け取るログは 16kb に制限されます。16kb を超過したログは分割されて受信します。

検証してみましょう。
以下のようなコンテナを ECS 上で実行します。そうすると毎秒「16,384 文字の0 + TEST」を Stdout に出力するので、そのログを使用して挙動を見てみます。

Dockerfile
FROM amazonlinux:2
COPY entrypoint.sh /tmp
ENTRYPOINT ["/tmp/entrypoint.sh"]
entrypoint.sh
#!bin/sh
while true; do sleep 1; printf "%016384dTEST\n"; done

partial_messagepartial_idpartial_ordinalpartial_lastの 4 つのキーがログに付与され、ログが分割されたことがわかります。

CloudWatch Logs 1
{
    "partial_message": "true",
    "partial_id": "7bc16f7a1b57f693f141742c44a086074a4a3d377e345d222b83fe1b24e3deb5",
    "partial_ordinal": "1",
    "partial_last": "false",
    "ecs_cluster": "example-ecs",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:account-id:task/example-ecs/ea92eef62d4c4d63a94cf249bcc66ea2",
    "ecs_task_definition": "example-taskdef:1",
    "container_id": "ea92eef62d4c4d63a94cf249bcc66ea2-3935363592",
    "container_name": "main",
    "source": "stdout",
    "log": "00000000...00000000"
}
CloudWatch Logs 2
{
    "partial_message": "true",
    "partial_id": "7bc16f7a1b57f693f141742c44a086074a4a3d377e345d222b83fe1b24e3deb5",
    "partial_ordinal": "2",
    "partial_last": "true",
    "ecs_cluster": "example-ecs",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:account-id:task/example-ecs/ea92eef62d4c4d63a94cf249bcc66ea2",
    "ecs_task_definition": "example-taskdef:1",
    "container_id": "ea92eef62d4c4d63a94cf249bcc66ea2-3935363592",
    "container_name": "main",
    "source": "stdout",
    "log": "TEST"
}

Fluent Bit が分割されたログを受け取った場合は、Multiline Parser を使用して結合することができます。

https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/filter-multiline-partial-message-mode

Multiline Parser については以下の記事でも紹介しています。詳細はそちらを確認ください。

https://zenn.dev/nh8939/articles/add269d15152ba

以下の組み込みパーサーを使用するだけです。

https://github.com/aws-samples/amazon-ecs-firelens-examples/blob/mainline/examples/fluent-bit/filter-multiline-partial-message-mode/README.md#L21-L28

CloudWatch Logs 3
{
    "ecs_cluster": "example-ecs",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:account-id:task/example-ecs/c205787da5204f0e98c7958299a35a34",
    "ecs_task_definition": "example-taskdef:1",
    "container_id": "c205787da5204f0e98c7958299a35a34-3935363592",
    "container_name": "main",
    "source": "stdout",
    "log": "00000000...00000000TEST"
}

CloudWatch Logs のサイズ上限

CloudWatch Logs のログイベントサイズ上限は 256kb です。これはハードリミットのため上限緩和できません。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html

Multiline Parser を使用して FireLens の 16kb 制限を回避すると、今度はこちらの制限に引っかかる場合があります。

こちらも検証してみました。
先ほど使用したコンテナの定義を少し変更して、今度は毎秒「262,144 文字の0 + TEST」を Stdout に出力させます。

entrypoint.sh
#!bin/sh
while true; do sleep 1; printf "%0262144dTEST\n"; done

CloudWatch Logs には以下のように後半部分が欠損したログが収集されました。これにより Json フォーマットも壊れてしまっています。

CloudWatch Logs 4
{
    "ecs_cluster":"example-ecs",
    "ecs_task_arn":"arn:aws:ecs:ap-northeast-1:account-id:task/example-ecs/973e05dc9c6b498b9450da1ac5f246a1",
    "ecs_task_definition":"example-taskdef:5",
    "container_id":"973e05dc9c6b498b9450da1ac5f246a1-3935363592",
    "container_name":"main",
    "source":"stdout",
    "log":"00000000...00000000

CloudWatch Logs のサイズ上限によりログが尻切れになると、FireLens 側のログに以下のようなイベントが出力されます。

[2024/01/08 02:17:15] [ warn] [output:cloudwatch_logs:cloudwatch_logs.1] [size=262466] Truncating event which is larger than max size allowed by CloudWatch

検知したところですでにログは失われてしまっているのでどうしようもないですが、ログ欠損が発生したときの原因調査には使えそうです。

おわりに

ログがここまで大きなサイズになることはあまりないかもしれませんが、スタックトレースをログ出力しているようなケースでは注意が必要です。いざトラブルが発生したとき、原因調査をしようと思ったらログが欠損していて調査ができない…みたいなことにならないように、ログサイズに関する制限事項はあらかじめ整理しておきましょう!
今回は以上です👋

Discussion