25
6

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.

ECS ログ出力カスタマイズ FireLens の設定方法をわかりやすく整理してみた

Last updated at Posted at 2022-10-15

FireLens でログ出力を分岐する記事もよかったら見てみてください。
https://qiita.com/sugimount-a/items/c43b1bd4f9ecbbb448b7

Amazon ECS におけるログ出力

Amazon ECS 上で稼働するログを、外部に保存する方法は、主に 3 つあります。
引用 : https://speakerdeck.com/prog893/aws-startup-tech-meetup-number-3-kantankontenaroginguxuan-shou-quan

 

awslogs logging driver

image-20221015214233390.png

 

独自で Fluentd のサイドカーを構成

image-20221015214318996.png

 

FireLens

image-20221015214337519.png

 

awslogs logging driver は、シンプルに実装できますが、ログの出力先は CloudWatch Logs だけです。大量なログが発生する環境では、CloudWatch Logs のデータ収集の料金が気になってきます。そういった時に、FireLens が便利に活用できます。

FireLens を使うことで、Amazon S3 といった他の AWS サービスなどにログを保存できます。例えば、Kinesis Data Firehose 経由で Amazon S3 に保存することで、Athena 上で SQL クエリーを使った検索・分析が可能です。また、QuickSight で可視化も含めた、他の AWS サービスとの連携が実現できます。

アプリケーション側でなにをしないといけないの?

若干古い構成図で、最新の Fargate とは異なる部分があるのですが、わかりやすい図なので引用します。

引用 : https://aws.amazon.com/jp/blogs/news/under-the-hood-firelens-for-amazon-ecs-tasks/

image-20221015215722969.png

上の図は、FireLens がどのように機能するかを示しています。画面左側にある Application が、動かすアプリケーションを表現しています。その隣の FireLens Container が、FireLens で管理されているサイドカーコンテナを表現しています。FireLens Container は、Fluent Bit や Fluentd をベースに構成されています。

 

FireLens を使ってログを送りたいときに、自分たちが動かすアプリケーション側では以下のどちらかの対応が必要です。

  • ログを標準出力に出力する

    • FireLens の仕組みで、Applicaton のタスクで標準出力に出されたログは、Unix Domain Socket として FireLens Container に送付されます。
    • FireLens Containerは、事前設定された内容に合わせて Kinesis Data Firehose といった任意の宛先にログを転送します。
  • Fluent Logger ライブラリを使って、FireLens コンテナに TCP で送付する

    • 1 つの Task の中で稼働する、2つのコンテナは、ネットワーク的に互いに通信ができます。

    • Fluent Logger ライブラリを使用することで、直接 FireLens Container にログを送付できます。

    • Go 言語JavaPythonNode.js など、多くの一般的な言語用のライブラリが利用できます。

    • たとえば、Go 言語の場合は次のように、ログを送付できます。

    • port, err := strconv.Atoi(os.Getenv("FLUENT_PORT"))
      if err != nil {
          // エラー処理
      }
      config := fluent.Config{
          FluentPort: port,
          FluentHost: os.Getenv("FLUENT_HOST"),
      }
      
      // Fluentd/Fluent Bit にログを送ることができる構造体のインスタンスを作成します
      fluentLogger, err := fluent.New(fluent.Config{})
      if err != nil {
          log.Fatal(err)
      }
      
      // 各ログに必要なタグを付けることができます
      tag := "app.logs"
      
      // 任意のデータをマップとして送信します
      data := map[string]string{
          "foo": "bar",
      }
      
      // Fluent インスタンスに送信します
      err = fluentLogger.Post(tag, data)
      

FireLens が転送する送付先の指定

FireLens 転送する送付先の指定方法がわかりにくかったので、個人的に整理しました。もしかしたら漏れとかがあるかもですが、基本的には問題ないと思います。ログ送付に使うサイドカーコンテナは、Fluent Bit と Fluentd の 2 種類がありますが、軽量な Fluent Bit を取り上げて紹介します。

ログ送付先が 1種類の場合

例えば、アプリケーションで生成されたすべてのログを、Kinesis Firehose に送付する場合が「ログ送付先が 1種類の場合」に当てはまります。逆に、Kinesis Firehose と Amazon S3 の 2 種類に送付したい場合は、当てはまりません。

「ログ送付先が 1種類の場合」は、ログの送付先を、ECS のタスク定義で指定するのが簡単です。

ECS の Task 定義の中に、JSON で転送先を指定できるオプションがあります。以下のように logConfiguration.options を指定することで、任意の送付先が選べます。

                        省略
            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": {
                    "Name": "kinesis_firehose",
                    "region": "ap-northeast-1",
                    "delivery_stream": "my-stream"
                }
            },
                        省略

 

上記の例では、"Name": "kinesis_firehose",となっており、Kinesis Data Firehose にログを送付します。他の宛先に関する指定方法は以下の URL を見ると調べられます。

 

一点注意点があります。上記の GitHub の sample は、CloudWatch, Kinesis Firehose, Kinesis Strems の指定方法が古いです。クラスメソッドさんのブログでわかりやすく紹介されているので、こちらもご確認ください。

 

なお、上記の logConfiguration.options を指定することで、裏側では、Fluent Bit に関する設定変更が行われます。 logConfiguration.options で指定した内容が、そのまま Fluent Bit の OUTPUT セクションに反映されます。

OUTPUT セクションの実際の記載方法は次の URL で確認できます。

 

以下のように、logConfiguration.options で指定した内容が、各 Plugin の項目にそのまま一致していることがわかります。各 Plugin でどういったカスタマイズか可能か確認すると、知らなかった発見があるかもしれません。

image-20221015225029029.png

ログ送付先が 2種類以上の場合

ログの送付先が 2 種類以上ある場合は、Task 定義だけでは出来ません。その代わり、実際に Fluent Bit の設定ファイルを作成して、細かく送付先を指定することができます。

Dockerfile

  • Fluent Bit の設定ファイルを作成し、コンテナイメージ内に配置する
FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.28.3
COPY ./extra.conf /fluent-bit/etc/extra.conf

 

Task 定義

  • Task 定義内で、上記の独自の設定ファイルのパスを指定する
         "firelensConfiguration":{
            "type":"fluentbit",
            "options":{
               "config-file-type": "file",
               "config-file-value": "/fluent-bit/etc/extra.conf"
            }

 

この詳細な内容は、以下の記事をご確認ください。

Go のアプリケーション

ここまで簡単に FireLens を紹介してきました。実際に FireLens を動かしてみて、動作を確認してみましょう。

まず、Go 言語で適当にコンテナイメージを作ります。以下の 2 種類のログを出力する、超シンプルなコンテナイメージです。

  • error : エラーログ
  • info : 通常のログ

 

Go 言語のコード

package main

import (
	"fmt"
	"net/http"
	"os"

	log "github.com/sirupsen/logrus"
)

func init() {
	// JSONフォーマット
	log.SetFormatter(&log.JSONFormatter{})

	// 標準出力とする
	log.SetOutput(os.Stdout)

	// Infoレベル以上を出力
	log.SetLevel(log.InfoLevel)
}

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "OPTIONS,POST,GET")
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	w.WriteHeader(200)

	// 適当に文字列を返却する
	fmt.Fprintf(w, "Hello, I am FireLens Test App!")

	// Info と Warn のログを出力する
	log.Info("this is Info Log")
	log.Warn("this is Warn Log")
}

func main() {
	http.HandleFunc("/", handler) // ハンドラを登録してウェブページを表示させる
	http.ListenAndServe(":8080", nil)
}

 

ローカルで動かすと、こんな感じで 2 行のログが出力されます。

> go run app.go
{"level":"info","msg":"this is Info Log","time":"2022-10-15T16:21:20+09:00"}
{"level":"warning","msg":"this is Warn Log","time":"2022-10-15T16:21:20+09:00"}

Docker Build

Docker Image をビルドします。詳細は本記事のメインではないので、適当に読み飛ばしてもらって大丈夫です。

Dockerfile

FROM public.ecr.aws/docker/library/golang:1.17

RUN mkdir /workdir
COPY app /workdir

EXPOSE 8080
CMD ["/workdir/app"]

build

go build -o app
docker build -t xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-testapp:0.0.1 .

ローカルでテスト : ちゃんとログが出る

> docker run -it -p 80:8080 xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-testapp:0.0.1
{"level":"info","msg":"this is Info Log","time":"2022-10-15T07:54:42Z"}
{"level":"warning","msg":"this is Warn Log","time":"2022-10-15T07:54:42Z"}

docker push

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
docker push xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-testapp:0.0.1

Task 定義で FireLens の指定をする

Task 定義で、FireLens の指定をしていきます。新しい Console だと設定が出来ないので、古い画面で作成をしていきます。

image-20221015170557315.png

Task 定義の名前やロールは適当に指定します。

  • Task Role に FireLens から送付する先に対する権限を適切に設定する (CloudWatch や Kinesis などへの書き込み権限)

image-20221015190311234.png

FireLens の設定部分です。Log router integration をオンにして、fluentbit を選択して Apply を押します。

image-20221015172805914.png

Apply を押すことで、Task 定義の中に、log_router という名前のコンテナ定義が作成されます。

メインのアプリケーションを追加するために、Add container を押します。

image-20221015173053224.png

Docker Build を行ったアプリケーションを指定します。

image-20221015172353941.png

Log configuration にある、Log driver から、awsfirelens を選択します

注意点 : Log options にある Name の個所は、必ず × を押します。これを削除しないとエラーになります

image-20221015174940918.png

Add を押します

image-20221015173149713.png

次に、log_router を押します。

image-20221015175226655.png

log_router 側は、awslogs を使います。

image-20221015175407209.png

Configure via JSON を押します。

image-20221015184343469.png

この JSON 定義の中で、FireLens の次に転送する宛先を指定します。

image-20221015184628352.png

変更前

            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": null
            },

変更後

  • CloudWatch Logs に送付
            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": {
                    "Name": "cloudwatch_logs",
                    "region": "ap-northeast-1",
                    "log_group_name": "/aws/firelens/firelens-testapp",
                    "auto_create_group": "true",
                    "log_stream_prefix": "from-fluent-bit"
                }
            },

Save

image-20221015231155250.png

Create を押す

image-20221015185445862.png

Task Definition が作成されました

image-20221015173359479.png

Service の作成

適当に Service を作成

image-20221015173536592.png

ECS Exec の有効化したあとに、再度 Task を強制的に再配置。

aws ecs describe-services --cluster test-cluster01 --services firelens-testapp-service
aws ecs update-service --cluster test-cluster01 --service firelens-testapp-service --enable-execute-command
aws ecs describe-services --cluster test-cluster01 --services firelens-testapp-service

動作検証

ブラウザからアクセスして、実際のログを生成します。

image-20221015175740529.png

CloudWatch Logs の Log Group に、Task 定義で指定したものが自動的に作成されています。

image-20221015190622345.png

Log Stream も作成されています。

image-20221015190648597.png

アプリケーションにアクセスするたびに、info のログと warning のログが出力されていることがわかります。

  • アプリケーション側で出力されているログは、log の中に格納されています

image-20221015190811467.png

{
    "container_id": "ea73b448250c42e1a86bc367762a7ddf-787454787",
    "container_name": "mainapp",
    "ecs_cluster": "test-cluster01",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:xxxxx:task/test-cluster01/ea73b448250c42e1a86bc367762a7ddf",
    "ecs_task_definition": "firelens-testapp-task-def:3",
    "log": "{\"level\":\"info\",\"msg\":\"this is Info Log\",\"time\":\"2022-10-15T10:01:04Z\"}",
    "source": "stdout"
}
{
    "container_id": "ea73b448250c42e1a86bc367762a7ddf-787454787",
    "container_name": "mainapp",
    "ecs_cluster": "test-cluster01",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:xxxxx:task/test-cluster01/ea73b448250c42e1a86bc367762a7ddf",
    "ecs_task_definition": "firelens-testapp-task-def:3",
    "log": "{\"level\":\"warning\",\"msg\":\"this is Warn Log\",\"time\":\"2022-10-15T10:01:04Z\"}",
    "source": "stdout"
}

付録 : Fluent Bit 設定ファイルを確認

ECR Gallery のコンテナイメージを確認

Fluent Bit は、以下の ECR Gallery に公開されている。

この Dockerfile はここに公開されている。

ローカルで起動してみる

docker run -it --rm public.ecr.aws/aws-observability/aws-for-fluent-bit:latest bash

entrypoint

bash-4.2# cat /entrypoint.sh 
echo -n "AWS for Fluent Bit Container Image Version "
cat /AWS_FOR_FLUENT_BIT_VERSION
exec /fluent-bit/bin/fluent-bit -e /fluent-bit/firehose.so -e /fluent-bit/cloudwatch.so -e /fluent-bit/kinesis.so -c /fluent-bit/etc/fluent-bit.conf

conf の設定値

  • この設定値は、ECS 上で動かすときに、自動的に書き換わる
bash-4.2# cat /fluent-bit/etc/fluent-bit.conf 
[INPUT]
    Name        forward
    Listen      0.0.0.0
    Port        24224

[OUTPUT]
    Name cloudwatch
    Match   **
    region us-east-1
    log_group_name fluent-bit-cloudwatch
    log_stream_prefix from-fluent-bit-
    auto_create_group true

etc ディレクトリ

bash-4.2# ls -la /fluent-bit/etc/
total 36
drwxr-xr-x 2 root root  213 Oct 11 20:54 .
drwxr-xr-x 1 root root   22 Oct 11 20:55 ..
-rw-r--r-- 1 root root  251 Oct 11 18:02 fluent-bit.conf
-rw-r--r-- 1 root root 4664 Oct 11 20:54 parsers.conf
-rw-r--r-- 1 root root  584 Oct 11 20:54 parsers_ambassador.conf
-rw-r--r-- 1 root root  226 Oct 11 20:54 parsers_cinder.conf
-rw-r--r-- 1 root root 2798 Oct 11 20:54 parsers_extra.conf
-rw-r--r-- 1 root root  240 Oct 11 20:54 parsers_java.conf
-rw-r--r-- 1 root root  845 Oct 11 20:54 parsers_mult.conf
-rw-r--r-- 1 root root 2954 Oct 11 20:54 parsers_openstack.conf

ECS Exec で動作中の log_router から確認

Task Definition の定義は、デフォルトのまま。宛先は null なので、どこにも転送しない。

            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": null
            },

ECS Exec で、FireLens のコンテナ内で bash を起動。

> aws ecs execute-command \
          --cluster test-cluster01 \
          --task a30895fa37584a258e617e894ebe402d \
          --container log_router \
          --interactive \
          --command "bash"

entrypoint

bash-4.2# cat /entrypoint.sh 
echo -n "AWS for Fluent Bit Container Image Version "
cat /AWS_FOR_FLUENT_BIT_VERSION
exec /fluent-bit/bin/fluent-bit -e /fluent-bit/firehose.so -e /fluent-bit/cloudwatch.so -e /fluent-bit/kinesis.so -c /fluent-bit/etc/fluent-bit.conf

conf ファイル

  • ECR Gallery の内容から書き換わっている
  • INPUT として、TCP, Unix Socket が指定されている
  • FILTER で ECS クラスター名や、タスク名が入っている
  • デフォルトだと、OUTPUT の name は null となっており、FireLens のコンテナはどこにもログを送信しない
bash-4.2# cat /fluent-bit/etc/fluent-bit.conf

[INPUT]
    Name tcp
    Listen 127.0.0.1
    Port 8877
    Tag firelens-healthcheck

[INPUT]
    Name forward
    unix_path /var/run/fluent.sock

[INPUT]
    Name forward
    Listen 127.0.0.1
    Port 24224

[FILTER]
    Name record_modifier
    Match *
    Record ecs_cluster test-cluster01
    Record ecs_task_arn arn:aws:ecs:ap-northeast-1:xxxxxxxx:task/test-cluster01/a30895fa37584a258e617e894ebe402d
    Record ecs_task_definition firelens-testapp-task-def:2

[OUTPUT]
    Name null
    Match firelens-healthcheck

ECS Exec で動作中の log_router から確認 : 宛先 CloudWatch

CloudWatch に転送する内容をタスク定義に指定

            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": {
                    "Name": "cloudwatch_logs",
                    "region": "ap-northeast-1",
                    "log_group_name": "/aws/firelens/firelens-testapp",
                    "auto_create_group": "true",
                    "log_stream_prefix": "from-fluent-bit"
                }
            },

ECS Exec で、FireLens のコンテナ内で bash を起動。

> aws ecs execute-command \
          --cluster test-cluster01 \
          --task e9ecc683995745b8a61c6a56540a1108 \
          --container log_router \
          --interactive \
          --command "bash"

以下の設定が自動設定されている

  • INPUT として、TCP, Unix Socket が指定されている

  • CloudWatch Logs に送付する OUTPUT セクションが自動生成されている

bash-4.2# cat /fluent-bit/etc/fluent-bit.conf

[INPUT]
    Name tcp
    Listen 127.0.0.1
    Port 8877
    Tag firelens-healthcheck

[INPUT]
    Name forward
    unix_path /var/run/fluent.sock

[INPUT]
    Name forward
    Listen 127.0.0.1
    Port 24224

[FILTER]
    Name record_modifier
    Match *
    Record ecs_cluster test-cluster01
    Record ecs_task_arn arn:aws:ecs:ap-northeast-1:xxxxxxxx:task/test-cluster01/e9ecc683995745b8a61c6a56540a1108
    Record ecs_task_definition firelens-testapp-task-def:4

[OUTPUT]
    Name null
    Match firelens-healthcheck

[OUTPUT]
    Name cloudwatch_logs
    Match mainapp-firelens*
    auto_create_group true
    log_group_name /aws/firelens/firelens-testapp
    log_stream_prefix from-fluent-bit
    region ap-northeast-1

検証を通じてわかったこと

  • アプリケーションを動かすメインコンテナ側のログ設定を、マネージメントコンソールで行うときに、かならず Name の欄の × を押す。これを押さないとエラーになる
    • Task Definition は、CLI だったり、CDK だったり、 ecspresso などで作るのが楽だと思う
      image-20221015174940918 - コピー.png
  • Task Definition の、logConfiguration にある Options に指定する文字列が、FluentBit の [OUTPUT] にそのまま反映される
    image-20221015225029029 - コピー.png
  • アプリケーションで出力したログは、メタデータが付与された形で転送される。
    • 実際のログのメッセージは、log に格納される
      image-20221015190811467 - コピー.png
  • Fluent Bit は、ECS 上で起動されるタイミングで、conf ファイルが自動的に書き換わる。ECR Gallary で公開されているイメージを、ローカルで確認してもよくわからないので、ECS Exec で中身を確認するとわかりやすい

参考URL

25
6
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
25
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?