今回のミッション
- EKSにデプロイしたアプリケーションログの一部をS3に保存する
- 秒間数百〜数千件、欠損はNG
AWS, コンテナ技術素人が上記ミッションに挑んだ備忘録です。
構成図
ログをfluentd(fluent-bit)が監視してKinesis Data Firehoseの配信ストリームに送り、S3に集約する。
前提:EKS, Firehose, S3の各リソースは既にあり、連携できる状態になっている。
(あとで絵を書く)
試行パターン
- fluentd デーモンセット
- fluentd サイドカー <--最終的に採用した方式
- fluent-bit デーモンセット
- fluent-bit サイドカー
ログを出力するアプリケーション
ここではnginxを例に記載する。
実際のアプリケーションはtomcat + Java。
サイドカー方式では/applog/配下に収集対象のログ(*.log)が出力されるものとする。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
fluentd
まずはKubernetes上のログ収集の常套手段であるデーモンセットでfluentdを動かすことを試しました。
しかし今回のアプリケーションはそもそものログ出力が多く、最終的には収集対象のログのみを別のログファイルに切り出し、それをサイドカーで収集する方針としました。
デーモンセット
ベースはここから取得(fluentd-daemonset-elasticsearch-rbac.yaml)
https://github.com/fluent/fluentd-kubernetes-daemonset
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
namespace: kube-system
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
spec:
selector:
matchLabels:
k8s-app: fluentd-logging
version: v1
template:
metadata:
annotations:
iam.amazonaws.com/role: ap-northeast-1a.staging.kubernetes.ruist.io-service-role #要確認
labels:
k8s-app: fluentd-logging
version: v1
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-kinesis
env:
- name: LOG_GROUP_NAME #要確認
value: "k8s"
- name: AWS_REGION #要確認
value: "ap-northeast-1"
- name: FLUENT_KINESIS_STREAMS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_DELIVERY_STREAM_NAME
value: "XXXXX-stream"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluentd-config
mountPath: /fluentd/etc/fluent.conf
subPath: fluent.conf
readOnly: true
- name: kubernetes-config
mountPath: /fluentd/etc/kubernetes.conf
subPath: kubernetes.conf
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluentd-config
configMap:
defaultMode: 0644
name: fluentd-config
- name: kubernetes-config
configMap:
defaultMode: 0644
name: kubernetes-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
fluent.conf: |
@include kubernetes.conf
<match **>
@type kinesis_firehose
@id out_kinesis_firehose
region "#{ENV['FLUENT_KINESIS_STREAM_REGION'] || nil}"
delivery_stream_name "#{ENV['FLUENT_KINESIS_DELIVERY_STREAM_NAME'] || nil}"
</match>
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kubernetes-config
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
kubernetes.conf: |
<label @FLUENT_LOG>
<match fluent.**>
@type null
</match>
</label>
<source>
@type tail
@id in_tail_container_logs
path "/var/log/containers/nginx*.log"
pos_file "/var/log/fluentd-container.log.pos"
tag "kubernetes.*"
read_from_head true
<parse>
@type "json"
time_format "%Y-%m-%dT%H:%M:%S.%NZ"
time_type string
</parse>
</source>
kubectl apply -f nginx-deployment.yaml
kubectl apply -f fluentd-daemonset-configmap.yaml
kubectl apply -f fluentd-daemonset.yaml
サイドカー
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-kinesis
env:
- name: LOG_GROUP_NAME
value: "k8s"
- name: AWS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_STREAMS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_DELIVERY_STREAM_NAME
value: "XXXXX-stream"
volumeMounts:
- name: applog-volume
mountPath: /applog
- name: fluentd-config
mountPath: /fluentd/etc/fluent.conf
subPath: fluent.conf
readOnly: true
- name: kubernetes-config
mountPath: /fluentd/etc/kubernetes.conf
subPath: kubernetes.conf
readOnly: true
volumes:
- name: applog-volume
emptyDir: {}
- name: fluentd-config
configMap:
defaultMode: 0644
name: fluentd-config
- name: kubernetes-config
configMap:
defaultMode: 0644
name: kubernetes-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
labels:
k8s-app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
fluent.conf: |
@include kubernetes.conf
<match **>
@type kinesis_firehose
@id out_kinesis_firehose
region "#{ENV['FLUENT_KINESIS_STREAM_REGION'] || nil}"
delivery_stream_name "#{ENV['FLUENT_KINESIS_DELIVERY_STREAM_NAME'] || nil}"
retries_on_batch_request 20
<buffer>
flush_interval 1
chunk_limit_size 1m
flush_thread_interval 0.1
flush_thread_burst_interval 0.01
flush_thread_count 15
</buffer>
</match>
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kubernetes-config
labels:
k8s-app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
kubernetes.conf: |
<source>
@type tail
path "/applog/*.log"
pos_file "/var/log/fluentd-applog.log.pos"
tag "applog.*"
read_from_head true
<parse>
@type "json"
time_format "%Y-%m-%dT%H:%M:%S.%NZ"
time_type string
</parse>
</source>
kubectl apply -f fluentd-sidecar-configmap.yaml
kubectl apply -f fluentd-sidecar.yaml
fluent-bit
fluentdでロストが発生したため、fluent-bitの利用も試しました。
結果的にはKinesis側のLimit超過が原因であったため、情報が多いfluentdでの検証に戻りましたが、ログとして残しておきます。
デーモンセット
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flunt-bit
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: flunt-bit
namespace: kube-system
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flunt-bit
roleRef:
kind: ClusterRole
name: flunt-bit
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: flunt-bit
namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: flunt-bit
namespace: kube-system
labels:
k8s-app: flunt-bit-logging
version: v1
spec:
selector:
matchLabels:
k8s-app: flunt-bit-logging
version: v1
template:
metadata:
annotations:
iam.amazonaws.com/role: ap-northeast-1a.staging.kubernetes.ruist.io-service-role # 要確認
labels:
k8s-app: flunt-bit-logging
version: v1
spec:
serviceAccount: flunt-bit
serviceAccountName: flunt-bit
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: flunt-bit
image: public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
env:
- name: LOG_GROUP_NAME
value: "k8s"
- name: AWS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_STREAMS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_DELIVERY_STREAM_NAME
value: "XXXXX-stream"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: flunt-bit-config
mountPath: /flunt-bit/etc/
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: flunt-bit-config
configMap:
name: flunt-bit-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: flunt-bit-config
namespace: kube-system
labels:
k8s-app: flunt-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
fluent.conf: |
[SERVICE]
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Tag kube.*
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[FILTER]
name grep
match *
regex log XXXXX
[OUTPUT]
Name kinesis_firehose
Match *
delivery_stream XXX-stream
region ap-northeast-1
parsers.conf: |
[PARSER]
Name docker
Format json
Time_Key time
Timt_Format "%Y-%m-%dT%H:%M:%S.%NZ"
Time_Keep On
kubectl apply -f nginx-deployment.yaml
kubectl apply -f fluentbit-daemonset-configmap.yaml
kubectl apply -f fluentbit-daemonset.yaml
サイドカー
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- name: flunet-bit
image: public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
env:
- name: LOG_GROUP_NAME
value: "k8s"
- name: AWS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_STREAMS_REGION
value: "ap-northeast-1"
- name: FLUENT_KINESIS_DELIVERY_STREAM_NAME
value: "XXXXX-stream"
volumeMounts:
- name: applog-volume
mountPath: /applog
- name: flunt-bit-config
mountPath: /flunt-bit/etc/
volumes:
- name: applog-volume
emptyDir: {}
- name: flunt-bit-config
configMap:
name: flunt-bit-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: flunt-bit-config
labels:
k8s-app: flunt-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
data:
fluent.conf: |
[SERVICE]
Parsers_File parsers.conf
[INPUT]
Name tail
Path /applog/*.log
Parser docker
DB /var/log/flb_kube.db
Tag applog.*
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[OUTPUT]
Name kinesis_firehose
Match *
delivery_stream XXX-stream
region ap-northeast-1
parsers.conf: |
[PARSER]
Name docker
Format json
Time_Key time
Timt_Format "%Y-%m-%dT%H:%M:%S.%NZ"
Time_Keep On
kubectl apply -f fluentbit-sidecar-configmap.yaml
kubectl apply -f fluentbit-sidecar.yaml
詰まりポイント
AWSがわからない
プラットフォームは提供してもらったものの、AWSそのものを初めて触るので何から手を付けていいかわからない状態でした。
- IAM
- ロールとポリシーが何にせよ必要
- AWS内のサービス間で連携する際はこの辺の設定ちゃんとしないと繋がらない
- AWS CLI
- 各サービスの作成、編集をCLIで実施する際に使用するコンソール
- 画面ぽちぽちでできないことをする時はコマンドで実施する必要あり
- kubectlの実行もここから
- 今回はブラウザ上でやったけど、デスクトップアプリでもできるらしい
- Kinesis
- データ収集、分析のために利用するAWSサービス
- Kinesisの中でも複数のサービスがあり、今回使用したData Firehose以外にData Streams、Video Streams、Data Analyticsがある
- 今回は即時性が不要 かつ S3に溜め込むだけという用途だったため、Firehoseを採用
EKS(k8s)がわからない
AzureのAKSを使用した経験はあったもののk8sの経験自体がほとんどなく、ネットで調べた情報をつなぎ合わせてマニフェストを作り上げた状態でした。
継続調査
- ServiceAccount
- ClusterRole
- ClusterRoleBinding
- spec.template.metadata.annotaionsのiam.amazonaws.com/role
fluentd(fluent-bit)がわからない
fluent.confの書き方がわからず、問題が発生した際にどうメンテナンスをしてよいかがわかりませんでした。
また、そもそもの動作原理を知らないために各種設定値の意味と結びつかないのも課題でした。
継続調査
- 環境変数の使い方
- 性能改善のための各種設定値
- parse, filterの機能、設定方法
- プラグインの使い方
- fluent-bitで同じことをする場合の書き方
エラーによるデータ欠損
アプリケーションをデプロイした直後は動作が不安定になるのか、ログの出力が急激かつ局所的に増えてKinesisのLimitを超えてしまい、リトライ回数上限以内に送りきれずエラーとなってログが欠損してしまう事象が発生しました。
解決方法:retries_on_batch_request
をデフォルトの8から20に設定