はじめに
Kubernetes環境でFluentdを使ってログを転送するという情報はよく見るのですが、Fluentdより軽量と噂のFluent Bitを使い、且つOpenShift環境でという構成はあまり情報なさそうでしたので、実際にやってみた結果を記載します。
Fluent Bitについては公式サイトを参照ください。
構成
全体構成
上記のように、OpenShiftクラスター上にFluent BitのPodをDaemonsetでデプロイし、OpenShiftクラスター全体のログを仮想サーバー上にインストールしたFluent Bitに転送する構成で検証を行いました。OpenShiftはIBM Cloudで提供されるRed Hat OpenShift on IBM Cloudを利用しています。
詳細構成情報
OpenShift(Red Hat OpenShift on IBM Cloud):v4.5.24_1527
Workerノード x3(以前こちらで記事にした環境です)
Fluent Bit:v1.6
仮想サーバー:RHEL7.9
作ってみた
仮想サーバー側(受信側)
インストールは公式サイトのこちらの手順に従い実施し、サービスを起動させておきます。
設定情報はこちらです。INPUT/OUTPUTの部分のみデフォルト値から変更しています。
[SERVICE]
flush 5
daemon Off
log_level info
parsers_file parsers.conf
plugins_file plugins.conf
http_server Off
http_listen 0.0.0.0
http_port 2020
storage.metrics on
[INPUT]
Name forward
Port 24225
Buffer_Chunk_Size 32MB
Buffer_Max_Size 64MB
[OUTPUT]
name file
match *
path /data/log/td-agent-bit # ディレクトリ配下にタグ名のファイルが出力されます。
※Port、Bufferサイズ等は特に意図はありません。サイズはあまり小さいとエラーになりました。
OpenShift側(送信側)
今回検証するにあたり、下記GitHubの情報を参考にしています。
fluent/fluent-bit-kubernetes-logging
fluent/fluentd-kubernetes-daemonset
Fluent BitのPodをデプロするために下記7つのリソースを作成します。
- Namespace
- ServiceAccount
- ClusterRole
- ClusterRoleBinding
- SecurityContextConstraints
- ConfigMap
- DaemonSet
下記それぞれの設定及びYAMLファイルです。
-
Namespace
Namespaceはoc new-project fluentbittest
コマンドでfluentbittestというNamespaceを作成しました。 -
ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: fluentbittest
- ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
verbs: ["get", "list", "watch"]
- ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: fluentbittest
- SecurityContextConstraints
こちらはOpenShift特有の特権設定となり、上記GitHubでも紹介されています。
kind: SecurityContextConstraints
apiVersion: security.openshift.io/v1
metadata:
name: fluentbittest
allowPrivilegedContainer: true
allowHostNetwork: true
allowHostDirVolumePlugin: true
priority:
allowedCapabilities: []
allowHostPorts: true
allowHostPID: true
allowHostIPC: true
readOnlyRootFilesystem: false
requiredDropCapabilities: []
defaultAddCapabilities: []
runAsUser:
type: RunAsAny
seLinuxContext:
type: MustRunAs
fsGroup:
type: MustRunAs
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- downwardAPI
- emptyDir
- hostPath
- persistentVolumeClaim
- projected
- secret
users:
- system:serviceaccount:fluentbittest:builder
- system:serviceaccount:fluentbittest:default
- system:serviceaccount:fluentbittest:deployer
- system:serviceaccount:fluentbittest:fluent-bit
- ConfigMap
OpenShiftのコンテナランタイムにはcri-oが使われているため、[INPUT]のparserにはcri
を指定します。cri
設定の詳細は[PARSER]のcri
部分を確認ください。
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: fluentbittest
labels:
k8s-app: fluent-bit
data:
# Configuration files: server, input, filters and output
# ======================================================
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-forward.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser cri
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
filter-kubernetes.conf: |
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
output-forward.conf: |
[OUTPUT]
Name forward
Match *
Host ${FLUENT_FOWARD_HOST}
Port ${FLUENT_FOWARD_PORT}
Retry_Limit False
parsers.conf: |
[PARSER]
Name apache
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
# http://rubular.com/r/tjUt3Awgg4
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
[PARSER]
Name syslog
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
- DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: fluentbittest
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "2020"
prometheus.io/path: /api/v1/metrics/prometheus
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:1.6
imagePullPolicy: Always
ports:
- containerPort: 2020
env:
- name: FLUENT_FOWARD_HOST
value: "X.X.X.X"
- name: FLUENT_FOWARD_PORT
value: "24225"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
securityContext:
privileged: true
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
※Docker関連の設定が残ったままです。。
※上記Daemonsetに下記のようにSecurityContextの設定を入れておかないと、Fluent Bit Podがノード上のログファイルを読めずにエラーになりますので、注意です。(参照)
securityContext:
privileged: true
上記リソースを順にoc create -f <filename>
で作成するとPodが起動します。
$ oc get pod
NAME READY STATUS RESTARTS AGE
fluent-bit-7wj72 1/1 Running 0 53s
fluent-bit-97z7r 1/1 Running 0 53s
fluent-bit-w26z5 1/1 Running 0 53s
受信確認
仮想サーバー側で転送されたログがファイル出力されているかを確認します。
$ ls -l /data/log/td-agent-bit/
total 80592
-rw-r--r-- 1 root root 1477 Jan 25 20:18 kube.var.log.containers.calico-kube-controllers-6c4d9c955b-k8w6d_calico-system_calico-kube-controllers-355b03ba53e7dea6461944eb616be24ae6817d21acec8133e9ff6a845fc7b954.log
-rw-r--r-- 1 root root 164785 Jan 25 20:19 kube.var.log.containers.calico-node-lgrns_calico-system_calico-node-592a8f869fcf0460fd01c0114ea8c558e6e8167d92c3e0d59a609d0abb72be2e.log
-rw-r--r-- 1 root root 171322 Jan 25 20:19 kube.var.log.containers.calico-node-wjsbn_calico-system_calico-node-a8cfa55b9431bd668bcf744ed125d29747ae3c454d2c6c292556789822b79a5f.log
-rw-r--r-- 1 root root 175063 Jan 25 20:19 kube.var.log.containers.calico-node-xdxjb_calico-system_calico-node-eb24febe3c3d651e39f941fac47f70f45664d83a31371bc774c25f61253347cf.log
-rw-r--r-- 1 root root 1128 Jan 25 20:17 kube.var.log.containers.calico-typha-5c8d96f77d-sfltp_calico-system_calico-typha-d70a3dde704772db3015d654db936e4b9b1902f8312228836dcb99e17d64959e.log
:
(後略)
ログファイルの中身(一番上に表示されたログで確認)。
[user@vsi ~]$ cat /data/log/td-agent-bit/kube.var.log.containers.calico-kube-controllers-6c4d9c955b-k8w6d_calico-system_calico-kube-controllers-355b03ba53e7dea6461944eb616be24ae6817d21acec8133e9ff6a845fc7b954.log
kube.var.log.containers.calico-kube-controllers-6c4d9c955b-k8w6d_calico-system_calico-kube-controllers-355b03ba53e7dea6461944eb616be24ae6817d21acec8133e9ff6a845fc7b954.log: [1611627483.220251215, {"stream":"stderr","logtag":"F","message":"2021-01-26 02:18:03.220 [INFO][1] watchercache.go 96: Watch channel closed by remote - recreate watcher ListRoot=\"/calico/resources/v3/projectcalico.org/nodes\"","kubernetes":{"pod_name":"calico-kube-controllers-6c4d9c955b-k8w6d","namespace_name":"calico-system","pod_id":"a25ffa7a-ecf7-4140-8e65-4e4f174e80f6","labels":{"k8s-app":"calico-kube-controllers","pod-template-hash":"6c4d9c955b"},"annotations":{"cni.projectcalico.org/podIP":"172.17.59.83/32","cni.projectcalico.org/podIPs":"172.17.59.83/32","k8s.v1.cni.cncf.io/network-status":"[{\n \"name\": \"k8s-pod-network\",\n \"ips\": [\n \"172.17.59.83\"\n ],\n \"default\": true,\n \"dns\": {}\n}]","k8s.v1.cni.cncf.io/networks-status":"[{\n \"name\": \"k8s-pod-network\",\n \"ips\": [\n \"172.17.59.83\"\n ],\n \"default\": true,\n \"dns\": {}\n}]"},"host":"10.240.0.4","container_name":"calico-kube-controllers","docker_id":"355b03ba53e7dea6461944eb616be24ae6817d21acec8133e9ff6a845fc7b954","container_hash":"registry.ng.bluemix.net/armada-master/calico/kube-controllers@sha256:eb456f071b19614a6a4eb149cf1eb2dc924780accc2f9b426305790e03c2403b","container_image":"registry.ng.bluemix.net/armada-master/calico/kube-controllers:v3.16.5"}}]
[user@vsi ~]$
これでOpenShift上のログが転送されていることが確認できました。
おまけ
せっかく軽量と噂のFluent Bitをたてたので、Fluentdと比較して軽量っぷりを見てみました。
- 比較用の環境
Fluentdはfluent/fluentd-kubernetes-daemonsetを参照して構築しました。このfluentdとFluent Bitの検証環境と同じOpenShift上のログを同仮想サーバーに転送するためのFluentd Podのリソース使用量を比較してみます。
$ oc get pod -n logtest
NAME READY STATUS RESTARTS AGE
fluentd-2cbqz 1/1 Running 0 5d19h
fluentd-4mh8f 1/1 Running 0 5d19h
fluentd-77h8b 1/1 Running 0 5d19h
こちらのFluentdは、多少のフィルタ処理をいれているため、正確な比較にはなっていませんが、ザックリとした比較にはなるかと思います。
以下結果となります。
- 結果:Fluentd
$ oc adm top po -n logtest
NAME CPU(cores) MEMORY(bytes)
fluentd-2cbqz 12m 112Mi
fluentd-4mh8f 15m 112Mi
fluentd-77h8b 15m 119Mi
- 結果:Fluent Bit
$ oc adm top po -n fluentbittest
NAME CPU(cores) MEMORY(bytes)
fluent-bit-7wj72 8m 9Mi
fluent-bit-97z7r 4m 8Mi
fluent-bit-w26z5 3m 7Mi
結果、Fluent BitのほうがFluentdと比較してCPU使用量は1/2程度以下、メモリ使用量は1/10程度以下のリソース使用量でした。(正確な比較ができてないのでザックリな表現となります。。)
上記記載の通り、Fluentdのほうが処理内容が多少多いというのはありますが、CPU使用量は1/2程度以下、メモリ使用量1/10程度以下のリソース使用量というのは魅力的ですね。特にOpenShift/Kubernetesクラスター環境はログの量が非常に多いため、ログコレクターの処理も重くなりがちです。今回の構成は単純にforward設定によりログを転送させただけの構成ですので、本番環境で使用するためには諸々調整および検証が必要になる点考慮いただければと思います。