Kubernetes 上的日志采集方案
DaemonSet 是什么?
DaemonSet 是 Kubernetes 集群中的一种资源类型,它确保了在集群中的每个节点上运行指定的 Pod 副本。当有新节点加入集群时,Kubernetes 会自动在这些节点上添加 DaemonSet 的 Pod,而当节点从集群中移除时,这些 Pod 也会被清理。这使得 DaemonSet 非常适合于运行集群存储、日志收集或监控等跨集群节点需要运行的服务。
DaemonSet 的几个常见用途包括:
- 日志收集器:在每个节点上运行日志收集器,如 Fluentd 或 Fluent Bit,用于自动收集该节点上所有容器的日志。
- 监控代理:在每个节点上运行监控代理,如 Prometheus Node Exporter,收集和报告节点级别的性能指标。
- 网络插件:运行网络插件,如 Calico、Cilium 或 Weave,这些服务通常需要在集群中的每个节点上运行以提供网络功能。
- 存储守护进程:在需要提供分布式存储的每个节点上运行如 Ceph、GlusterFS 等存储守护进程。
DaemonSet 是通过 Kubernetes API 创建的,通常使用 YAML 配置文件来定义。这个定义包含了 Pod 的定义及应在哪些节点上运行这些 Pod 的规则(可以通过选择器(selectors)来指定节点的特定标签)。
例如,一个 DaemonSet 资源
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: kube-system
spec:
selector:
matchLabels:
name: fluent-bit
template:
metadata:
labels:
name: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:latest
ports:
- containerPort: 2020
这个 DaemonSet 配置将在集群的每个节点上部署 Fluent Bit 日志收集器。
Fluent Bit 是什么?
Fluent Bit 是一个开源的日志处理器和转发器,设计用于处理来自不同来源的日志数据,然后发送到多种目的地。它特别轻量级,适合在资源受限的环境(如容器和微服务架构)中使用。Fluent Bit 支持多种输入(如文件、HTTP、Syslog 等)和输出插件(如 Elasticsearch、Kafka、HTTP 等),并且能够进行日志数据的解析和过滤。
要部署 Fluent Bit 到 Kubernetes 集群中,通常需要准备一个 Kubernetes DaemonSet 配置文件,以及相关的 ConfigMap 来定义 Fluent Bit 的配置。
下面是一个基本的示例,展示了如何部署 Fluent Bit 并配置其从 Kubernetes 集群中收集日志。
这个示例包括两部分:ConfigMap 和 DaemonSet。ConfigMap 包含 Fluent Bit 的配置,而 DaemonSet 确保在集群的每个节点上运行一个 Fluent Bit 的副本。
首先是配置文件 ConfigMap (fluent-bit-config.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: kube-system
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser cri
Tag kube.*
Refresh_Interval 5
Mem_Buf_Limit 5MB
[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
Merge_Log On
[OUTPUT]
Name stdout
Match *
parsers.conf: |
[PARSER]
Name cri
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
注意这里的 Daemon 设置成 off,在传统的 Linux 系统环境中,守护进程是在后台运行的进程,它们脱离终端,独立于控制终端并周期性地执行任务或等待处理某些事件。在容器化环境(如 Docker 容器或 Kubernetes Pod)中,通常期望容器内的主进程在前台运行。
因为这里使用的是 containerd 作为容器运行时,所以日志文件的路径是 /var/log/containers/*.log,如果使用 Docker 作为容器运行时,日志文件的路径是 /var/lib/docker/containers/*/*.log。
然后部署这个 Fluent Bit,DaemonSet (fluent-bit-daemonset.yaml)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: kube-system
labels:
k8s-app: fluent-bit-logging
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:latest
imagePullPolicy: Always
volumeMounts:
- name: varlog
mountPath: /var/log
- name: config-volume
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: config-volume
configMap:
name: fluent-bit-config
要部署这个配置,首先应用 ConfigMap:
kubectl apply -f fluent-bit-config.yaml
然后部署 DaemonSet:
kubectl apply -f fluent-bit-daemonset.yaml
请注意,这个配置将日志输出到标准输出(stdout)。在实际部署中,你可能希望将 [OUTPUT] 部分修改为将日志发送 到你选择的日志存储/分析服务,如 Elasticsearch、Kafka 或其他服务。同时,确保你的 Kubernetes 集群和 Fluent Bit 版本兼容。
配置日志过滤
上面的配置中,我们使用了 Fluent Bit 的 Kubernetes 过滤器来解析 Kubernetes Pod 的日志。这个过滤器会自动解析 Kubernetes Pod 的日志,并将其转换为 JSON 格式。这样,我们就可以在 Elasticsearch 或其他服务中对日志进行搜索和分析。
但是正常情况下我们不需要这么多的日志,所以我们需要对日志进行过滤,只保留我们需要的日志,我们添加了一个 Match 和 Kubernetes 过滤器,以只采集来自特定命名空间的日志,或者根据需要调整过滤条件。
#...
fluent-bit.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
Merge_Log On
[FILTER]
Name grep
Match kube.*
# 过滤特定命名空间的日志
Regex kubernetes['namespace_name'] your_namespace
# 或者过滤特定容器名的日志
# Regex kubernetes['container_name'] your_container_name
在 Fluent Bit 配置中,可以使用多个 [FILTER] 条目来应用一系列的日志处理和过滤规则。每个 [FILTER] 都可以根据不同的需求来进行特定的过滤或数据修改。例如,你可以使用一个过滤器来添加 Kubernetes 元数据,然后使用另一个过滤器来基于这些元数据过滤日志。
上面的第一个过滤块采集的系统的元数据
例如下面我想只采集业务相关的日志,不去采集系统的日志,那么可以这样配置
[FILTER]
Name grep
Match kube.*
# 排除系统命名空间的日志
Exclude kubernetes['namespace_name'] ^(kube-system|kube-public|kube-node-lease)$
部署日志服务
kubectl create ns zincsearch
helm install zincsearch helm/zincsearch -n zincsearch
然后继续修改上面的配置,让 Fluent Bit 将日志输出到 zincsearch 这个服务上
[OUTPUT]
Name es
Match *
Path /api
Index syslog
Type journal
Host zinc.common-namespace.svc.cluster.local
Port 4080
Generate_ID On
HTTP_User admin
HTTP_Passwd 123456
修改配置后,删除原来的 DaemonSet,重新部署
kubectl delete pods -l k8s-app=fluent-bit-logging -n kube-system
完整的配置
注意,这个日志不是看 log 的输出进行正则的,而是需要在 log 文件中确认,因为有些内容是在 log 文件中没有的,比如 upstream_name,所以需要在 log 文件中确认
不确定是否正则写正确,可以使用 https://rubular.com/r/tjUt3Awgg4 确定
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: kube-system
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
[INPUT]
Name tail
Tag traefik
Path /var/log/containers/traefik*.log
Parser traefik_parser
DB /var/log/flb_traefik.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[FILTER]
Name kubernetes
Match traefik
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
Merge_Log Off
Keep_Log Off
K8S-Logging.Parser Off
K8S-Logging.Exclude Off
Labels Off
Annotations Off
[FILTER]
Name grep
Match traefik
Exclude upstream_name \b(ping@internal|common-namespace|kube-system-traefik|drone-drone-ingressroute)\b
[FILTER]
Name modify
Match traefik
Add _cluster_name ${CLUSTER_NAME}
Add _node_name ${HOSTNAME}
Add _node_ip ${NODE_IP}
[OUTPUT]
Name es
Match traefik
Host zinc.common-namespace.svc.cluster.local
Path /api
Port 4080
Logstash_Format On
Logstash_Prefix ingress-logs
Logstash_DateFormat %Y.%m.%d
Index ingress-logs-%Y.%m.%d
Type _doc
Generate_ID On
HTTP_User admin
HTTP_Passwd 123456
Replace_Dots On
Retry_Limit False
parsers.conf: |
[PARSER]
Name traefik_parser
Format regex
Regex ^(?<k8s_time>[^ ]+) (?<stream>stdout|stderr) [^ ]+ (?<remote_ip>[^ ]+) - - \[(?<http_time>[^\]]+)\] "(?<method>\S+)? (?<path>[^\s]+)? (?<http_version>[^"]+)?" (?<status>[0-9]+) (?<body_bytes_sent>[^ ]+) "(?<http_referer>[^"]+)" "(?<http_user_agent>[^"]+)" (?<request_id>[^ ]+) "(?<upstream_name>[^"]+)" "(?<upstream_ip>[^"]+)" (?<request_time>[^ ]+)ms$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
下面是服务启动的配置
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: kube-system
labels:
k8s-app: fluent-bit-logging
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:latest
imagePullPolicy: Always
env:
- name: CLUSTER_NAME
value: "k3s-cluster"
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
volumeMounts:
- name: varlog
mountPath: /var/log
- name: config-volume
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: config-volume
configMap:
name: fluent-bit-config