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、および他の多くのバックエンドにログをストリーミングするように設定できます。これにより、管理者は組織内の必要なグループへのアクセスを提供できます。