EKS Worker NodeのkubeletログをFluentdで収集しCloudWatch Logsに集約させる

# はじめに

EKSに立てたPodのログは標準出力に吐き出すようにしてFluentdで収集するのが楽な方法です。
Worker Nodeのkubeletのログはjournalログであり次のコマンドで確認できます。

```console
$ journalctl -u kubelet > kubelet.log
```

今回はEKS worker nodeのkubeletのログをFluentdで収集しCloudWatch Logsに集約させる方法を説明します。

# 環境情報
MacOS Mojave 10.14.6
EKS 1.16
Helm 3.4.0
Helmfile 0.122.0

# 前提
* EKSクラスタが構築されていること。
* kube2iamがデプロイされていること。

# 手順

Helmfileで[fluentd-cloudwatch](https://github.com/helm/charts/tree/master/incubator/fluentd-cloudwatch)をデプロイします。
このHelm Chartで使用されるFluentdのDockerイメージにはfluent-plugin-systemdプラグインが同梱されているためjournalログを収集することができます。

Helmfileの設定ファイルは次のようになります。

```yaml:helmfile.yaml
repositories:
  - name: incubator
    url: https://charts.helm.sh/incubator

releases:
  - name: fluentd-cloudwatch
    namespace: kube-system
    chart: incubator/fluentd-cloudwatch
    version: 0.13.0
    values:
      - podAnnotations:
          iam.amazonaws.com/role: arn:aws:iam::XXXXXXXXXXXX:role/cloud-watch-logs-full-access-role
        awsRegion: ap-northeast-1
        rbac:
          create: true
        extraVars:
          - "{ name: FLUENT_UID, value: '0' }"
        data:
          fluent.conf: |
            <match fluent.**>
              @type null
            </match>

            <source>
              @type tail
              enable_stat_watcher false
              path /var/log/containers/*.log
              pos_file /var/log/fluentd-containers.log.pos
              time_format %Y-%m-%dT%H:%M:%S.%NZ
              tag kubernetes.*
              format json
              read_from_head true
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format /^(?<time>[^ ]* [^ ,]*)[^\[]*\[[^\]]*\]\[(?<severity>[^ \]]*) *\] (?<message>.*)$/
              time_format %Y-%m-%d %H:%M:%S
              path /var/log/salt/minion
              pos_file /var/log/fluentd-salt.pos
              tag salt
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format syslog
              path /var/log/startupscript.log
              pos_file /var/log/fluentd-startupscript.log.pos
              tag startupscript
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format /^time="(?<time>[^)]*)" level=(?<severity>[^ ]*) msg="(?<message>[^"]*)"( err="(?<error>[^"]*)")?( statusCode=($<status_code>\d+))?/
              path /var/log/docker.log
              pos_file /var/log/fluentd-docker.log.pos
              tag docker
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format none
              path /var/log/etcd.log
              pos_file /var/log/fluentd-etcd.log.pos
              tag etcd
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/kubelet.log
              pos_file /var/log/fluentd-kubelet.log.pos
              tag kubelet
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/kube-proxy.log
              pos_file /var/log/fluentd-kube-proxy.log.pos
              tag kube-proxy
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/kube-apiserver.log
              pos_file /var/log/fluentd-kube-apiserver.log.pos
              tag kube-apiserver
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/kube-controller-manager.log
              pos_file /var/log/fluentd-kube-controller-manager.log.pos
              tag kube-controller-manager
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/kube-scheduler.log
              pos_file /var/log/fluentd-kube-scheduler.log.pos
              tag kube-scheduler
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/rescheduler.log
              pos_file /var/log/fluentd-rescheduler.log.pos
              tag rescheduler
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/glbc.log
              pos_file /var/log/fluentd-glbc.log.pos
              tag glbc
            </source>

            <source>
              @type tail
              enable_stat_watcher false
              format kubernetes
              multiline_flush_interval 5s
              path /var/log/cluster-autoscaler.log
              pos_file /var/log/fluentd-cluster-autoscaler.log.pos
              tag cluster-autoscaler
            </source>

            <source>
              @type systemd
              tag kubelet
              path /var/log/journal
              matches [{ "_SYSTEMD_UNIT": "kubelet.service" }]
              read_from_head true

              <storage>
                @type local
                path /var/log/fluentd-journald-kubelet-cursor.json
              </storage>

              <entry>
                fields_strip_underscores true
                fields_lowercase true
              </entry>
            </source>

            <filter kubernetes.**>
              @type kubernetes_metadata
            </filter>

            <match **>
              @type cloudwatch_logs
              log_group_name "#{ENV['LOG_GROUP_NAME']}"
              auto_create_stream true
              use_tag_as_stream true
            </match>
```

valuesの内容について説明していきます。

Podに付与するIAM RoleのARNを記述します。CloudWatch LogsにフルアクセスできるRoleを付与しています。

```yaml
podAnnotations:
  iam.amazonaws.com/role: arn:aws:iam::XXXXXXXXXXXX:role/cloud-watch-logs-full-access-role
```

Fluentdの設定の中でkubeletログの収集は以下の部分になります。

```yaml
data:
  fluent.conf: |
    <source>
      @type systemd
      tag kubelet
      path /var/log/journal
      matches [{ "_SYSTEMD_UNIT": "kubelet.service" }]
      read_from_head true

      <storage>
        @type local
        path /var/log/fluentd-journald-kubelet-cursor.json
      </storage>

      <entry>
        fields_strip_underscores true
        fields_lowercase true
      </entry>
    </source>
```


次のコマンドでデプロイします。

```bash
$ helmfile apply -f ./helmfile.yaml
```

# 確認

実際にCloudWatch Logsで確認していきます。

![スクリーンショット 2020-08-29 17.25.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/b66d755b-4413-fb6e-b17c-301000dc267e.png)

確かにkubeletのログが収集できていることが分かります。

![スクリーンショット 2020-08-29 17.28.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/0213b497-bafe-9444-41d6-f6dda51510cb.png)


# まとめ

Fluentdを使用してEKS Worker Nodeのkubeletログを収集してCloudWatch Logsに集約できることを確認しました。
EBSのバーストバランスといったAWSリソースが原因で起こる不具合を調査するときにkubeletログが必要になることはあるので取れるものは取っておいたほうがいいです。

今回は触れていませんが、EKS Masterのログ収集についてはEKSクラスタ作成時にオプションで選択できます。