ECS FargateのログをSplunkに送る

# はじめに

先日、[ECS (EC2タイプ) のログをSplunkに送る記事](https://qiita.com/kikeyama/items/e92befb361565ff61b03) を書きました
今回はFargateにデプロイしたアプリケーションコンテナのログをSplunkに送る方法について書きます

EC2 ECSとはちょっと勝手が違うためハマった点や注意点について書いていきます

# 概要

FargateもEC2 ECSと同様にSplunkのLog Driverが利用可能ですが、再送処理だったり複数行のログの扱いだったりで、自分の思い通りにログをとれないことがあったので **FireLens** と **Fluentd** でログをSplunkに送ります

# 設定方法

以下の手順で設定します

## 1. SplunkでHTTP Event Collectorを有効化

設定方法は [ここらへんの記事](https://qiita.com/kikeyama/items/bb609cb90269019962cb#http-event-collector%E8%A8%AD%E5%AE%9A) を参考にしてください

## 2. log_routerコンテナ追加

今回はJava Spring Bootのデモ用アプリケーションを適当に作ってFargateにデプロイしてみます
ログを取りたいサービスが入っているタスク定義に `log_router` コンテナをFireLensのサイドカーとして追加して登録します

```yaml:kikeyama-services-fargate-task.yaml
containerDefinitions:
- cpu: 0
  dependsOn:
  - condition: START
    containerName: log_router
  dockerLabels:
    app: spring-sfx-demo
  essential: true
  image: ************.dkr.ecr.**-****-*.amazonaws.com/kikeyama-spring-sfx-demo:latest
  logConfiguration:
    logDriver: awsfirelens
    options:
      '@label': '@CONCAT'
      '@type': relabel
  memoryReservation: 512
  mountPoints: []
  name: kikeyama-spring-sfx-demo
  portMappings:
  - containerPort: 8080
    hostPort: 8080
    protocol: tcp
  volumesFrom: []
- cpu: 0
  environment: []
  essential: true
  firelensConfiguration:
    type: fluentd
    options:
      config-file-type: file
      config-file-value: /mnt/fluent.conf
  image: splunk/fluentd-hec:1.2.0
  logConfiguration:
    logDriver: awslogs
    options:
      awslogs-group: /ecs/kikeyama-sfx-demo-services-fargate
      awslogs-region: **-****-*
      awslogs-stream-prefix: fargate
  mountPoints:
  - containerPath: /mnt
    readOnly: true
    sourceVolume: efs_volume
  name: log_router
  portMappings: []
  user: '0'
  volumesFrom: []
cpu: '1024'
executionRoleArn: arn:aws:iam::************:role/fargateTaskExecutionRole
family: kikeyama-sfx-demo-services-fargate
memory: '2048'
networkMode: awsvpc
placementConstraints: []
requiresCompatibilities:
- FARGATE
volumes:
- efsVolumeConfiguration:
    authorizationConfig:
      iam: DISABLED
    fileSystemId: fs-********
    rootDirectory: /
    transitEncryption: DISABLED
  name: efs_volume
```

```bash
# タスク定義を登録
aws ecs register-task-definition --cli-input-yaml file://./kikeyama-services-fargate-task.yaml
```

## ECSとの違い

### 1. Fargateでは `config-file-type` にS3を指定できない

FargateではFluentdの設定ファイル `fluent.conf` をS3から取得できないので、EFSでマウントしたファイルシステムから読み込みます
今回はS3に配置したfluent.confをDataSync通してEFSに同期しました
なので、設定の変更はS3にアップロードしてDataSyncを動かして反映します
`aws` CLIで処理できるので楽でした

ここらへんの設定は本題から外れてしまうので割愛しますが、ヒマなときにでも記事を書くかもしれません
といってもAWS関連は他の方がたくさん記事書いてるので、ググればいろいろ情報出てくるんだけどね

### 2. FargateではLog Driverのオプションに `@type` 指定必須

これはかなり悶々とハマりました
アプリケーションコンテナの↓の部分です

```yaml:アプリケーションコンテナのログ設定抜粋
  logConfiguration:
    logDriver: awsfirelens
    options:
      '@label': '@CONCAT'
      '@type': relabel
```

ちなみに `@type` ブランクにしたら↓のメッセージで怒られました

```
2020-07-10 05:38:36 +0000 [info]: parsing config file is succeeded path="/fluentd/etc/fluent.conf"
2020-07-10 05:38:36 +0000 [error]: config error file="/fluentd/etc/fluent.conf" error_class=Fluent::ConfigError error="Unknown output plugin ''. Run 'gem search -rd fluent-plugin' to find plugins"

```

EC2 ECSの場合は `options` 省略可だったのですけどね

```yaml:ECSの場合はoptions省略可
  logConfiguration:
    logDriver: awsfirelens
```

この制約に伴って、fluent.confも少しEC2 ECSとは違ってきます

```conf:fluent.conf
<system>
  log_level info
</system>

<label @CONCAT>
  <filter **spring-sfx-demo**>
    @type concat
    key log
    #stream_identity_key container_id
    stream_identity_key stream
    multiline_start_regexp /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d{3}/
    flush_interval 5s
    timeout_label @SPLUNK
    #separator ""
    use_first_timestamp true
  </filter>
  
  <match **>
    @type relabel
    @label @SPLUNK
  </match>
</label>

<label @SPLUNK>
  <match **>
    @type splunk_hec
    protocol SPLUNK_HEC_SCHEME(http|https)
    hec_host SPLUNK_HEC_HOST
    hec_port SPLUNK_HEC_PORT
    hec_token SPLUNK_HEC_TOKEN
    index SPLUNK_INDEX
    host_key ec2_instance_id
    source_key ecs_cluster
    sourcetype_key ecs_task_definition
    insecure_ssl true
    <fields>
      container_id
      container_name
      ecs_task_arn
      source
    </fields>
    <format>
      @type single_value
      message_key log
      add_newline false
    </format>
  </match>
</label>

<label @ERROR>
  <match **>
    @type relabel
    @label @SPLUNK
  </match>
</label>
```

何が違うかっていうと、 `<filter>` と `<match>` をlabel `@CONCAT` で括ってるんですね
タスク定義で `@type` 指定する必要があったので、 `relabel` としてこの `@CONCAT` の処理をさせてます

Fluentd初心者なのでここらへんちょっと苦労しました

あとはこの設定ファイルをS3に置いてDataSync動かすだけです

## 3. サービス更新

ステップ2で作成したタスク定義を指定してサービスを動かせば、Java SpringのログはSplunkに送られます

# Splunkでログ確認

ということでSplunkにログが送られてきてるのを確認できました
複数行のログもまとまってます

![fargate_firelens_logs_in_splunk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174837/1b3279b6-ca75-13bb-f91b-336d90ca9876.png)

# さいごに

[ドキュメント](https://github.com/splunk/splunk-connect-for-kubernetes/blob/develop/firelens/README.md) にしたがって設定したのですがLog Driverのオプションに `@typ` 必須というところは記載がなく、何度ドキュメント通りに設定しても原因不明のエラーになってファッ！？となってました
かなり苦戦しました

とはいえ、[前回の記事](https://qiita.com/kikeyama/items/e92befb361565ff61b03) も含めてECSはEC2タイプもFargateもログをSplunkに送って無事検索したり可視化できるのでひと安心です