59
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS FireLensでfluentbitコンテナをカスタマイズする具体的な方法

Last updated at Posted at 2020-03-14

ECSのログ事情について

AWSのECSによるコンテナ運用では、ログの出力がデフォルトでCloudWatchに限定されています。
これはECSのログドライバがawslogsになっているということになりますが、実用面では力不足であることが否めません。
S3やElasticsearchにログを流すには?Datadogなどのサードパーティツールにログを流すには?
解決方法として、AWSの公式でFireLensという方法が紹介されています。

FireLensはfluentbitのコンテナをサイドカーとして起動し、ログの収集をfluentbitの機能で実行するというものです。
fluentdも選択できます)

例)今回目指すWebアプリケーションの構成
firelens.png

実際に、AWSコンソールのECSのタスク定義を設定する画面には以下のような設定項目があります。

スクリーンショット 2020-03-14 15.01.08.png

FireLensの統合を有効にするを選択すると、自動で906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latestのコンテナがサイドカーとして追加されるようになります。
このコンテナはAWSの公式コンテナです。

当記事では、このコンテナを自身でカスタマイズしたものを適用する一例を紹介します。

コンテナ定義

それではコンテナ定義がどのようになるかTerraformで見てみましょう。

task_definitisons.tf
data "template_file" "default" {
  template = <<EOF
[
  {
    "image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/go:latest",
    "name": "go",
    "essential": false,
    "logConfiguration": {
      "logDriver": "awsfirelens"
    },
    "portMappings": [],
    "cpu": 64,
    "memoryReservation": 128
  },
  {
    "image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/nginx:latest",
    "name": "nginx",
    "essential": true,
    "logConfiguration": {
      "logDriver": "awsfirelens"
    },
    "portMappings": [
      {
        "hostPort": 80,
        "protocol": "tcp",
        "containerPort": 80
      }
    ],
    "cpu": 64,
    "memoryReservation": 128
  },
  {
    "image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest",
    "name": "log_router",
    "essential": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/firelens-sample",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "firelensConfiguration": {
      "type": "fluentbit",
      "options": {
        "config-file-type": "file",
        "config-file-value": "/fluent-bit/etc/fluent-bit_custom.conf"
      }
    },
    "portMappings": [],
    "cpu": 128,
    "memoryReservation": 256
  }
]
EOF
}

Goのコンテナ、Nginxのコンテナ、fluentbitのコンテナを定義しました。
以下を確認してください。

"logConfiguration": {
    "logDriver": "awsfirelens"
}

logDriverをawsfirelensとしています。
なお、fluentbitコンテナ自体のログドライバはawslogsとしてください。

そして注目すべき定義はこちらです。

"firelensConfiguration": {
    "type": "fluentbit",
    "options": {
        "config-file-type": "file",
        "config-file-value": "/fluent-bit/etc/fluent-bit_custom.conf"
    }
}

config-file-valueを指定しています。
この/fluent-bit/etc/fluent-bit_custom.confに独自カスタマイズしたfluentbitの設定を記載しましょう。

fluentbitの設定

よくある設定

サンプルコードをもとに進めます。
先ほどの/fluent-bit/etc/fluent-bit_custom.confを記載していきましょう。

./docker/fluentbit/fluent-bit_custom.conf
[SERVICE]
    Flush 1
    Grace 30
    Log_Level info

[OUTPUT]
    Name cloudwatch
    Match *
    log_key log
    region ap-northeast-1
    log_group_name /ecs/firelens-sample
    log_stream_name container

[OUTPUT]
    Name firehose
    Match *
    delivery_stream my-firehose
    region ap-northeast-1

OUTPUTにcloudwatchfirehoseを指定していることに注目してください。
後述しますが、amazon/aws-for-fluent-bitのコンテナをベースに進めますのでデフォルトで上記のプラグインがインストールされた状態になっています。

さて、この設定でcloudwatchfirehoseにログが出力されるようになりますが、一点、Match *に注目しましょう。
これはFireLensが収集したログを全て出力されるようになっており、
例えばDataLakeのようなログの流し方であれば問題ありませんが、もう少し整形してから流したいというニーズがあるかもしれません。

そこでParserを使って、ログをコンテナごとのCloudWatchストリームに分けるということをしてみましょう。

Parser

先ほどのconfを以下のようにしました。

./docker/fluentbit/fluent-bit_custom.conf
[SERVICE]
    Flush 1
    Grace 30
    Log_Level info
    Streams_File stream_processor.conf
    Parsers_File parser.conf

[FILTER]
    Name parser
    Match *
    Key_Name container_name
    Parser container
    Reserve_Data true

[OUTPUT]
    Name cloudwatch
    Match combine.nginx
    log_key log
    region ap-northeast-1
    log_group_name /ecs/firelens-sample
    log_stream_name nginx

[OUTPUT]
    Name cloudwatch
    Match combine.go
    log_key log
    region ap-northeast-1
    log_group_name /ecs/firelens-sample
    log_stream_name go

[OUTPUT]
    Name firehose
    Match container
    delivery_stream my-firehose
    region ap-northeast-1

以下の箇所でログのパースをおこないます。

[FILTER]
    Name parser
    Match *
    Key_Name container_name
    Parser container
    Reserve_Data true

さてここで、そもそもFireLensからはどのようなログが収集されているのでしょうか。
当然、自身で流したアプリケーション用のログ、Nginxのアクセスログなどをイメージしますが
実はそのほかのメタ情報を付与した状態で収集しています。
以下を確認してください。

{
    "container_id": "abcde12345",
    "container_name": "/ecs-firelens-sample-1-nginx-xxxyyyzzz",
    "ecs_cluster": "arn:aws:ecs:ap-northeast-1:1234567890123:cluster/firelens-sample",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:1234567890123:task/aaa-bbb-ccc-ddd-eee",
    "ecs_task_definition": "firelens-sample:1",
    "log": "10.0.0.0 - - [14/Mar/2020:10:00:00 +0000] \"GET /healthcheck HTTP/1.1\" 200 0 \"-\" \"ELB-HealthChecker/2.0\" \"-\"",
    "source": "stdout"
}

firelensが収集するログの例です。logというキーが出力されるログですが、それ以外にタスク定義の情報やコンテナ名などもあります。
今回はこれらの情報でパースをしてみます。

container_nameをパースしてnginxという部分を抜き出します。
Goコンテナの場合はgoという文字を抜き出します。

./docker/fluentbit/parser.conf
[PARSER]
    Name container
    Format regex
    Regex ^\/(?<task_name>(ecs-firelens-sample))-(?<task_revision>\d+)-(?<container_name>.+)-(?<target_name>.+)$

正規表現で抜き出してみました。以下のようにパースされました。


{
    "task_name": "ecs-firelens-sample",
    "task_revision": 1,
    "container_name": "nginx",
    "target_name": "xxxyyyzzz",
}

container_nameが「nginx」となっています。
これでコンテナ別にログの出力先を分けるということができそうです。

もちろん、ログそのものに対してパースをしてログを振り分けることができれば様々な要件にも対応できるかと思います。
いろいろなパターンを考慮して要件に沿った設計を考えてみてください。

Stream Processor

ログの種類別にタグを振り分けることができます。
先ほどのcontainer_name「nginx」「go」をそれぞれ「combine.nginx」「combine.go」というタグを付与してみましょう。

./docker/fluentbit/stream_processor.conf
[STREAM_TASK]
    Name nginx
    Exec CREATE STREAM nginx WITH (tag='combine.nginx') AS SELECT log FROM TAG:'*-firelens-*' WHERE container_name = 'nginx';

[STREAM_TASK]
    Name go
    Exec CREATE STREAM go WITH (tag='combine.go') AS SELECT log FROM TAG:'*-firelens-*' WHERE container_name = 'go';

# すべてのログ
[STREAM_TASK]
    Name container
    Exec CREATE STREAM container WITH (tag='container') AS SELECT * FROM TAG:'*-firelens-*';

SQLのような形式で記載します。
FireLensのログは元々*-firelens-*にマッチするタグが付与されています。
それにWHERE container_name = 'nginx'などとすることでnginxの新しいタグを追加できます。

追加したタグは、[OUTPUT]セクションで指定しましょう。

[OUTPUT]
    Name cloudwatch
    Match combine.nginx
    log_key log
    region ap-northeast-1
    log_group_name /ecs/firelens-sample
    log_stream_name nginx

Docker定義

続いてDocker定義です。

amazon/aws-for-fluent-bitコンテナをベースに使います。

./docker/fluentbit/Dockerfile
FROM amazon/aws-for-fluent-bit:2.1.1

COPY ./docker/fluentbit/fluent-bit_custom.conf /fluent-bit/etc/fluent-bit_custom.conf
COPY ./docker/fluentbit/stream_processor.conf /fluent-bit/etc/stream_processor.conf
COPY ./docker/fluentbit/parser.conf /fluent-bit/etc/parser.conf

これでECRにプッシュすれば立派なログ収集コンテナとして活躍できるでしょう。

$ aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com

$ docker build -t firelens-sample-fluentbit:latest -f docker/fluentbit/Dockerfile .
$ docker tag firelens-sample-fluentbit:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest
$ docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest

おわり

fluentbitを使ったログ収集用コンテナのカスタマイズについて解説しました。
誰かの参考になれば幸いです。

59
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?