用 CRD 来控制 Kubernetes 日志流
概述
Banzai logging operator 已经出到了 v3 版本。这个项目以 Fluentd 为基础,使用 Operator 的实现模式,在 Kubernetes 上用 CRD 的形式,对日志的采集行为进行定制,并进行过滤、路由等操作,最终可以将日志输出到 Elasticsearch、Loki、S3、Kafka 等多种后端。
图中可见,Logging 把日志分为了采集、路由和输出三个阶段。这三个阶段对应三种不同的 CRD:
- 采集:使用 fluentbit 对日志进行采集
- 路由:在 fluentd 中使用多种条件对日志条目进行过滤,并将结果发往目标
- 输出:可以定义各种后端用于接收存储日志。
安装
可以使用 Helm 进行安装:
$ kubectl create ns logging
namespace/logging created
$ helm repo add banzaicloud-stable \
https://kubernetes-charts.banzaicloud.com
"banzaicloud-stable" has been added to your repositories
$ helm install --namespace logging \
logging banzaicloud-stable/logging-operator \
--set createCustomResource=false
...
安装之后,会看到 logging
命名空间中的 Pod:
$ kubectl get po -n logging
NAME READY STATUS RESTARTS AGE
...
logging-logging-operator-7b4f9987f9-86clp 1/1 Running 0 120m
Logging
首先可以定义一个新的 Logging 对象:
apiVersion: logging.banzaicloud.io/v1beta1
kind: Logging
metadata:
name: default-logging-simple
spec:
fluentd: {}
fluentbit: {}
controlNamespace: logging
提交到集群:
$ kubectl apply -f empty-logging.yaml
logging.logging.banzaicloud.io/empty-logging created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
empty-logging-fluentbit-2pghs 0/1 ContainerCreating 0 0s
empty-logging-fluentbit-cc4r4 0/1 ContainerCreating 0 0s
empty-logging-fluentbit-jzkpr 0/1 ContainerCreating 0 0s
empty-logging-fluentd-0 0/2 Pending
...
Describe 新生成的 Pod,会发现它们加载了几个 Secret 作为配置文件。例如 fluentbit 的内容:
$ kubectl view-secret empty-logging-fluentbit
Choosing key: fluent-bit.conf
...
[OUTPUT]
Name forward
Match *
Host empty-logging-fluentd.logging.svc
Port 24240
Retry_Limit False
这里的配置表明,采集器收集到的日志会输出到 empty-logging-fluentd
的服务之中。
追查一下 fluentd 的配置内容,发现其输出配置为空,也就是说,没有提供输出能力。
查看 operator 的日志:
$ kubectl logs -f -l app.kubernetes.io/name=logging-operator
...
{"level":"info","ts":1583835777.4591844,"logger":"controllers.Logging","msg":"resource created","name":"empty-logging-fluentbit","namespace":"logging","apiVersion":"apps/v1","kind":"DaemonSet"}
...
{"level":"info","ts":1583835834.1114376,"logger":"controllers.Logging","msg":"no flows found, generating empty model"}
输出日志中表示,缺乏 flow 定义。
Output
先使用 Helm 安装 Loki,然后定义一个 output:
apiVersion: logging.banzaicloud.io/v1beta1
kind: Output
metadata:
name: loki-output
spec:
loki:
url: http://loki-1583844504.loki:3100
configure_kubernetes_labels: true
buffer:
timekey: 1m
timekey_wait: 30s
timekey_use_utc: true
创建这个资源之后,Secret 和 Pod 都没发生什么变化,甚至 Operator Pod 的日志都没有输出内容,看来还是需要创建 Flow 将日志输出过去。
Flow
创建如下的 Flow 对象:
apiVersion: logging.banzaicloud.io/v1beta1
kind: Flow
metadata:
name: loki-flow
spec:
filters:
- tag_normaliser: {}
- parser:
remove_key_name_field: true
reserve_data: true
parse:
type: nginx
match:
- select:
labels:
app.kubernetes.io/name: log-generator
outputRefs:
- loki-output
其中 filters
成员中标识了我们要对应用进行的处理,使用 tag_normaliser
加入 Kubernetes 标签。
在 match
中使用标签加入过滤功能,这里选择了 app.kubernetes.io/name: log-generator
的标签对
outputRefs
指定输出到前面创建的 loki-output
。
CRD 创建之后
进入 fluentd Pod 的 Shell,会发现配置发生变化:
$ cat fluentd/app-config/fluentd.conf
<match **>
...
<match>
labels app.kubernetes.io/name:log-generator
namespaces logging
...
<match kubernetes.**>
@type tag_normaliser
@id loki-flow_0_tag_normaliser
format ${namespace_name}.${pod_name}.${container_name}
</match>
<filter **>
@type parser
@id loki-flow_1_parser
...
<parse>
@type nginx
...
<match **>
@type loki
部署一个应用:
apiVersion: apps/v1
kind: Deployment
metadata:
name: log-generator
spec:
selector:
matchLabels:
app.kubernetes.io/name: log-generator
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: log-generator
spec:
containers:
- name: nginx
image: banzaicloud/log-generator:0.3.2
此时打开 Loki 的 Grafana,就能对日志进行查询了:
结语
这个产品很好的展示了 Operator 固化运维技能的特征。化繁为简,将日志集采过程中所需的复杂知识,精选为一系列的配置组合,以 CRD 的形式呈现给非专家型客户,开箱即用。
kubectl 的 view-secret 插件,Bug 比较严重,不要问我是怎么知道的。