EKS の Amazon CloudWatch Observability アドオンを利用して Fluent Bit をインストールした際に設定ファイルをカスタマイズする

EKS の Amazon CloudWatch Observability アドオンを利用して Fluent Bit をインストールした際に設定ファイルをカスタマイズする

EKS を利用する際、amazon-cloudwatch-observability アドオンを利用することでプリセットされた EKS モニタリング用のダッシュボードが使えます。
また、このアドオンをインストールすると Fluent Bit もインストール可能です。

https://dev.classmethod.jp/articles/eks-on-ec2-container-insights-with-enhanced-observability/

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Observability-EKS-addon.html

今回は amazon-cloudwatch-observability アドオン経由で Fluent Bit をインストールした際に設定をカスタマイズしてみます。

アドオンインストール時のデフォルト設定

amazon-cloudwatch-observability アドオン v1.7.0 をインストールした際のデフォルト設定は下記でした。

containerLogs:
  fluentBit:
    config:
      service: |
        [SERVICE]
          Flush                     5
          Grace                     30
          Log_Level                 error
          Daemon                    off
          Parsers_File              parsers.conf
          storage.path              /var/fluent-bit/state/flb-storage/
          storage.sync              normal
          storage.checksum          off
          storage.backlog.mem_limit 5M
      customParsers: |
        [PARSER]
          Name                syslog
          Format              regex
          Regex               ^(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
          Time_Key            time
          Time_Format         %b %d %H:%M:%S

        [PARSER]
          Name                container_firstline
          Format              regex
          Regex               (?<log>(?<="log":")\S(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
          Time_Key            time
          Time_Format         %Y-%m-%dT%H:%M:%S.%LZ

        [PARSER]
          Name                cwagent_firstline
          Format              regex
          Regex               (?<log>(?<="log":")\d{4}[\/-]\d{1,2}[\/-]\d{1,2}[ T]\d{2}:\d{2}:\d{2}(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
          Time_Key            time
          Time_Format         %Y-%m-%dT%H:%M:%S.%LZ
      extraFiles:
        application-log.conf: |
          [INPUT]
            Name                tail
            Tag                 application.*
            Exclude_Path        /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
            Path                /var/log/containers/*.log
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_container.db
            Mem_Buf_Limit       50MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Rotate_Wait         30
            storage.type        filesystem
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 application.*
            Path                /var/log/containers/fluent-bit*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_log.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 application.*
            Path                /var/log/containers/cloudwatch-agent*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_cwagent.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                kubernetes
            Match               application.*
            Kube_URL            https://kubernetes.default.svc:443
            Kube_Tag_Prefix     application.var.log.containers.
            Merge_Log           On
            Merge_Log_Key       log_processed
            K8S-Logging.Parser  On
            K8S-Logging.Exclude Off
            Labels              Off
            Annotations         Off
            Use_Kubelet         On
            Kubelet_Port        10250
            Buffer_Size         0

          [OUTPUT]
            Name                cloudwatch_logs
            Match               application.*
            region              ${AWS_REGION}
            log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application
            log_stream_prefix   ${HOST_NAME}-
            auto_create_group   true
            extra_user_agent    container-insights
        dataplane-log.conf: |
          [INPUT]
            Name                systemd
            Tag                 dataplane.systemd.*
            Systemd_Filter      _SYSTEMD_UNIT=docker.service
            Systemd_Filter      _SYSTEMD_UNIT=containerd.service
            Systemd_Filter      _SYSTEMD_UNIT=kubelet.service
            DB                  /var/fluent-bit/state/systemd.db
            Path                /var/log/journal
            Read_From_Tail      ${READ_FROM_TAIL}

          [INPUT]
            Name                tail
            Tag                 dataplane.tail.*
            Path                /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_dataplane_tail.db
            Mem_Buf_Limit       50MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Rotate_Wait         30
            storage.type        filesystem
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                modify
            Match               dataplane.systemd.*
            Rename              _HOSTNAME                   hostname
            Rename              _SYSTEMD_UNIT               systemd_unit
            Rename              MESSAGE                     message
            Remove_regex        ^((?!hostname|systemd_unit|message).)*$

          [FILTER]
            Name                aws
            Match               dataplane.*
            imds_version        v2

          [OUTPUT]
            Name                cloudwatch_logs
            Match               dataplane.*
            region              ${AWS_REGION}
            log_group_name      /aws/containerinsights/${CLUSTER_NAME}/dataplane
            log_stream_prefix   ${HOST_NAME}-
            auto_create_group   true
            extra_user_agent    container-insights
        host-log.conf: |
          [INPUT]
            Name                tail
            Tag                 host.dmesg
            Path                /var/log/dmesg
            Key                 message
            DB                  /var/fluent-bit/state/flb_dmesg.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 host.messages
            Path                /var/log/messages
            Parser              syslog
            DB                  /var/fluent-bit/state/flb_messages.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 host.secure
            Path                /var/log/secure
            Parser              syslog
            DB                  /var/fluent-bit/state/flb_secure.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                aws
            Match               host.*
            imds_version        v2

          [OUTPUT]
            Name                cloudwatch_logs
            Match               host.*
            region              ${AWS_REGION}
            log_group_name      /aws/containerinsights/${CLUSTER_NAME}/host
            log_stream_prefix   ${HOST_NAME}.
            auto_create_group   true
            extra_user_agent    container-insights

charts/amazon-cloudwatch-observability/values.yaml

デフォルト設定だと 3 つの CloudWatch ロググループが作成され、下記のように別れてログ出力されます。

各種アプリケーションログ: /aws/containerinsights/<ClusterName>/application
kubelet, kube-proxy など Kubernetes のノードコンポーネントのログ: /aws/containerinsights/<ClusterName>/dataplane
システム起動時のログなど、EC2 ホストの OS ログ: /aws/containerinsights/<ClusterName>/host

今回はアプリケーションのログを CloudWatch と S3 の両方に出力するように、ノードコンポーネントログと OS ログを S3 に出力するように変更します。

アドオン追加

EKS クラスターを作成して、アドオンが利用するための S3 と CloudWatch の権限を付与します。
現状は Node のロールに追加するか、IRSA を利用することができます。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Observability-EKS-addon.html

各種アドオンは Pod Identity での権限付与が推奨されておりますが、amazon-cloudwatch-observability アドオンは未だに未対応です。
aws-for-fluent-bit が Pod Identity に対応していなかったためと推測されますが、直近こちらは対応しました。

https://github.com/amazon-contributing/upstream-to-fluent-bit/pull/6

amazon-cloudwatch-observability アドオンも近い内に Pod Identity 対応すると思われます。

今回は IRSA を利用することとして、Terraform で下記のように定義しました。

locals {
  cluster_name                  = "test-cluster"
  k8s_service_account_namespace = "amazon-cloudwatch"
  k8s_service_account_name      = "cloudwatch-agent"
}


module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.31.6"

  cluster_name                   = local.cluster_name
  cluster_version                = "1.31"
  cluster_endpoint_public_access = true

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  enable_irsa = true

  enable_cluster_creator_admin_permissions = true

  cluster_compute_config = {
    enabled    = true
    node_pools = ["general-purpose", "system"]
  }
}

resource "aws_s3_bucket" "eks_log_bucket" {
  bucket = "masukawa-eks-log-bucket"
}

data "aws_iam_policy_document" "put_s3" {
  statement {
    effect = "Allow"
    actions = [
      "s3:PutObject",
    ]
    resources = ["${aws_s3_bucket.eks_log_bucket.arn}/*"]
  }
}

resource "aws_iam_policy" "put_s3" {
  name        = "put-s3-policy"
  description = "IAM policy for putting objects to S3"
  policy      = data.aws_iam_policy_document.put_s3.json
}

module "iam_assumable_role_admin" {
  source                        = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
  version                       = "3.6.0"
  create_role                   = true
  role_name                     = "AmazonCloudWatchObservabilityAddonRole"
  provider_url                  = replace(module.eks.cluster_oidc_issuer_url, "https://", "")
  role_policy_arns              = [aws_iam_policy.put_s3.arn, "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"]
  oidc_fully_qualified_subjects = ["system:serviceaccount:${local.k8s_service_account_namespace}:${local.k8s_service_account_name}"]
}

アドオン追加はマネジメントコンソールから行います。

スクリーンショット 2025-01-17 21.21.50.png

アドオンのみ Terraform 管理外にしたのは、高度な設定を行う場合はマネジメントコンソールから yaml ベースで設定するのが一番楽に感じたためです。

スクリーンショット 2025-01-17 23.25.27.png

Fluent Bit の設定ファイルは長くなりがちなので configuration_values に JSON ベースで設定するのが少し面倒でした。
この辺りが気になる場合は、コンテナログの収集をオプトアウトしておいて Helm ベースで入れた方が楽かもしれません。

--configuration-values '{ "containerLogs": { "enabled": false } }'

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Observability-EKS-addon.html

設定をカスタマイズしてみる

設定を変更していきます。
application-log.conf の OUTPUT に下記を追加します(バケット名は適宜変更する必要があります)。

          [OUTPUT]
            Name                s3
            Match               *
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz

dataplane-log.conf の OUTPUT を下記に変更します。

          [OUTPUT]
            Name                s3
            Tag                 dataplane.*
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz

host-log.conf の OUTPUT も下記に変更します。

          [OUTPUT]
            Name                s3
            Tag                 host.*
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz

全体として高度な設定は下記になりました。

containerLogs:
  fluentBit:
    config:
      service: |
        [SERVICE]
          Flush                     5
          Grace                     30
          Log_Level                 error
          Daemon                    off
          Parsers_File              parsers.conf
          storage.path              /var/fluent-bit/state/flb-storage/
          storage.sync              normal
          storage.checksum          off
          storage.backlog.mem_limit 5M
      customParsers: |
        [PARSER]
          Name                syslog
          Format              regex
          Regex               ^(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
          Time_Key            time
          Time_Format         %b %d %H:%M:%S

        [PARSER]
          Name                container_firstline
          Format              regex
          Regex               (?<log>(?<="log":")\S(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
          Time_Key            time
          Time_Format         %Y-%m-%dT%H:%M:%S.%LZ

        [PARSER]
          Name                cwagent_firstline
          Format              regex
          Regex               (?<log>(?<="log":")\d{4}[\/-]\d{1,2}[\/-]\d{1,2}[ T]\d{2}:\d{2}:\d{2}(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
          Time_Key            time
          Time_Format         %Y-%m-%dT%H:%M:%S.%LZ
      extraFiles:
        application-log.conf: |
          [INPUT]
            Name                tail
            Tag                 application.*
            Exclude_Path        /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
            Path                /var/log/containers/*.log
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_container.db
            Mem_Buf_Limit       50MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Rotate_Wait         30
            storage.type        filesystem
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 application.*
            Path                /var/log/containers/fluent-bit*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_log.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 application.*
            Path                /var/log/containers/cloudwatch-agent*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_cwagent.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                kubernetes
            Match               application.*
            Kube_URL            https://kubernetes.default.svc:443
            Kube_Tag_Prefix     application.var.log.containers.
            Merge_Log           On
            Merge_Log_Key       log_processed
            K8S-Logging.Parser  On
            K8S-Logging.Exclude Off
            Labels              Off
            Annotations         Off
            Use_Kubelet         On
            Kubelet_Port        10250
            Buffer_Size         0

          [OUTPUT]
            Name                cloudwatch_logs
            Match               application.*
            region              ${AWS_REGION}
            log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application
            log_stream_prefix   ${HOST_NAME}-
            auto_create_group   true
            extra_user_agent    container-insights

          [OUTPUT]
            Name                s3
            Match               *
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz
        dataplane-log.conf: |
          [INPUT]
            Name                systemd
            Tag                 dataplane.systemd.*
            Systemd_Filter      _SYSTEMD_UNIT=docker.service
            Systemd_Filter      _SYSTEMD_UNIT=containerd.service
            Systemd_Filter      _SYSTEMD_UNIT=kubelet.service
            DB                  /var/fluent-bit/state/systemd.db
            Path                /var/log/journal
            Read_From_Tail      ${READ_FROM_TAIL}

          [INPUT]
            Name                tail
            Tag                 dataplane.tail.*
            Path                /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
            multiline.parser    docker, cri
            DB                  /var/fluent-bit/state/flb_dataplane_tail.db
            Mem_Buf_Limit       50MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Rotate_Wait         30
            storage.type        filesystem
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                modify
            Match               dataplane.systemd.*
            Rename              _HOSTNAME                   hostname
            Rename              _SYSTEMD_UNIT               systemd_unit
            Rename              MESSAGE                     message
            Remove_regex        ^((?!hostname|systemd_unit|message).)*$

          [FILTER]
            Name                aws
            Match               dataplane.*
            imds_version        v2

          [OUTPUT]
            Name                s3
            Match               *
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz
        host-log.conf: |
          [INPUT]
            Name                tail
            Tag                 host.dmesg
            Path                /var/log/dmesg
            Key                 message
            DB                  /var/fluent-bit/state/flb_dmesg.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 host.messages
            Path                /var/log/messages
            Parser              syslog
            DB                  /var/fluent-bit/state/flb_messages.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [INPUT]
            Name                tail
            Tag                 host.secure
            Path                /var/log/secure
            Parser              syslog
            DB                  /var/fluent-bit/state/flb_secure.db
            Mem_Buf_Limit       5MB
            Skip_Long_Lines     On
            Refresh_Interval    10
            Read_from_Head      ${READ_FROM_HEAD}

          [FILTER]
            Name                aws
            Match               host.*
            imds_version        v2

          [OUTPUT]
            Name                s3
            Match               *
            bucket              masukawa-eks-log-bucket
            region              ap-northeast-1
            total_file_size     50M
            use_put_object      Off
            compression         gzip
            s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz

マネジメントコンソールの「高度な設定」にコピペして保存します。

スクリーンショット 2025-01-17 23.50.39.png

設定後、kubectl exec -it fluent-bit-xxx -n amazon-cloudwatch /bin/bash で Pod 内に入って確認すると OUTPUT として S3 が追加されてました。
良さそうですね。

bash-4.2# cat fluent-bit/etc/application-log.conf
[INPUT]
  Name                tail
  Tag                 application.*
  Exclude_Path        /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
  Path                /var/log/containers/*.log
  multiline.parser    docker, cri
  DB                  /var/fluent-bit/state/flb_container.db
  Mem_Buf_Limit       50MB
  Skip_Long_Lines     On
  Refresh_Interval    10
  Rotate_Wait         30
  storage.type        filesystem
  Read_from_Head      ${READ_FROM_HEAD}

[INPUT]
  Name                tail
  Tag                 application.*
  Path                /var/log/containers/fluent-bit*
  multiline.parser    docker, cri
  DB                  /var/fluent-bit/state/flb_log.db
  Mem_Buf_Limit       5MB
  Skip_Long_Lines     On
  Refresh_Interval    10
  Read_from_Head      ${READ_FROM_HEAD}

[INPUT]
  Name                tail
  Tag                 application.*
  Path                /var/log/containers/cloudwatch-agent*
  multiline.parser    docker, cri
  DB                  /var/fluent-bit/state/flb_cwagent.db
  Mem_Buf_Limit       5MB
  Skip_Long_Lines     On
  Refresh_Interval    10
  Read_from_Head      ${READ_FROM_HEAD}

[FILTER]
  Name                kubernetes
  Match               application.*
  Kube_URL            https://kubernetes.default.svc:443
  Kube_Tag_Prefix     application.var.log.containers.
  Merge_Log           On
  Merge_Log_Key       log_processed
  K8S-Logging.Parser  On
  K8S-Logging.Exclude Off
  Labels              Off
  Annotations         Off
  Use_Kubelet         On
  Kubelet_Port        10250
  Buffer_Size         0

[OUTPUT]
  Name                cloudwatch_logs
  Match               application.*
  region              ${AWS_REGION}
  log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application
  log_stream_prefix   ${HOST_NAME}-
  auto_create_group   true
  extra_user_agent    container-insights

[OUTPUT]
  Name                s3
  Match               *
  bucket              masukawa-eks-log-bucket
  region              ap-northeast-1
  total_file_size     50M
  use_put_object      Off
  compression         gzip
  s3_key_format       /$TAG/%Y/%m/%d/%H_%M_%S.gz

S3 にも無事ログファイルが出力されてました。

スクリーンショット 2025-01-17 23.58.35.png

ちなみに、コンテナログの収集をオプトアウトした場合は Fluent Bit がインストールされません。

containerLogs:
  enabled: false

下記のように CloudWatch エージェントと amazon-cloudwatch-observability-controller-manager のみインストールされます。

% kubectl get pod -n amazon-cloudwatch
NAMESPACE           NAME                                                              READY   STATUS    RESTARTS   AGE
amazon-cloudwatch   amazon-cloudwatch-observability-controller-manager-65457cfthjph   1/1     Running   0          20m
amazon-cloudwatch   cloudwatch-agent-dxwmz                                            1/1     Running   0          20m

このように Fluent Bit はオプトアウトしつつ、Helm で管理する形を採用しても良いと思います。

まとめ

amazon-cloudwatch-observability アドオン経由で Fluent Bit をインストールしつつ、Fluent Bit の設定をカスタマイズしてみました。
細かいカスタマイズをしつつ、複数環境に適用するのであれば Helm の方が楽かもしれませんが、手軽に Fluent Bit をセットアップしたい場合は有用かと思います。
また、アドオンで Fluent Bit を管理しない場合も設定時の参考になると思います。
このブログがどなたかの役に立てば幸いです!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.