简单的集群日志采集方案

背景

当一个程序运行时,为了掌握程序的运行情况,我们需要对程序进行日志采集。无论是传统的机器部署,还是基于 k8s 的服务部署,日志采集的整体逻辑是一样的: ① 日志落盘 ② 日志采集 ③ 日志处理 ④ 日志存储 ⑤ 日志展示 ⑥ 日志归档

本文,我们一起搭建一套简单的日志采集体系。

日志流程

日志落盘

  • 传统部署

传统服务运行时,可以通过将日志写到一个目录下的文件中。

TODO: 跑通一个日志库的完整使用

  • k8s

stdout 直接输出到容器日志文件

日志采集

日志处理

  • logstash
  • fluent-bit

日志存储

  • es
  • loki

日志展示

  • kibana

日志归档

  • es-rotate (这块儿流程跑一下)

基于日志的告警

其他

  • 找一套轻量的日志采集方案

实操

fluentd k8s 实操

由于集群的日志都会在当前机器的文件系统存放,所以实际上只需要起一个 fluentd 进程,并且能够读取日志目录即可。

如果在集群中,则需要以下操作: ① 启动 fluentd 的进程容器 ② 把 k8s 日志目录挂到容器中。 为了解决对 每台机器 的日志采集,需要采用部署中的 daemonset。

另外,由于一些插件可能会用到集群信息,需要给部署集绑定集群角色。

具体可以查看: fluentd 的官方文档

实际的部署文件如下: (参考官方案例)

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd
addonmanager.kubernetes.io/mode: Reconcile
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
labels:
k8s-app: fluentd
addonmanager.kubernetes.io/mode: Reconcile
rules:
- apiGroups:
- ""
resources:
- "namespaces"
- "pods"
verbs:
- "get"
- "watch"
- "list"
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
labels:
k8s-app: fluentd
addonmanager.kubernetes.io/mode: Reconcile
subjects:
- kind: ServiceAccount
name: fluentd
namespace: kube-system
apiGroup: ""
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: ""
---
kind: ConfigMap
apiVersion: v1
metadata:
name: fluentd
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: Reconcile
data:
input.conf: |-
<source>
@type tail
tag k8s.fluentd.log.*
path /var/log/containers/*.log
pos_file /var/log/k8s.fluentd.log.pos
refresh_interval 3s
read_from_head true
rotate_wait 30s
<parse>
@type regexp
expression ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
time_type string
time_format %Y-%m-%dT%H:%M:%S.%N%z
localtime true
keep_time_key true
</parse>
</source>

# 这些都是根据自己的情况配置,可以输出到文件、到es、到kafka、到logstash等,具体配置可以查看 [官方文档](https://docs.fluentd.org/output/file)
output.conf: |-
<match k8s.**>
@type file
path /xxx/log
add_path_suffix true
path_suffix ".log"
append true
<format>
@type json
</format>
<buffer tag>
@type file
path /xx/log/fluentd.log.buffer
chunk_limit_size 8MB
total_limit_size 64MB
flush_mode interval
flush_interval 1s
retry_max_interval 30
retry_forever true
flush_thread_count 4
</buffer>
</match>
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd
addonmanager.kubernetes.io/mode: Reconcile
spec:
selector:
matchLabels:
k8s-app: fluentd
template:
metadata:
labels:
k8s-app: fluentd
annotations:
seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
spec:
priorityClassName: system-node-critical
serviceAccountName: fluentd
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.15-debian-forward-amd64-1
imagePullPolicy: IfNotPresent
env:
- name: FLUENTD_ARGS
value: --no-supervisor -q
resources:
limits:
cpu: 1
memory: 3Gi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: config-volume
mountPath: /etc/fluent/config.d
- name: outputlog
mountPath: {{ EXT_K3S_LOG_PATH }}
terminationGracePeriodSeconds: 30
tolerations:
- operator: Exists
volumes:
- name: varlog
hostPath:
path: /var/log
- name: config-volume
configMap:
name: fluentd
- name: outputlog
hostPath:
path: {{ EXT_K3S_LOG_PATH }}

fluentbit k8s 实操

fluentd 是一个 ruby + c 的实现,可能还是有些问题吧,主要是多了一些系统依赖,于是又搞了一个 纯c 写的 fluentbit, 这有官方文档写的两者的关系和差别。 fluentd 的缺点是有 ruby 的环境依赖,优点是拥有 1000+ plugins。 fluentbit 的缺点是目前仅有 70+ 插件,优点是无环境依赖,一个二进制就可以跑。

k8s 使用 helm 部署,可以直接参考 官方文档, 这是默认的 values.yaml

对于一个日志采集器而言,主要就是配置这么几个环节(管道):

  • input: 用于配置从何处采集日志,例如文件夹、http、mqtt 等等
  • parser: 处理拿到的日志格式,例如 把 docker 日志处理成 json、把 ng 日志处理成 json 等等。
  • filter: 过滤操作,可以修改、添加一些信息,例如加 pod name 之类的。
  • output: 日志的输出,可以是 kafka、file、es、nats

值得一提的是,fluentbit 支持用 golang build 成 lib 的方式,具体可以参考 官方文档


Life is really simple, but we insist on making it complicated.
Confucius


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!