使用日志记录运算符访问 Kubernetes 日志的方法

192 阅读3分钟

你需要从几个(通常是不同的)来源收集数据。通常,当我们调试一个集群时,我们需要它的日志流的输出来验证其日志堆栈的一致性。在这种情况下,我们的想法是偷看由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连接日志运营商。

image.png

第一次尝试,使用 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和着色扩展相结合。