Kubernetes容器日志处理方案

Kubernetes容器日志处理方案

50-Kubernetes容器日志处理方案

0 前言

k8s里面对容器日志的处理都叫cluster-level-logging,即该日志处理系统,与容器、Pod及Node的生命周期完全无关。这种设计当然为保证,无论容器挂、Pod被删,甚至节点宕机,应用日志依然可被正常获取。

对于一个容器,当应用把日志输出到 stdout、stderr后,容器项目默认就会把这些日志输出到宿主机上的JSON文件。你就能kubectl logs看到这些容器的日志。

该机制就是本文要讲解的容器日志收集的基础假设。而若你的应用是把文件输出到其他地方,如容器里的某个件或输出到远程存储,那就属特殊情况。

k8s本身不会为你做容器日志收集,为实现cluster-level-logging,你要在部署集群时,提前规划日志方案。

而 k8s本身推荐:

1 三种日志方案

1.1 在 Node 上部署 logging agent

将日志文件转发到后端存储里保存。方案架构图:

alt

logging agent ,一般以 DaemonSet 运行在节点,然后将宿主机上的容器日志目录挂载进去,最后由 logging-agent 把日志转发出去。

如可通过 Fluentd 作为宿主机上的 logging-agent,然后把日志转发到远端的 ES 保存供检索。

具体操作:这篇文档。

很多 k8s 部署里,会自动启用 logrotate,在日志文件超过10MB 时自动对日志文件rotate。

可见 Node 上部署logging agent最大优点:一个节点只需部署一个 agent,且不会对应用和 Pod 有任何侵入性。所以,该方案最常用。

但不足在于,它要求应用输出的日志,须直接输出到容器的 stdout、stderr 。所以,k8s 容器日志方案第二种,就是对这种特殊情况的处理,即:当容器的日志只能输出到某些文件,可通过一个 sidecar 容器把这些日志文件重新输出到 sidecar 的 stdout、stderr,就能继续使用第一种方案。

方案具体工作原理:

alt 比如,现在我的应用 Pod 只有一个容器,它会把日志输出到容器里的/var/log/1.log 和2.log 这两个文件里。这个 Pod 的 YAML 文件如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    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: {}

在这种情况下,你用 kubectl logs 命令是看不到应用的任何日志的。而且我们前面讲解的、最常用的方案一,也是没办法使用的。

那么这个时候,我们就可以为这个 Pod 添加两个 sidecar容器,分别将上述两个日志文件里的内容重新以 stdout 和 stderr 的方式输出出来,这个 YAML 文件的写法如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    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: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

这时候,你就可以通过 kubectl logs 命令查看这两个 sidecar 容器的日志,间接看到应用的日志内容了,如下所示:

$ kubectl logs counter count-log-1
0: Mon Jan 1 00:00:00 UTC 2001
1: Mon Jan 1 00:00:01 UTC 2001
2: Mon Jan 1 00:00:02 UTC 2001
...
$ kubectl logs counter count-log-2
Mon Jan 1 00:00:00 UTC 2001 INFO 0
Mon Jan 1 00:00:01 UTC 2001 INFO 1
Mon Jan 1 00:00:02 UTC 2001 INFO 2
...

由于 sidecar 跟主容器之间是共享 Volume 的,所以这里的 sidecar 方案的额外性能损耗并不高,也就是多占用一点 CPU 和内存罢了。

但需要注意的是,这时候,宿主机上实际上会存在两份相同的日志文件:一份是应用自己写入的;另一份则是 sidecar 的 stdout 和 stderr 对应的 JSON 文件。这对磁盘是很大的浪费。所以说,除非万不得已或者应用容器完全不可能被修改,否则我还是建议你直接使用方案一,或者直接使用下面的第三种方案。

第三种方案,就是通过一个 sidecar 容器,直接把应用的日志文件发送到远程存储里面去。也就是相当于把方案一里的 logging agent,放在了应用 Pod 里。这种方案的架构:

alt 这种方案里,你的应用还可直接把日志输出到固定的文件里而不是 stdout,你的 logging-agent 还可以使用 fluentd,后端存储还可是 ElasticSearch。只不过, fluentd 的输入源变成应用的日志文件。一般会把 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>

然后,我们在应用 Pod 的定义里,就可以声明一个Fluentd容器作为 sidecar,专门负责将应用生成的1.log 和2.log转发到 ElasticSearch 当中。这个配置,如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    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: k8s.gcr.io/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

可以看到,这个 Fluentd 容器使用的输入源,就是通过引用我们前面编写的 ConfigMap来指定的。这里我用到了 Projected Volume 来把 ConfigMap 挂载到 Pod 里。如果你对这个用法不熟悉的话,可以再回顾下第15篇文章《 深入解析Pod对象(二):使用进阶》中的相关内容。

这方案虽部署简单,且对宿主机非常友好,但这 sidecar 容器很可能会消耗较多的资源,甚至拖垮应用容器。并且,由于日志还是没有输出到 stdout上,所以你通过 kubectl logs 是看不到任何日志输出的。

总结

k8s 项目对容器应用日志的收集方式。综合对比以上三种方案,建议你将应用日志输出到 stdout 和 stderr,然后通过在宿主机上部署 logging-agent 的方式来集中处理日志。

这方案管理简单,kubectl logs 也可用,可靠性高,且宿主机本身很可能就自带 rsyslogd 等非常成熟的日志收集组件来供使用。

还有一种方式就是在编写应用时,就直接指定好日志的存储后端:

alt 这种方案下,k8s 就完全不必操心容器日志收集,这对于本身已经有完善的日志处理系统的公司很友好。

无论哪种方案,须及时将这些日志文件从宿主机上清理掉或给日志目录专门挂载一些容量巨大的远程盘。一旦主磁盘分区被打满,整个系统就可能会陷入崩溃。

FAQ

日志量很大时,直接将日志输出到容器 stdout 和 stderr上,有什么隐患?解决办法?

获取更多干货内容,记得关注我哦。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/4.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【青牛科技】GC4921替代BD6921/罗姆在水泵、筋膜枪、吸尘器和电动工具中的应用

在现代电动设备中&#xff0c;电机驱动控制器的选择对设备的性能和效率至关重要。GC4921作为一种新型的电机驱动控制器&#xff0c;逐渐被视为BD6921/罗姆的替代品。本文将对GC4921与BD6921进行对比&#xff0c;探讨其在水泵、筋膜枪、吸尘器和电动工具等设备中的应用优势。 1…

Android OpenGL ES详解——裁剪Scissor

目录 一、概念 二、如何使用 1、开启裁剪测试 2、关闭裁剪测试 3、指定裁剪窗口&#xff08;位置和大小&#xff09; 4、裁剪应用举例 三、窗口、视⼝和裁剪区域三者区别 四、源码下载 一、概念 定义1&#xff1a; 裁剪是OpenGL中提⾼渲染的⼀种方式&#xff0c;只刷新…