首页 > 其他分享 >容器日志收集与管理:让日志无处可逃

容器日志收集与管理:让日志无处可逃

时间:2023-06-25 13:32:09浏览次数:31  
标签:容器 00 log 无处可逃 var 日志 name


本文将详细介绍 Kubernetes 里对容器日志的处理方式。

首先需要明确,Kubernetes 里对容器日志的处理方式都叫作 cluster-level-logging,即这个日志处理系统与容器、Pod 以及节点的生命周期都完全无关。这种设计当然是为了保证无论容器不 工作、Pod 被删除,甚至节点宕机,依然可以正常获取应用的日志。

对于一个容器来说,当应用把日志输出到 stdout 和 stderr 之后,容器项目本身默认会把这些日志输出到宿主机上的一个 JSON 文件里。这样,通过 kubectl logs 命令就可以看到这些容器的日志了。

上述机制就是本节要讲解的容器日志收集的基础假设。如果你的应用是把文件输出到别处, 比如直接输出到容器里的某个文件里,或者输出到远程存储里,就另当别论了。当然,本节也会 介绍这些特殊情况的处理方法。

Kubernetes 本身实际上不会为你做容器日志收集工作。所以,为了实现上述 cluster-level-logging, 你需要在部署集群的时候提前规划具体的日志方案。Kubernetes 项目本身主要推荐了 3 种日志 方案。

第一种方案,在节点上部署 logging agent,将日志文件转发到后端存储里保存起来。这种方 案的架构如图 1 所示。

容器日志收集与管理:让日志无处可逃_hadoop

(图1)

不难看出,这里的核心就在于 logging agent,它一般会以 DaemonSet  的方式在节点上运行, 然后把宿主机上的容器日志目录挂载进去,最后由 logging-agent 把日志转发出去。

例如,我们可以把 Fluentd 项目作为宿主机上的 logging-agent,然后把日志转发到远端的 Elasticsearch 里保存起来供将来检索。具体的操作过程参见官方文档。另外,Kubernetes 的很多部署会自动为你启用logrotate,在日志文件大小超过10 MB时自动对日志文件进行切割(logrotate)操作。

可以看到,在节点上部署 logging agent 最大的优点在于一个节点仅需部署一个agent,并且对应用和Pod 没有任何侵入性。所以,在社区中该方案最常用。但是,这种方案的明显不足之处在于,它要求应用输出的日志都必须直接输出到容器的 stdout 和 stderr 里。

第二种 Kubernetes 容器日志方案处理的就是这种特殊情况:当容器的日志只能输出到某些文件里时,我们可以通过一个 sidecar 容器把这些日志文件重新输出到 sidecar 的 stdout 和 stderr 上, 这样就能够继续使用第一种方案了。这种方案的具体工作原理如图 2 所示。

容器日志收集与管理:让日志无处可逃_运维_02

(图2)

比如,现在我的应用 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 里。这种方案的架构如图 3 所示。

容器日志收集与管理:让日志无处可逃_kubernetes_03

(图3)

在这种方案下,你的应用还可以直接把日志输出到固定的文件里,而不是 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 里。需要注意的是,这种方案虽然部署简单,并且对宿主机非常友好,但是这个 sidecar 容器很可能会消耗较多资源,甚至拖垮应用容器。并且,由于日志还是没有输出到 stdout 上,因此通过 kubectl logs 看不到任何日志输出。以上就是 Kubernetes 项目管理容器应用日志最常用的 3 种手段。

小结 

本文详细讲解了 Kubernetes 项目对容器应用日志的收集方式。上述 3 种方案中,最常用的一种方式就是将应用日志输出到 stdout 和 stderr,然后通过在宿主机上部署 logging-agent 来集中处 理日志。这种方案不仅管理简单,可靠性高,kubectl logs 也可以用,而且宿主机很可能自带了 rsyslogd 等成熟的日志收集组件供使用。除此之外,还有一种方式:在编写应用时直接指定好日志的存储后端,如图  4 所示。

容器日志收集与管理:让日志无处可逃_java_04

(图4)

在这种方案下,Kubernetes 就完全不必操心容器日志的收集了,这对于已经有完善的日志处 理系统的公司来说是一个非常好的选择。

最后需要指出的是,无论采用哪种方案,你都必须配置好宿主机上的日志文件切割和清理工作,或者给日志目录专门挂载一些容量巨大的远程盘。否则,一旦主磁盘分区占满,整个系统就可能陷入崩溃状态,这是非常麻烦的。

小思考

1.当日志量很大时,直接将日志输出到容器 stdout 和 stderr 上有无隐患?有何解决办法?

2.你还有哪些容器日志收集方案?

容器日志收集与管理:让日志无处可逃_运维_05

  • CNCF TOC成员张磊重磅作品,近4万读者一致好评
  • 基于Kubernetes v1.18,深入剖析核心原理
  • 后端技术人员与基础平台工程师必读
  • 打通Kubernetes的任督二脉,掌握容器技术体系的精髓

容器日志收集与管理:让日志无处可逃_docker_06


标签:容器,00,log,无处可逃,var,日志,name
From: https://blog.51cto.com/u_15767091/6545211

相关文章

  • 02 | 日志系统:一条SQL更新语句是如何执行的?
    以下内容出自《MySQL实战45讲》02|日志系统:一条SQL更新语句是如何执行的?查询语句的那套流程,更新语句也会走一遍。更新流程中和查询不一样的是,更新流程中涉及了两个重要的日志模块。redolog(重做日志)和binglog(归档日志)。redolog-InnoDb特有作用:用来记录对磁......
  • 容器基础-- namespace,Cgoup 和 UnionFS
    Namespace什么是Namespace?这里的"namespace"指的是Linuxnamespace技术,它是Linux内核实现的一种隔离方案。简而言之,Linux操作系统能够为不同的进程分配不同的namespace,每个namespace都具有独立的资源分配,从而实现了进程间的隔离。如果你的Linux安装了GCC,可以通过......
  • linux 系统清理 systemctl 日志
    背景生产环境日志文件太多导致磁盘空间不足,临时删除一些systemctl日志解决步骤首先,停止systemd-journald服务:sudosystemctlstopsystemd-journald清理日志文件:sudojournalctl--vacuum-size=100M上述命令将清理超过100MB大小的日志文件。你可以根据需要调整--vacuum-size参数......
  • 查找容器网卡对应的宿主机veth
    dockerrun-dnginx:1.91.容器内查看网卡索引值dockerexec-it2740f92cat/sys/class/net/eth0/iflink2.根据网卡索引值查找系统网卡ipa|grep"7:"......
  • [转载] 常用 Git 命令清单 - 阮一峰的网络日志
    常用Git命令清单作者:阮一峰日期:2015年12月9日我每天使用Git,但是很多命令记不住。一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。下面是我整理的常用Git命令清单。几个专用名词的译名如下。Workspace:工作区Index/Stag......
  • python入门(六):数据结构和容器
    Python数据结构和容器指南原文|大纲|首页在Python中,数据结构和容器用于存储和组织数据。它们提供了不同的方式来操作和访问数据,以满足不同的需求。了解Python的数据结构和容器对于编写高效和灵活的代码至关重要。列表(List)列表是Python中最常用的数据结构之一。它是一个......
  • 【set容器】
    set定义set又名集合,是一种内部自动排序、且不含重复元素的容器●有序●去重遍历只能用迭代器遍历#include<set>#include<cstdio>usingnamespacestd;intmain(){set<int>st;for(inti=6;i>0;i--){st.insert(i);}for(s......
  • python学习日志,五大容器的比较
    列表的使用:列表.append(元素):向列表中追加一个元素列表.extend(容器):将数据容器的内容依次取出,追加到列表尾部列表.insert(下标,元素):在指定下标处,插入指定的元素del列表[下标]:删除列表指定下标元素列表.pop(下标):删除列表指定下标元素列表.remove(元素):从前向后,翻除此......
  • 服务器日志事件ID4107
    在查看系统日志时,你是否有遇到过事件ID4107错误,来源CAPI2,详细信息在<http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab>从自动更新cab中提取第三方的根目录列表失败,错误为:已处理证书链,但是在不受信任提供程序信任的根证书中终......
  • 6. 核心容器
    这里所说的核心容器,大家可以把它简单的理解为​ApplicationContext​,前面虽然已经用到过,但是并没有系统的学习,接下来咱们从以下几个问题入手来学习下容器的相关知识:如何创建容器?创建好容器后,如何从容器中获取bean对象?容器类的层次结构是什么?BeanFactory是什么?1.容......