一、前言

集群级/应用日志通过将日志数据从容器或节点中解耦,并将其发送到集中的日志存储或处理系统,使日志数据在整个集群中可见。

优势:即使容器或节点发生故障,仍然可以访问日志数据以进行故障排查和监控。

二、应用日志分析

应用容器标准输出流:

[root@master01 9]# vim podstdr.yaml
apiVersion: v1
kind: Pod
metadata:
  name: podstdr
spec:
  containers:
    - name: podstdr
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30 
      args:
        [
          /bin/sh,
          -c,
          'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done',
        ]

# 应用
[root@master01 9]# kaf podstdr.yaml

# 验证
[root@master01 9]# kgp | grep podstdr
podstdr                                    1/1     Running   0              14s

创建完成后,可以使用 kubectl logs 命令查看日志信息:

$ kubectl logs podstdr
0: Mon Apr 14 23:52:56 UTC 2025
1: Mon Apr 14 23:52:57 UTC 2025
2: Mon Apr 14 23:52:58 UTC 2025
...
...

三、Pod日志收集

Kubernetes 集群本身不提供日志收集的解决方案,一般主要有 4 种方案来做日志收集:

  • 在节点上运行一个 agent 来收集日志;
  • 在 Pod 中包含一个 sidecar 容器来收集应用日志;
  • 在 Pod 中启动一个 sidecar Agent容器来收集应用日志;
  • 直接在应用程序中将日志信息推送到采集后端;

3.1 节点代理方式日志采集

Day09-可观察性-ELK&Loki-图1

  • DaemonSet 控制器运行该应用程序,每个节点上运行一个日志收集的agent来采集日志数据;
  • 日志agent用一个容器来运行,可以访问该节点上所有应用程序容器的日志文件所在目录。
  • 对应用程序没有任何侵入性,仅适用于收集输出到 stdout 和 stderr 的应用程序日志。

3.2 Sidecar 方式收集容器日志

如果应用程序的日志是输出到容器中的某个日志文件呢?

Day09-可观察性-ELK&Loki-图2在 Pod 中启动另外一个 sidecar 容器,直接将应用程序的日志通过这个容器重新输出到 stdout,即可完成日志收集。

主要逻辑:将应用程序中的日志进行重定向打印,逻辑非常简单,开销很小,由于输出 到了 stdout 或者 stderr,故也可使用 kubectl logs来查看日志。

示例:Pod 中将日志记录在容器的两个本地文件之中

apiVersion: v1
kind: Pod
metadata:
  name: podstdr
spec:
  containers:
    - name: podstdr
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30
      args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$i: $(date)" >> /var/log/1.log;
            echo "$(date) INFO $i" >> /var/log/2.log;
            i=$((i+1));
            sleep 1;
          done
      volumeMounts:
        - name: varlog
          mountPath: /var/log
  volumes:
    - name: varlog
      emptyDir: {}

利用另外一个 sidecar 容器去获取容器中的日志文件,然后将日志重定向到自己的 stdout 流中。

可以将上面的 YAML 文件做如下修改: two-files-pod-sidecar.yaml

[root@master01 9]# vim two-files-pod-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: podstdr2
spec:
  containers:
    - name: podstdr2
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30
      args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$i: $(date)" >> /var/log/1.log;
            echo "$(date) INFO $i" >> /var/log/2.log;
            i=$((i+1));
            sleep 1;
          done
      volumeMounts:
        - name: varlog
          mountPath: /var/log
    - name: count-log-1
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30
      args: [/bin/sh, -c, "tail -n+1 -f /var/log/1.log"]
      volumeMounts:
        - name: varlog
          mountPath: /var/log
    - name: count-log-2
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30
      args: [/bin/sh, -c, "tail -n+1 -f /var/log/2.log"]
      volumeMounts:
        - name: varlog
          mountPath: /var/log
  volumes:
    - name: varlog
      emptyDir: {}

运行成功后,我们可以通过下面的命令来查看日志的信息:

$ kubectl logs -f podstdr2 count-log-1
0: Sat Jul 1 11:34:56 UTC 2023
1: Sat Jul 1 11:34:57 UTC 2023
2: Sat Jul 1 11:34:58 UTC 2023
3: Sat Jul 1 11:34:59 UTC 2023
...

$ kubectl logs -f podstdr2 count-log-2
Sat Jul 1 11:34:56 UTC 2023 INFO 0
Sat Jul 1 11:34:57 UTC 2023 INFO 1
Sat Jul 1 11:34:58 UTC 2023 INFO 2
Sat Jul 1 11:34:59 UTC 2023 INFO 3
...

# 环境复原
[root@master01 9]# k delete -f two-files-pod-sidecar.yaml

这样前面节点上的日志采集 agent 就可以自动获取这些日志信息,而不需要其他配置。

这种方法虽然可以解决上面的问题,但是也有一个明显的缺陷,就是日志不仅会在原容器文件中保留下来,还会通过 stdout 输出后占用磁盘空间,这样无形中就增加了一倍磁盘空间。

3.3 Sidecar 运行日志采集 agent

主要流程:

  1. 部署:在同一个宿主机上部署主应用程序的容器和 Sidecar 容器。主应用程序容器 负责运行应用程序本身,而Sidecar 容器则专门负责运行日志采集 agent。
  2. 连接:Sidecar 容器与主应用程序容器之间建立连接,以获取需要采集的日志数据。 通过共享文件系统方式实现。
  3. 日志采集:Sidecar 容器中运行的日志采集 agent 监听主应用程序容器的日志输 出,并将日志数据收集起来。可使用特定的方式来解析不同的日志格式。
  4. 存储或转发:采集到的日志数据根据需求进行转发。推送到消息队列或直接推送到ES服务等。

下面是 Kubernetes 官方的一个 fluentd 的配置文件示例,使用 ConfigMap 对象来保存:

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>

上面的配置文件是配置收集原文件 /var/log/1.log 和 /var/log/2.log 的日志数据,然后通过 google_cloud 这个插件将数据推送到 EFK 后端。

下面是我们使用上面的配置文件在应用程序中运行一个 fluentd 的容器来读取日志数据:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
    - name: count
      image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.30
      args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$i: $(date)" >> /var/log/1.log;
            echo "$(date) INFO $i" >> /var/log/2.log;
            i=$((i+1));
            sleep 1;
          done
      volumeMounts:
        - name: varlog
          mountPath: /var/log
    - name: count-agent
      image: registry.cn-hangzhou.aliyuncs.com/github_images1024/fluentd-gcp:1.30
      env:
        - name: FLUENTD_ARGS
          value: -c /etc/fluentd-config/fluentd.conf
      volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: config-volume
          mountPath: /etc/fluentd-config
  volumes:
    - name: varlog
      emptyDir: {}
    - name: config-volume
      configMap:
        name: fluentd-config

如上主要功能:容器 count-agent 就会将 count 容器中的日志进行收集然后上传。

3.4 直接从应用程序收集日志

Day09-可观察性-ELK&Loki-图3

通过在应用程序代码中添加日志输出语句,将应用程序的关键信息记录下来。这种方式有以下几个主要步骤:

  1. 添加日志库:在应用程序中引入适合的日志库或框架,如Logback、Log4j、或是应 用程序特定的日志库。
  2. 配置日志属性:根据需要,配置日志属性,如日志的级别(如DEBUG、INFO、 ERROR等)、输出格式、输出位置等。
  3. 在应用程序中添加日志语句:在关键的代码块、重要的事件或异常处理中添加合适 的日志输出语句。日志语句应该包含相关的上下文信息,如时间戳、请求参数、业 务数据等,以便更好地理解日志内容。
  4. 输出和存储:当应用程序执行到日志语句时,日志库会根据配置将日志输出到指定 的目标,如控制台、文件、数据库等。
  5. 日志级别控制:通过设置日志级别,可以控制哪些级别的日志会被输出和记录。

直接从应用程序收集日志的好处:

  • 应用程序开发者可以更精确地控制日志输出内容和格式。
  • 日志与应用程序的关联更紧密,方便定位和排查问题。
  • 可以根据不同的环境和需求调整日志级别和目标,以达到最佳的日志记录效果。

四、总结

  1. 节点代理方式:使用节点代理来采集日志。独立的代理程序,负责监控和收集该节点上运行的容器或应用程序的日志。
  2. Sidecar 方式收集容器日志:在容器编排平台(例如 Kubernetes)中,可以使用 Sidecar 容器模式来收集主要应用程序容器的日志。这种方式中,一个专门的 Sidecar 容器与主应用程序容器一起部署在同一个 Pod 中,并负责收集主应用程序容器的日志。
  3. Sidecar 运行日志采集 Agent:类似于上述的 Sidecar 方式,但不仅仅收集主应用程 序容器的日志,还负责运行日志采集。利用容器的特性(如共享卷或网络通信)来获取主应用程序的日志,并将其发送到中央日志存储或分析系统。
  4. 直接从应用程序收集日志:应用程序本身负责进行日志的记录和发送。将日志数据直接发送到中央日志存储或分析系统。