Fluent Bit 日志收集
fluent-bit 是一种在 Linux,OSX 和 BSD 系列操作系统运行,兼具快速、轻量级日志处理器和转发器。它非常注重性能,通过简单的途径从不同来源收集日志事件
数据分析通常发生在数据存储和数据库索引之后,但对于实时和复杂的分析需求,在日志处理器中处理仍在运行的数据会带来很多好处,这种方法被称为边缘流处理(Stream Processing on the Edge)
工作原理
日志通过数据管道从数据源发送到目的地,一个数据管道通常由 Input、Parser、Filter、Buffer、Routing 和 Output组成
- Input:用于从数据源抽取数据,一个数据管道中可以包含多个 Input
- Parser:负责将 Input 抽取的非结构化数据转化为标准的结构化数据,每个 Input 均可以定义自己的 Parser(可选)
- Filter:负责对格式化数据进行过滤和修改。一个数据管道中可以包含多个 Filter,Filter 会顺序执行,其执行顺序与配置文件中的顺序一致
- Buffer:用户缓存经过 Filter 处理的数据,默认情况下 Buffer 把 Input 插件的数据缓存到内存中,直到路由传递到 Output 为止
- Routing:将 Buffer 中缓存的数据路由到不同的 Output
- Output:负责将数据发送到不同的目的地,一个数据管道中可以包含多个 Output
Fluent Bit 支持多种类型的 Input、Parser、Filter、Output 插件,可以应对各种场景:
关键概念
事件或记录(Event or Record)
Fluent Bit 检索到的每一个属于日志或指标的输入数据都被视为事件或记录,以 Syslog 文件为例:
Jan 18 12:52:16 flb systemd[2222]: Starting GNOME Terminal Server
Jan 18 12:52:16 flb dbus-daemon[2243]: [session uid=1000 pid=2243] Successfully activated service 'org.gnome.Terminal'
Jan 18 12:52:16 flb systemd[2222]: Started GNOME Terminal Server.
Jan 18 12:52:16 flb gsd-media-keys[2640]: # watch_fast: "/org/gnome/terminal/legacy/" (establishing: 0, active: 0)
它包含四行,它们代表四个独立的事件。在内部,一个事件总是有两个组件(以数组形式):
[TIMESTAMP, MESSAGE]
过滤(Filtering)
在某些情况下,需要对事件内容执行修改,更改、填充或删除事件的过程称为过滤
有许多需要过滤的用例,如:
- 向事件附加特定信息,如 IP 地址或元数据
- 选择一个特定的事件内容
- 处理匹配特定模式的事件
标签(Tag)
每一个进入 Fluent Bit 的事件都会被分配一个标签。这个标签是一个内部字符串,路由器稍后会使用它来决定必须通过哪个 Filter 或 Output 阶段
大多数标签都是在配置中手动分配的。如果没有指定标签,那么 Fluent Bit 将指定生成事件的输入插件实例的名称作为标签
唯一不分配标签的输入插件是 Forward 输入。这个插件使用名为 Forward 的 Fluentd wire 协议,其中每个事件都有一个相关的标签。Fluent Bit 将始终使用客户端设置的传入标签
标签记录必须始终具有匹配规则。要了解关于标签和匹配的更多信息,请查看路由部分
时间戳(Timestamp)
时间戳表示事件被创建的时间,每个事件都包含一个相关联的时间戳。时间戳的格式:
SECONDS.NANOSECONDS
Seconds 是自 Unix epoch 以来经过的秒数。Nanoseconds 是小数秒或十亿分之一秒
时间戳总是存在的,要么由 Input 插件设置,要么通过数据解析过程发现
匹配(Match)
Fluent Bit 允许将收集和处理的事件传递到一个或多个目的地,这是通过路由阶段完成的。匹配表示一个简单的规则,用于选择与已定义规则匹配的事件
结构化消息(Structured Messages)
源事件可以有或没有结构。结构在事件消息中定义了一组键和值。作为一个例子,考虑以下两个消息:
没有结构化的消息
"Project Fluent Bit created on 1398289291"
结构化消息:
{"project": "Fluent Bit", "created": 1398289291}
在较低的级别上,两者都只是字节数组,但结构化消息定义了键和值,具有结构有助于实现对数据快速修改的操作
Fluent Bit 总是将每个事件消息作为结构化消息处理。出于性能原因,我们使用名为 MessagePack 的二进制序列化数据格式
可以把 MessagePack 看作是 JSON 的二进制版本
容器日志
docker 容器中输出到 stdout 的日志,都会以 *-json.log 的命名方式保存在 /var/lib/docker/containers/ 目录下, 且在 /var/log/containers/ 目录下生成的软链接,如:
{"log":"10.244.0.1 - - [24/Feb/2023:17:33:29 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.29.0\" \"-\"\n","stream":"stdout","time":"2023-02-24T17:33:29.239451924Z"}
其中包含三个字段:log、stream 和 time,当然仅仅这三个字段还不够,还需要知道容器属于哪个POD,信息越详细越好,这些 fluent-bit 都通过插件支持好了
部署
参考文档:installation
先创建 logging
命名空间,从 fluent-bit-kubernetes-logging 仓库里执行安装 role、role-binding、service-account、configmap 和 ds 资源
示例配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
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.conf
@INCLUDE output.conf
@INCLUDE filter.conf
input.conf: |
[INPUT]
Name mem # 获取内存信息
Tag memory
[INPUT]
Name tail # 获取docker日志文件
Tag kube.*
Path /var/log/containers/*.log
Path_Key logfile
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB # 内存缓冲大小
Skip_Long_Lines On
Refresh_Interval 5
output.conf: |
[OUTPUT]
Name es # 输出到elasticsearch
Match memory # 匹配mem
Host 192.168.0.111
Port 9200
Index mem_index
Trace_Error On
[OUTPUT]
Name es
Match kube.* # 匹配docker日志
Host 192.168.0.111
Port 9200
Index pod_index
filter.conf: |
[FILTER]
Name record_modifier
Match kube.*
# 手动添加nodename字段,值为fluent-bit的环境变量NODE_NAME,ds中配置env示例:
#env:
# - name: NODE_NAME
# valueFrom:
# fieldRef:
# fieldPath: spec.nodeName
Record nodename ${NODE_NAME}
[FILTER]
Name kubernetes # 关联的k8s元信息
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
parsers.conf: |
[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]
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
没有热更新,修改配置后删除 pod
kubectl get pod -n logging | grep fluent-bit | awk '{print $1}' | xargs kubectl delete pod -n logging