日志收集,考虑到我们没有主动上报日志,格式也不太统一,容器化的时候,对于我们而言采集容器标准输出的日志是更快捷达到目的的方式。
大致对比了filebeat、fluentd、fluentbit、vector、logstash后,锁定了flientbit。
虽然它功能不是最强大的,但是基本功能能满足我们的业务需求,采集输入端多样化,也能做简单的filter,支持output的端也很丰富。其次,它吃资源比较少,性能也不错。
我们只需要它收集标准输出的日志,输出到es即可。
而在k8s中,有kubesphere共献给fluent的fluent-operator。该opeator提供了fluentbit only、fluentd only、fluentbit+fluentd 这几种采集方式。对于我们而言,就fluentbit 就够了。
我选择了helm方式安装,下载了它chart (https://github.com/fluent/fluent-operator/releases/download/v2.2.0/fluent-operator.tgz)包,解压到`/app/fluent-operator/ `,直接修改里面values.yaml,这样好方便未来管理自己的部署配置。
简单按照文档,修改了fluentbit的input和output后,直接安装即可
1 2
| helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd
|
按照服务名区分索引名
日志是能收集了,但是所有的服务的日志,当天都放到一个索引,类似 “k8s-logs-<yyyy.mm.dd>” 这样的索引中。本地还好,如果生产环境的话,那一天单个索引就十分庞大了。而且对于不同服务日志保留天数做差异化保留的时候也不好处理,也不好直观展示各个服务日志大小。
所以能按照k8s-<service-name>-<yyyy.mm.dd>
格式的话,对我们会更为理想点。截止目前的版本,没有现成的配置可以实现这个。经过研究,可以利用fluentbit的 logstash_prefix_key 这个来实现。logstash_prefix是固定的前缀,logstash_prefix_key则可以动态读取 key来作为索引名。
1.修改fluent-operator,让其支持logstash_prefix_key
经过测试,fluent-operator当前版本的模板并没有对该字段进行处理,所以要进行修改让它支持这个字段。
我需要output到es,所以我就修改了es相关的output,其它输出源的可以自己检查下。
修改 ./templates/fluentbit-output-elasticsearch.yaml
在Values.fluentbit.output.es.logstashPrefix之后增加 .Values.fluentbit.output.es.logstashPrefixKey 这段的配置,如下:
1 2 3 4 5 6
| {{- if .Values.fluentbit.output.es.logstashPrefix }} logstashPrefix: {{ .Values.fluentbit.output.es.logstashPrefix | default "ks-logstash-log" | quote }} {{- end }} {{- if .Values.fluentbit.output.es.logstashPrefixKey }} logstashPrefixKey: {{ .Values.fluentbit.output.es.logstashPrefixKey | default "ks-logstash-log-key" | quote }} {{- end }}
|
2.让它能获取app name作为服务名
./values.yaml 修改fluentbit部分,开启kubernetes labels,因为labels的app标签就是我们的服务名。
1 2 3 4 5 6
| filter: kubernetes: enable: true labels: true
|
3. 拼接索引名
由于logstash_prefix_key只能接受 key,且不支持嵌套的对象的key。
举个直观的例子,假设你的对象是这样的:
1 2 3 4 5 6 7 8
| { "file": "systemd.log", "kubernetes": { "labels": { "app": "demo-service" } } }
|
像 k8s-$kubernets["labels"]["app"]
和 像 $kubernets["labels"]["app"]
这样的值,是取不到任何内容的。像上面的例子,只接受 $file 这个值。
所以这里要实现我们的目标,我们得自己拼接出一个新的key,作为索引名。类似下面:
1 2 3 4 5 6 7 8 9
| { "file": "systemd.log", "app_name": "k8s-demo-service", "kubernetes": { "labels": { "app": "demo-service" } } }
|
我是这样实现的,利用lua的filter,通过取出kubernetes的labels,拼接新的值。
我修改的是 cat ./templates/fluentbit-containerd-config.yaml 增加了一个 add_k8s_app_name_field 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| {{- if .Values.Kubernetes -}} {{- if .Values.fluentbit.enable -}} {{- if .Values.fluentbit.filter.containerd.enable -}} apiVersion: v1 kind: ConfigMap metadata: name: fluent-bit-containerd-config data: containerd.lua: | function containerd( tag, timestamp, record) if(record["logtag"]~=nil) then timeStr = os.date("!*t", timestamp["sec"]) t = string.format("%4d-%02d-%02dT%02d:%02d:%02d.%sZ", timeStr["year"], timeStr["month"], timeStr["day"], timeStr["hour"], timeStr["min"], timeStr["sec"], timestamp["nsec"]); record["time"] = t; record["log"] = record["message"]; record["message"] = nil; return 1, timestamp, record else return 0,timestamp,record end end
function add_k8s_app_name_field(tag, timestamp, record) retcode = 0 prefix = 'k8s' app_name = record['kubernetes']['labels']['app'] if app_name ~= nil then app_name = prefix .. '-' .. app_name if app_name ~= nil then record['app_name'] = app_name retcode = 2 end end return retcode, timestamp, record end {{- end }} {{- end }} {{- end }}
|
修改 templates/fluentbit-clusterfilter-kubernetes.yaml 增加新增的lua filter函数 对kubernetes标签进行处理
cat templates/fluentbit-clusterfilter-kubernetes.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| {{- if .Values.Kubernetes -}} {{- if .Values.fluentbit.enable -}} {{- if .Values.fluentbit.filter.kubernetes.enable -}} apiVersion: fluentbit.fluent.io/v1alpha2 kind: ClusterFilter metadata: name: kubernetes labels: fluentbit.fluent.io/enabled: "true" fluentbit.fluent.io/component: logging spec: match: kube.* filters: - kubernetes: kubeURL: https://kubernetes.default.svc:443 kubeCAFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt kubeTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token {{- $params := omit .Values.fluentbit.filter.kubernetes "enable" }} {{- if .Values.fluentbit.output.stdout.enable }} {{- $_ := set $params "k8sLoggingExclude" true -}} {{- end }} {{- with $params }} {{- . | toYaml | nindent 6 }} {{- end }} - lua: script: key: containerd.lua name: fluent-bit-containerd-config call: add_k8s_app_name_field timeAsTable: true - nest: operation: lift nestedUnder: kubernetes addPrefix: kubernetes_ - modify: rules: - remove: stream - remove: kubernetes_pod_id - remove: kubernetes_docker_id - remove: kubernetes_container_hash - remove: kubernetes_labels - nest: operation: nest wildcard: - kubernetes_* nestUnder: kubernetes removePrefix: kubernetes_ {{- end }} {{- end }} {{- end }}
|
主要增加了
1 2 3 4 5 6
| - lua: script: key: containerd.lua name: fluent-bit-containerd-config call: add_k8s_app_name_field timeAsTable: true
|
到这里基本就满足所有的条件了,为了不影响systemd的日志收集和归类,也给它的lua filter增加app_name
字段.
./templates/fluentbit-lua-config.yaml
1
| new_record["app_name"] = "systemd"
|
经过前面的修改后,只要在values.yaml 设置 fluentbit.output.es.logstashPrefixKey=”$app_name” 即可
4. 应用变更
我修改过的values.yaml,去除了无关部分后主要如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| Kubernetes: true fluentbit: crdsEnable: true enable: true image: repository: "hub.xxxx.com/library/fluent-bit" tag: "v2.0.11" input: tail: enable: true refreshIntervalSeconds: 10 memBufLimit: 50MB path: "/var/log/containers/*.log" skipLongLines: false systemd: enable: true path: "/var/log/journal" includeKubelet: true output: es: enable: true host: "es.xxx.local" port: 9200 logstashFormat: true logstashPrefixKey: "$app_name" filter: kubernetes: enable: true labels: true annotations: false k8sLoggingExclude: true containerd: enable: true systemd: enable: true
|
再来helm更新一下,搞定
1
| helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd
|
效果如下:
1 2 3 4
| yellow open k8s-kiali-2023.05.10 iWEHK0gKR6GoPKjO3gH6Eg 1 1 30 0 105.9kb 105.9kb yellow open k8s-nginx-deploy-2023.05.10 OCzPIKWgRneSn27J-o4k9g 1 1 16 0 76.3kb 76.3kb yellow open k8s-istiod-2023.05.10 GlA1dI7aQDqPFD-5ZMX6iw 1 1 797 0 329.5kb 329.5kb yellow open k8s-reviews-2023.05.10 41Ifiq3cTQGBKdJ4Fn2MJQ 1 1 18 0 86.6kb 86.6kb
|