日志是应用服务运行过程中的一个关键环节,借助日志,我们可以排查定位问题,也可以借助集中化的日志管理平台(如ELK)来做一些必要的数据统计分析与监控告警。在 K8s 环境中,容器的日志有可能是通过 STDOUT/STDERR 输出(对于标准输出,前面 Docker笔记(十三):容器日志采集实践 有相关介绍可参考),并且一般也推荐将日志写到标准输出,但是也有一些特殊的场景,应用直接将日志写在容器内部的日志文件。对于容器的标准输出日志来说,Docker Engine 本身就提供了一个很好的日志采集能力,但是对于容器内部的文件日志采集,现在却并没有一个很好的工具能够去动态发现采集。因为在分布式的容器集群中,容器随着 Pod 调度被动态创建或删除,我们无法像虚拟机环境那样事先配置好日志采集路径等信息,目前的采集工具都是需要我们事先手动配置好日志采集方式和路径等信息,它无法自动感知到容器的生命周期变化或者动态漂移(一个 Pod 挂了,可能是在另一个节点上启动一个新的 Pod),无法进行动态的配置。因此,在 K8s 中进行日志采集将变得更为复杂。

1. K8s 的日志采集模式

综观 K8s 下的日志采集模式,大致有三种:

1.Node 代理模式

就是在每个 Node 上部署一个日志采集代理程序(如 filebeat,fluentd,或 logstash 等),一般是以 DaemonSet 的形式在每个 Node 上部署一个 Pod,来采集这个 Node 上所有容器的日志。这种模式的优点是资源消耗少,一个节点一个 Pod, 且对应用无侵入。

2.SideCar 模式

这种模式就是在 Pod 中除了运行我们的应用程序容器, 再起一个负责日志采集的容器,比如再起一个 logstash 或 fluentd 容器。当 Pod 数量一多,这种方案资源消耗很大,对日志存储后端也会占用过多的连接数,并且日志不输出到标准输出,不能通过 kubectl logs 命令查看。

3.应用程序推送模式

直接在应用程序里将日志内容发送到日志采集服务,比如在程序里将日志发到 kafka, 再使用 logstash 从 kafka 拉取到 elasticsearch。这种方案对应用具有侵入性。

有没有一种方案或工具,既能采集 k8s 中标准输出日志,又能采集到容器内部的日志文件输出日志,并且资源消耗小,对应用无侵入呢。拒作者了解,阿里开源的 log-pilot 基本能满足要求,只是更新较慢,目前版本基于 ELK 6,如果要适配到 ELK 7 或以上,需进行一些必要的调整。

2. log-pilot 介绍

log-pilot 是阿里开源的一个同时支持容器标准输出日志采集与容器内部文件日志动态配置采集的组件。log-pilot 具备如下特性。

1. 采集目标多

log-pilot 同时支持采集标准输出日志和动态发现配置采集容器内部文件日志。

2. 声明式的日志配置

log-pilot 支持声明式日志配置,可以依据容器的 Label 或者 ENV 来动态地生成日志采集的配置文件。这里主要说明两个变量:

  • name:我们自定义的一个字符串,它在不同的场景下指代不同的含义。当日志采集到 ElasticSearch 的时候, name 表示的是 Index;当日志采集到 Kafka 的时候, name 表示的是 Topic;当日志采集到阿里云日志服务的时候,name 表示的就是 LogstoreName。
  • path:支持两种形式,一种是约定关键字 stdout,表示的是采集容器的标准输出日志,第二种是容器内部的具体文件日志路径,可以支持通配符的方式。比如我们要采集 tomcat 容器日志,我们可以通过配置标签 aliyun.logs.catalina=stdout 来采集 tomcat 标准输出日志,通过配置标签 aliyun.logs.access=/usr/local/tomcat/logs/*.log 来采集 tomcat 容器内部文件日志。

声明式的日志配置

3. 动态配置的能力

log-pilot 本身分为三部分,其中一部分就是容器的事件管理,它能够动态地监听容器的事件变化(如创建、删除),然后依据容器的标签来进行解析,生成日志采集配置文件,然后交由采集插件来进行日志采集。通过全量扫描加事件监听的方式,比如采集工具进程在起来的时候,注册事件监听,然后全量扫描一遍宿主机上的所有容器列表,然后依据容器的声明式配置来进行日志采集配置文件的动态生成。

4. 防重复和丢失

log-pilot 内部具有 CheckPoint 和句柄保持的机制。

  • checkPoint机制: log-pilot 内部会实时跟踪日志采集的偏移量,然后维持日志文件信息与偏移量的映射关系,最后定期地持久化到磁盘中。采用偏移量的方式我们可以避免日志采集丢失和重复的问题,同时即使当采集工具宕掉再起来,它也可以通过加载持久化在磁盘上的元数据信息,从指定的日志偏移位置上继续采集日志。
  • 句柄保持机制: log-pilot 在监测到配置的日志路径目录下有新的日志文件产生时会主动地打开其句柄,并维持打开状态,这样是为了防止因日志采集工具比较慢或者应用日志输出速率特别大,比如说当前已经生成五个日志文件但只采集到第三个,后面两个还没有开始采集,一旦这个容器退出就可能导致后面两个文件的日志丢失了。

5. 明确日志来源

支持日志自动数据打标。log-pilot 在采集容器日志的时候,同时也会收集容器的元数据信息,包括容器的名称,容器所属的服务名称以及容器所属的应用名称,同时在 Kubernetes 里面也会采集容器所属的 Pod 信息,包括 Pod 的名称,Pod 所属的 namespace 以及 Pod 所在的节点信息。这样排查问题时,就可以很方便地知道这个日志是来源于哪个节点上的哪个应用容器。

自动数据打标

6. 支持自定义 Tag

log-pilot 支持自定义Tag,我们可以在容器的标签或者环境变量里配置 aliyun.logs.$name.tags: k=v,那么在采集日志的时候就会将k=v采集到容器的日志输出中。比如针对不同的环境(如开发环境、测试环境),可以使用 tag 来进行区分。也可以使用自定义 tag 来进行日志的统计、路由与过滤等。

自定义tag

7. 支持多种日志解析格式

log-pilot 支持多种日志解析格式,通过 aliyun.logs.$name.format: <format> 标签就可以告诉 log-pilot 在采集日志的时候,同时以什么样的格式来解析日志记录。目前主要支持六种:

  • none:默认格式,指不对日志记录做任何解析,整行采集出来直接输出到日志存储后端。
  • json:log-pilot 在采集日志的同时会将每一行日志以 json 的方式进行解析,解析出多个 KV 对,然后输出到日志存储后端。
  • csv:主要是针对 csv 格式的日志采集配置(需配置 fluentd 插件)。
  • nginx:主要是针对 Nginx 的日志采集配置(需配置 fluentd 插件)。
  • apache2:主要是针对 Apache 的日志采集配置(需配置 fluentd 插件)。
  • regexp:用户可以通过 format 标签来自定义正则表达式,告诉 log-pilot 在解析日志记录的时候以什么样的格式来进行解析(需配置 fluentd 插件)。

多种解析格式

8. 支持自定义输出Target

假设我们同时有一个生产环境和一个测试环境,应用日志都需要被采集到同一套 Kafka 中,然后由不同的 consumer 去消费。但是我们希望对环境进行区分,某条日志是由生产环境的应用容器产生的,还是测试环境的应用容器产生的,但我们在测试环境中的应用容器已经配置了 aliyun.logs.svc=stdout 标签,那么当这些应用容器的标准输出日志被采集到 kafka 中,它最终会被路由到 topic=svc 的消息队列中,那么订阅了 topic=svc 的 consumer 就能够接收测试环境的应用容器产生的日志。但当我们将该应用发布到生产环境时,希望它产生的日志只能交由生产环境的 consumer 来接收处理,那么我们就可以通过 target 的方式,给生产环境的应用容器额外定义一个 target=pro-svc,那么生产环境的应用日志在被采集到 Kafka 中时,最终会被路由到 topic 为 pro-svc 的消息队列中,那么订阅了 topic =pro-svc 的 consumer 就可以正常地接收到来自于生产环境的容器产生的日志。

因此这里的 target 本身也有三种含义:

  1. 日志对接到 ElasticeSearch 时,这个 target 字符串是 Index;
  2. 对接到 Kafka 时,它指代的是 topic;
  3. 对接到阿里云日志服务时,它代表的是 Logstore Name。

自定义输出target

9. 支持多种采集插件

目前 log-pilot 支持两种采集插件:一个是 CNCF 社区的 Fluentd 插件,一个是 Elastic 的 Filebeat 插件;同时支持对接多种存储后端,目前 Fluentd 和 Filebeat 都支持 Elasticsearch、Kafka、File、Console 作为日志存储后端,而 Fluentd 还支持 Graylog、阿里云日志服务 以及 Mongodb 作为存储后端。

3. 总结

本文对 k8s 集群中日志采集的常见模式,及阿里开源的 log-pilot 支持的特性进行了介绍。但 log-pilot 目前基于 ELK 6 版本, 如果需要适配 ELK 7 或以上版本,需要对其进行必要的调整。下文将介绍如何进行适配调整及如何将 log-pilot 部署到 k8s 集群中进行日志采集。

参考:

容器日志采集利器 Log-Pilot: https://developer.aliyun.com/article/674327
log-pilot 官方源码地址:https://github.com/AliyunContainerService/log-pilot

评论