背景
FargateのログドライバはCloudWatch Logs,Splunkしか使用できず、その他のログ基盤に連携を行いたい場合はCloudWatch Logsから転送するか(CloudWatch Logsの料金がかかる)、ログドライバを使用せず共有ボリュームにログファイルを出力してサイドカーのFluentdで収集する(ボリュームは8GBまで)といろいろと制約があり、なかなかFargateに移行ができないでいました。
この度、FireLensがGAになり、自前のFluentdに転送できるとのことだったので試してみたところ、色々と詰まりましたのでメモとして残します。
開発
0. FireLensについて
クラスメソッド様の記事がとてもわかり易いと思います。
1. 概要
作成、使用した環境はGitHubにあります。
構成としてはFargateでnginxのログをFirelens(fluentd)を使い、S3に出力しています。
nginx -> firelens(fluentd) -> S3
2. パッケージ構成
ざっくりとしたパッケージ構成とその説明です。
.
├── fluentd                         
│   ├── conf.in                 <-- fluentdのcofigディレクトリ。input情報を書いている
│   ├── conf.out                <-- fluentdのcofigディレクトリ。output情報を書いている
│   ├── Dockerfile              <-- fluentdのDockerfile
│   ├── build.sh                <-- Dockerfileでbuildし、ECRにpushしています
│   └── fluent.conf             <-- fluentdのcofigファイル。conf.inとconf.outをincludeしている
├── nginx                    
│   ├── Dockerfile              <-- nginxのDockerfile
│   └── build.sh                <-- Dockerfileでbuildし、ECRにpushしています
├── terraform                   <-- AWS環境一式をterraform化してあります 
└── README.md
3. メモ
fluentdのDockerfileです。
firelensに対応するため、COPY fluent.conf /fluentd/etc/fluent-custom.confと名前を変更しています。これは後述のtask-definition.jsonで使用するためです。
FROM fluent/fluentd:v1.7-1
# Use root account to use apk
USER root
# below RUN includes plugin as examples elasticsearch is not required
# you may customize including plugins as you wish
RUN apk add --no-cache --update --virtual .build-deps \
        sudo build-base ruby-dev \
 && sudo gem install fluent-plugin-rewrite-tag-filter -v 2.2.0 \
 && sudo gem install fluent-plugin-s3 -v 1.1.11 \
 && sudo gem sources --clear-all \
 && apk del .build-deps \
 && rm -rf /tmp/* /var/tmp/* /usr/lib/ruby/gems/*/cache/*.gem
COPY fluent.conf /fluentd/etc/fluent-custom.conf
COPY conf.in      /fluentd/etc/conf.in
COPY conf.out     /fluentd/etc/conf.out
USER fluent
次にfluent.confですが、input情報である<source>の記述を消しています。理由は記述するともうポートが空いているとerrorになるため削除しています。おそらく、AWS側で記述があるためだと思われます。
<match>
  @type relabel
  @label @firelens_log
  @id    in_forward_all
</match>
@include conf.in/*.conf
@include conf.out/*.conf
最後にtask-definition.jsonですが、
- 
config-file-type->file - 
config-file-value->/fluentd/etc/fluent-custom.conf 
としています。fluent.confと記述してしまうと予約後なのか、errorになるため、このような記述になります。
公式ドキュメントには特に記述は見当たりませんでした。
[
  {
    "name": "nginx",
    "image": "xxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/infra/nginx-1.17.4:latest",
    "cpu": 256,
    "memory": 512,
    "essential": true,
    "portMappings": [
      {
        "containerPort": 80,
        "protocol": "tcp",
        "hostPort": 80
      }
    ],
    "logConfiguration": {
      "logDriver": "awsfirelens"
    }
  },
  {
    "name": "fluentd",
    "image": "xxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/infra/fluentd-1.7:latest",
    "cpu": 256,
    "memory": 512,
    "essential": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/firelens-fluentd",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "firelensConfiguration": {
      "type": "fluentd",
      "options": {
        "config-file-type": "file",
        "config-file-value": "/fluentd/etc/fluent-custom.conf"
      }
    }
  }
]
他に気をつけることとしては、tagがコンテナ名-firelens-コンテナIDとハイフン区切りで渡されてきます。
fluentdはドット区切りなため、rewrite_tag_filterを使い、正規表現でtagをrewriteしています。
ソースコードを見たのですが、こちらから上書きする手段はなさそうです。
Fluent Bitだったらドット区切りではないため、困らないからでしょう。
ECSのfluentdログドライバだったらoptionでtagを渡せたため、そうしてくれるとありがたいと思いました。
<label @firelens_log>
  <filter>
    @type record_transformer
    <record>
    tag ${tag}
    </record>
  </filter>
  <match>
    @type rewrite_tag_filter
    @label @create_tag
    <rule>
      key tag
      pattern /^nginx-firelens-(\w.+)$/
      tag "nginx.sample.$1"
    </rule>
  </match>
</label>
〜〜省略〜〜
まとめ
色々と書きましたが、十分本番環境に耐えられる状態として提供されたのではないでしょうか。
既存のログ基盤がすでにあるサービスにとっては、Fargateへの移行に関しての懸念事項がまた一つ消えたと思います。