你需要从几个(通常是不同的)来源收集数据。通常,当我们调试一个集群时,我们需要它的日志流的输出来验证其日志堆栈的一致性。在这种情况下,我们的想法是偷看由fluentd传输的消息。让我们来看看我们如何完成这个任务。
琐碎的解决方案 🔗︎
在简单的情况下,我们可以使用一个stdout 过滤器。这个解决方案对于低流量来说效果很好。下面是一个例子,说明它是如何做的。
apiVersion: logging.banzaicloud.io/v1beta1
kind: Flow
metadata:
name: debug-flow
spec:
filters:
- parser:
remove_key_name_field: true
reserve_data: true
parse:
type: json
- stdout:
output_type: json
selectors: {}
localOutputRefs:
- null-output
配置上线后,我们可以对fluentd的日志进行跟踪。
$ kubectl exec -it logging-demo-fluentd-0 cat /fluentd/log/out
2020-09-09 09:37:45 +0000 [info]: #0 starting fluentd worker pid=15 ppid=6 worker=0
2020-09-09 09:37:45 +0000 [info]: #0 [main_forward] listening port port=24240 bind="0.0.0.0"
2020-09-09 09:37:45 +0000 [info]: #0 fluentd worker is now running worker=0
2020-09-09 09:37:45 +0000 [info]: Hello World!
优点。
缺点。
- 高流量会淹没stdout
- 它可能会对主流量产生副作用
- 你需要处理多个fluentd实例(可能很麻烦)
一个更可靠的解决方案 🔗︎
另一种检查Flow输出的方法是将输出指向一个已知的目的地。我们的第一个想法是使用一个fluentd实例,并将所有内容保存到输出文件中。这个架构很简单。我们用一个服务设置了一个fluentd pod,然后我们exec 到这个pod。虽然听起来很简单,但有几件事情我们必须要确定。
- 创建一个 fluentd 配置(一个 configmap)。
- 创建一个fluentd pod(用config reloader或者你需要杀死pod来重新加载它)
- 创建一个指向Pod的服务
- 执行到Pod中,并跟踪该文件
我们决定不采用这个方案。我们将不得不创建大量的资源来使其工作,而且它仍然没有我们希望的那么方便。
优点。
- 从主要的日志流程中分离出debug fluentd
缺点。
- 更改是通过configmap进行的
- 容器应该有其他工具,比如:grep、bash
将日志传送到本地机器上 🔗︎
我们最喜欢的方法是在你的本地计算机上跟踪日志。而这其实并不难。你可能对我们的开源项目kurun很熟悉。它利用inlet在你的本地机器和Kubernetes集群之间创建一个隧道。
这个想法是在你的本地计算机上启动一个fluentd容器,用fluentd连接日志运营商。
第一次尝试,使用 fluentd 的 forward-protocol 🔗︎
Fluentd有一个内置的协议来在fluentd实例之间传输日志。现在把这个堆栈放在一起。
我们从一个kurun 命令开始,在Kubernetes集群和本地机器之间建立一个连接
kurun port-forward --servicename fluentd-debug --serviceport 24222 localhost:24222
我们创建一个fluentd.conf ,用一个正向输入配置,在标准输出上打印出所有的消息。
@type forward
port 24222
@type stdout
我们可以启动本地的fluentd 容器,在前面kurun命令中定义的端口上监听。
docker run -p 24222:24222 -v $PWD/fluentd.conf:/fluent/fluent.conf --rm banzaicloud/fluentd:v1.11.2-alpine-2 -c /fluent/fluent.conf
之后,我们可以创建一个Output 配置,指向我们的kurun 服务。
apiVersion: logging.banzaicloud.io/v1beta1
kind: Output
metadata:
name:
spec:
forward:
servers:
- host: fluentd-debug
port: 24222
buffer:
timekey: 10s
timekey_wait: 3s
timekey_use_utc: true
最后一步是改变Flow ,将forward-output-debug 添加到localOutputRefs
apiVersion: logging.banzaicloud.io/v1beta1
kind: Flow
metadata:
name: flow-sample
namespace: default
spec:
filters:
...
localOutputRefs:
- ...
- forward-output-debug
match:
...
只有一个问题,那就是kurun 只能传输HTTP流量:无奈。
从TCP到HTTP 🔗︎
在我们发现这个小小的设计缺陷后,我们把forward 协议改为http 。
将本地源设置为http 。
@type http
@id http_input
port 24222
@type json
@type stdout
并将输出定义改为http 。
apiVersion: logging.banzaicloud.io/v1beta1
kind: Output
metadata:
name: http-output-debug
spec:
http:
endpoint: http://kurun:24224
buffer:
flush_interval: 10s
timekey: 5s
timekey_wait: 1s
flush_mode: interval
format:
type: json
不幸的是,这并不奏效,因为HTTP输入被设计为接收来自应用程序的日志,通过http 发送成批的日志。就像下面的例子。
curl -X POST -d 'json={"foo":"bar"}' http://localhost:9880/app.log
内置的fluentd HTTP输出会像这样发送以行为界的新JSON。
{"timestamp": "2020-09-09 09:37:45", "log":"Hello World!"}
{"timestamp": "2020-09-09 09:37:45", "log":"Hello World!"}
{"timestamp": "2020-09-09 09:37:45", "log":"Hello World!"}
所以我们需要调整配置,以大数组的形式发送日志批次。我们只需要将json_array 变量设置为true 。
apiVersion: logging.banzaicloud.io/v1beta1
kind: Output
metadata:
name: http-output-debug
spec:
http:
endpoint: http://kurun:24224
json_array: true
buffer:
flush_interval: 10s
timekey: 5s
timekey_wait: 1s
flush_mode: interval
format:
type: json
最后,我们有了一个工作设置。我们可以使用grep或者直接在本地编辑fluentd的配置,为我们的调试流程增加一些额外的功能。
优点。
- 将debug fluentd与主日志流程分开
- 所有的工具都在你的电脑上,更容易管理
缺点。
- 你需要从你的集群中获取流量
+1 使用msgpack 🔗︎
另一个有趣的事实是,可以通过HTTP使用msgpack格式。
要做到这一点,您需要将format 设置为msgpack。
@type http
endpoint http://some.your.http.endpoint:9882/your-awesome-path
@type msgpack
flush_interval 2s
然后,在接收端,你需要将信息parse 为msgpack。
@type http
port 9882
bind 0.0.0.0
@type msgpack
@type json
这里的启示是,有了强大的构建模块,建立更复杂的系统很容易。您可以将日志操作员的强大匹配机制与您的本地终端grep和着色扩展相结合。