EKS -> Cloudwatchのログ転送を FluentD から Fluent Bit に移行する

移行しました

https://zenn.dev/k5trismegistus/articles/922715b2ad4e13

## サマリー

EKSのアプリケーションログをCloudwatchに転送するのに使っていたFluentDをFluent Bitに置き換えました。
この時に、公式ドキュメントだけでは痒い所に手が届かない情報しか載っていなかったので苦労した部分を共有します。

## FluentDからFluent Bitへの移行が推奨されています

数年前にEKSクラスタ上で動かすアプリケーションのログを、FluentDを使ってCloudwatchに集約できるようにしました。その時の記事は↓↓↓です。

https://qiita.com/k5trismegistus/items/9675d90a22746dd030cf

この設定をしてから約3年半ずっとログはこれで取れていました。
ところが先日、EKSのKubernetesのバージョンを1.22から1.25まで上げたところ、ログがとれない状態に。
原因を調べている中で、今はもうFluentDを使うことが非推奨になっていることを知ります。
ログがとれない原因究明は時間の無駄になりそうなのでやめて、先にFluent Bitへの移行を進めることにしました。

>Container Insights の FluentD のサポートは現在メンテナンスモードになっています。つまり、AWS はこれ以上 FluentD の更新を提供せず、近い将来に非推奨にする予定です。
> (中略)
> 可能な限り Container Insights で FluentBit を使用するように移行することを強くお勧めします。Container Insights のログフォワーダとして FluentBit を使用すると、パフォーマンスが大幅に向上します。

[(オプション) CloudWatch Logs へログを送信する DaemonSet として FluentD を設定する](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Container-Insights-setup-logs.html)

## Fluent Bitへの移行について

### 移行手順

基本的には公式ドキュメント

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Container-Insights-setup-logs-FluentBit.html

がベースになりますが、ちょっとこれだけだと融通がきかないです。

公式ドキュメントでは、Fluent Bit用の設定ファイルを直接適用するように書かれています。

> 次のいずれかのコマンドを実行して、Fluent Bit daemonset をクラスターにダウンロードしてデプロイします。

しかし、このファイルをそのまま適用すると不便なところがあります。そこで、いきなり適用するのではなく手を入れることにします。

ドキュメントには「より Fluentd に似ている Fluent Bit の設定」という候補があるのですが、少なくとも私の環境では全く似ていなかったので、「Fluent Bit 最適化設定」の方を使いました。

まずは、次のファイルをダウンロードしましょう。

https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/fluent-bit/fluent-bit.yaml

### ログのパース方法を変える

デフォルトでは、ログは「CRI形式」で出力されます。
これはAWSの配っているサンプルの設定では、単なるテキストとして集約されます。

![before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133157/48d807a5-e0bb-9de9-1fa0-b6392936cfa2.png)

なのでこれをパースして、構造化されたデータにします。

![after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133157/c8d0477f-ad65-0dac-2391-2ab5a9f1717c.png)

`parsers.conf`を設定している部分に
```yaml
[PARSER]
    Name        cri
    Format      regex
    Regex       ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
    Time_Key    time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
```
というのを足します。

そして、`application-log.conf`を設定している部分で、`docker`を利用している部分を全て`cri`に置き換えます。

```yaml
Parser              docker
```
を
```yaml
Parser              cri
```
に。

参考リンク：
https://progret.hatenadiary.com/entry/2022/04/24/102737

### ログストリーム名を変える

「Fluent Bit 最適化設定」では、デフォルトで

> kubernetes-nodeName-application.var.log.containers.kubernetes-podName_kubernetes-namespace_kubernetes-container-name-kubernetes-containerID

という名前になります。

私の希望としては、Podを跨いで同じアプリケーションのログは同じログストリームに集約したいです。エラーログを確認したいときにコンテナのIDごとにログストリームが分かれていると探すのが大変だからです。（本来ならElasticsearchとかで集約すべきなんでしょうか？）

```yaml
    [OUTPUT]
        Name                cloudwatch_logs
        Match               application.*
        region              ${AWS_REGION}
        log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application
        log_stream_prefix   ${HOST_NAME}-
        log_stream_template $kubernetes['labels'] ['app'].$kubernetes['namespace_name'] // ここ！
        auto_create_group   true
        extra_user_agent    container-insights
```

答えとしては、`log_stream_template`というのを指定します。
ただし、フォールバック用に`log_stream_name`または`log_stream_prefix`の指定を残しておく必要はあります。

`log_stream_template`は、`Record Accessor`という書き方をすることになります。

https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/record-accessor

ドキュメント中には記載がないのですが、ハイフンの使用には制限があるようです。
本来なら、`アプリ名`-`環境名`でログを集約したかったのですが、`アプリ名`.`環境名`としました。

## まとめ

そして最後に、

`kubectl apply -f fluent-bit.yaml`

で設定を適用すれば、望むログストリーム名でパースされたログがFluent BitによってCloudwatchに転送されます。

あとは、FluentDのDaemonSetを消しましょう。

----

お仕事のご相談はこちらから

https://k5trismegistus.me/works