Amazon Web Services ブログ

AWS for Fluent Bit による Kubernetes ロギング



集中ログは、Kubernetes クラスターを大規模に実行および管理するための重要なコンポーネントです。開発者はアプリケーションのデバッグとモニタリングのために、運用チームはアプリケーションのモニタリングのために、セキュリティはモニタリングのために、それぞれログにアクセスする必要があります。これらのチームには、ログの処理と保存に関する異なる要求事項があります。このブログ記事では、Amazon CloudWatch と組み合わせた AWS for Fluent Bit を使用してログを集中管理するソリューションを紹介します。

AWS for Fluent Bit は Fluent Bit 上に構築されたコンテナであり、ログフィルター、パーサー、およびさまざまな出力先へのルーターとして設計されています。AWS for Fluent Bit は、Amazon CloudWatch、Amazon Kinesis Data Firehose、Amazon Kinesis Data Streams などの AWS のサービスのサポートを追加します。

ソリューションの解説の前に、Fluent Bit によってログが処理され、出力先に送信される方法を見てみましょう。ログは最初に Input 経由で取り込まれます。Kubernetes の場合、Input は Docker がそのホスト上のコンテナの stdout および stderr から生成したコンテナログファイルです。この Input は Docker ログ形式を処理し、ログエントリで時間が適切に設定されていることを確認します。

[INPUT]
    Name                    tail
    Tag                        kube.*
    Path                      /var/log/containers/*.log
    DB                         /var/log/flb_kube.db
    Parser                   docker
    Docker_Mode       On
    Mem_Buf_Limit     5MB
    Skip_Long_Lines   On
    Refresh_Interval    10

次に、ログは Fluent Bit フィルターのセットによってフィルター処理されます。このソリューションは Kubernetes フィルターを活用して、ログストレージソリューションでのクエリを容易にするために、ポッドラベルと注釈でログエントリを充実させます。

[FILTER]
    Name                kubernetes
    Match               kube.*
    Kube_URL            https://kubernetes.default.svc.cluster.local:443
    Merge_Log           On
    Merge_Log_Key       data
    K8S-Logging.Parser  On
    K8S-Logging.Exclude On

デフォルトでは、Kubernetes フィルターはログデータが JSON 形式であると想定し、そのデータの解析を試みます。たとえば、ログエントリが JSON から文字列にシリアル化された場合、ログエントリで構造化データを使用できるようにします。たとえば、次のログエントリがある場合、構造化データをバックエンドシステムで使用できるようにします。プラグインは、アプリケーションからの元のエントリも保持します。

Input:

"{ \"message\": \"A new user signed up!\", \"service\": \"user-service\", \"metadata\": { \"source\": \"mobile\" } }"

Output:

{
    "data": {
        "message": "A new user signed up!",
        "service": "user-service",
        "metadata": {
            "source": "mobile"
        }
    },
    "log": "{ \"message\": \"A new user signed up!\", \"service\": \"user-service\", \"metadata\": { \"source\": \"mobile\" } }"
}

パーサーは、NGINX や Apache などのカスタムパーサーを使用するようにカスタマイズできます。Kubernetes Pod に注釈を追加することにより、デフォルトの JSON パーサーをオーバーライドできます。これで、さまざまなログ形式を解析し、文字列形式から構造化形式に逆シリアル化できるようになりました。

annotations:
    fluentbit.io/parser: nginx

また、Kubernetes Filter は、Kubernetes メタデータでデータを充実します。Kubernetes API Server を呼び出し、そのポッドに関する情報をクエリします。これにより、Kubernetes というログエントリにさらなるキーが追加されます。

kubernetes: {
    annotations: {
        "kubernetes.io/psp": "eks.privileged"
    },
    container_hash: "<some hash>",
    container_name: "myapp",
    docker_id: "<some id>",
    host: "ip-10-1-128-166.us-east-2.compute.internal",
    labels: {
        app: "myapp",
        "pod-template-hash": "<some hash>"
    },
    namespace_name: "default",
    pod_id: "198f7dd2-2270-11ea-be47-0a5d932f5920",
    pod_name: "myapp-5468c5d4d7-n2swr"
}

ログが解析され、メタデータで充実されたので、ログを出力先に送信します。さまざまなユースケースに対して複数の出力を行うのが一般的です。たとえば、すべてのログを Amazon CloudWatch に送信して、開発者がログにアクセスできるようにしたり、長期保存のために S3 へのエクスポートを可能にしたりすることもできます。一部の開発チームは ELK (Elasticsearch、Logstash、Kibana) スタックを使用する場合があります。ELK の場合、Kinesis Data Firehose プラグインを活用して、ログを Amazon Elasticsearch および S3 にストリーミングできます。このツールセットは、開発者が S3 を介して長期の監査要件を維持しながら、デバッグのためにログをライブストリームするために一般的に使用されます。ログを複数の出力に分割する方法の詳細については、Fluent Bit ストリームに関するこのブログ記事を参照してください。この例を単純な状態に保ちつつ、Amazon CloudWatch Logs のみを使用しています。

最初に、CloudWatch Logs の出力を設定する必要があります。Fluent Bit を設定して、ログを特定のロググループに送信し、存在しない場合はそのグループを作成します。

[OUTPUT]
        Name cloudwatch
        Match   **
        region us-east-2
        log_group_name fluentbit-cloudwatch
        log_stream_prefix fluentbit-
        auto_create_group true

Fluent Bit の設定ができたので、クラスターと DaemonSet をデプロイする必要があります。最初に、eksctl を使用して、サービスアカウントの IAM ロールを有効にして新しいクラスターを作成します。これを行う方法の詳細については、eksctl に関する文書を参照してください。クラスターをセットアップする場合、適切な ALB Ingress Controller 権限がある「kube-system」名前空間に「alb-ingress-controller」というサービスアカウントがあり、Amazon CloudWatch Logs に書き込む権限がある「fluentbit-system」名前空間に「fluentbit」があることを確認してください。これで FluentBit サービスアカウントを持つ EKS クラスターができました。

helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
helm upgrade -i aws-alb incubator/aws-alb-ingress-controller \
  --namespace kube-system \
  --set clusterName=fluentbit-demo-cluster \
  --set awsRegion=us-east-2 \
  --set awsVpcID=<vpc id of cluster> \
  --set image.tag=v1.1.5 \
  --set rbac.create=true \
  --set rbac.serviceAccountName=alb-ingress-controller

クラスターがプロビジョニングされたら、DaemonSet をデプロイします。まず、fluentbit.yml ファイルを作成します。このファイルは、Fluent Bit エージェントをデプロイするために使用される ClusterRole、ClusterRoleBinding、ConfigMap、およびDaemonSet を定義します。

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: fluentbit
rules:
  - apiGroups: [""]
    resources:
      - namespaces
      - pods
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: fluentbit
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: fluentbit
subjects:
  - kind: ServiceAccount
    name: fluentbit
    namespace: fluentbit-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentbit-config
  namespace: fluentbit-system
  labels:
    app.kubernetes.io/name: fluentbit
data:
  fluent-bit.conf: |
    [SERVICE]
        Parsers_File /fluent-bit/parsers/parsers.conf

    [INPUT]
        Name              tail
        Tag               kube.*
        Path              /var/log/containers/*.log
        DB                /var/log/flb_kube.db
        Parser            docker
        Docker_Mode       On
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On
        Refresh_Interval  10

    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc.cluster.local:443
        Merge_Log           On
        Merge_Log_Key       data
        K8S-Logging.Parser  On
        K8S-Logging.Exclude On

    [OUTPUT]
        Name cloudwatch
        Match   **
        region us-east-2
        log_group_name fluentbit-cloudwatch
        log_stream_prefix fluentbit-
        auto_create_group true
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentbit
  namespace: fluentbit-system
  labels:
    app.kubernetes.io/name: fluentbit
spec:
  selector:
    matchLabels:
      name: fluentbit
  template:
    metadata:
      labels:
        name: fluentbit
    spec:
      serviceAccountName: fluentbit
      containers:
        - name: aws-for-fluent-bit
          imagePullPolicy: Always
          image: amazon/aws-for-fluent-bit:latest
          volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true
            - name: fluentbit-config
              mountPath: /fluent-bit/etc/
          resources:
            limits:
              memory: 500Mi
            requests:
              cpu: 500m
              memory: 100Mi
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers
        - name: fluentbit-config
          configMap:
            name: fluentbit-config

これがデプロイされると、CoreDNS や AWS Node コンテナなどのシステムコンテナの Amazon CloudWatch Log Group にストリーミングされるログが表示されます。これがどのようにアプリケーションに使用されるのかを示すために、NGINX コンテナをデプロイし、カスタム Fluent Bit パーサーを指定します。以下は、デモアプリケーションで使用される demo.yml ファイルです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
      annotations:
        fluentbit.io/parser: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17-alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: nginx-service
              servicePort: 80

これがクラスターにデプロイされたら、ALB が設定されるのを待ちます。次のコマンドを実行して、ALB エンドポイントを取得できます。

❯ kubectl get ingress/nginx-ingress                                                                                    
NAME            HOSTS   ADDRESS                                                                  PORTS   AGE
nginx-ingress   *       3d6aa6b1-default-nginxingr-29e9-1180704856.us-east-2.elb.amazonaws.com   80      3h3m

次に、hey のような HTTP リクエストのシミュレーションに使用して、アプリケーションの負荷をシミュレーションし、ログを生成します。

hey -n 30 -c 1 http://3d6aa6b1-default-nginxingr-29e9-1180704856.us-east-2.elb.amazonaws.com/

これで、ログに次のようなエントリが表示されます。

{
   "data":{
      "agent":"hey/0.0.1",
      "code":"200",
      "host":"-",
      "method":"GET",
      "path":"/",
      "referer":"-",
      "remote":"10.1.128.166",
      "size":"612",
      "user":"-"
   },
   "kubernetes":{
      "annotations":{
         "fluentbit.io/parser":"nginx",
         "kubernetes.io/psp":"eks.privileged"
      },
      "container_hash":"0e61b143db3110f3b8ae29a67f107d5536b71a7c1f10afb14d4228711fc65a13",
      "container_name":"nginx",
      "docker_id":"b90a89309ac90fff2972822c66f11736933000c5aa6376dff0c11a441fa427ee",
      "host":"ip-10-1-128-166.us-east-2.compute.internal",
      "labels":{
         "app":"nginx",
         "pod-template-hash":"5468c5d4d7"
      },
      "namespace_name":"default",
      "pod_id":"198f7dd2-2270-11ea-be47-0a5d932f5920",
      "pod_name":"nginx-5468c5d4d7-n2swr"
   },
   "log":"10.1.128.166 - - [19/Dec/2019:17:41:12 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"hey/0.0.1\" \"52.95.4.2\"\n",
   "stream":"stdout",
   "time":"2019-12-19T17:41:12.70630778Z"
}

 

素晴らしい! これで、AWS for Fluent Bit DaemonSet は、アプリケーションからログをストリーミングし、Kubernetes メタデータを追加し、ログを解析し、モニタリングとアラートのために Amazon CloudWatch に送信するようになりました。

注意 : AWS Fargate でコンテナを実行している場合、Fargate は DaemonSets をサポートしていないため、Pod ごとに個別のサイドカーコンテナを実行する必要があります。

結論として、このアーキテクチャは、モニタリングおよび分析できる構造化された方法で、Amazon CloudWatch、Amazon Kinesis Data Firehose、Amazon Kinesis Data Streams、および他の多くのバックエンドにログをストリーミングするように設定できます。これにより、管理者は組織内の必要なグループへのアクセスを提供できます。