谈到日志收集,估计大家第一个想到的就是 ELK 这个比较成熟的方案,如果是特别针对云原生上的,那么将采集器稍微变一下为 Fluentd 组成 EFK 即可。以上两种方案其实没有本质上的区别,采集器换了换而已。最终存储、查询等等采用的还是 elasticsearch 这一套。
elasticsearch 确实功能丰富、非常强大,但是代价也是极其昂贵的,elasticsearch 采用全文索引的方式,对存储以及内存的要求比较高,而这些代价换来的功能对于日常日志管理来说却是不常用的。这些缺点在主机模式下其实是可以容忍的,但在云原生模式下就显得比较臃肿了。
PLG 是 promtail+loki+grafana 的合称,这是一套非常适合云原生下日志的采集方案。grafana 大家会比较熟悉,一个非常棒的可视化的框架,支持多种数据源。最常见的就是将 prometheus 的数据进行可视化展示。而 loki 就是今天我们要谈的主角,这个也是 grafana 家的产品,promtail 则是 loki 的官方日志采集器。
与 elk 相比这一套方案非常轻量级,功能实用,使用起来简单易用,并且在展示上采用 grafana 减少引入可视化的框架,展示终端上的统一也有利于用户的使用。
(一) 日志新贵 loki
Loki 是受 Prometheus 启发的水平可扩展,高度可用的多租户日志聚合系统。它的设计具有很高的成本效益,并且易于操作。它不索引日志的内容,而是为每个日志流设置一组标签。
与其他日志聚合系统相比,Loki
不对日志进行全文本索引。通过存储压缩的,非结构化的日志以及仅索引元数据,Loki 更加易于操作且运行成本更低。
使用与 Prometheus 相同的标签对日志流进行索引和分组,从而使您能够使用与 Prometheus 相同的标签在指标和日志之间无缝切换。
特别适合存储 Kubernetes Pod 日志。诸如 Pod 标签之类的元数据会自动被抓取并建立索引。
Grafana 原生支持(需要 Grafana v6.0 以上)。
这段是 loki 在 GitHub 上的介绍,可以看出这是一款为云原生而打造的轻量级日志聚合系统。目前社区非常活跃。而且采用 prometheus 类似的标签的思想,与 grafana 打通进行可视化展示,无论是思想还是用法都非常的 “云原生”。
(二) ♂️ 亲儿子 Promtail
promtail 是 loki 的官方日志采集器,本身代码就在 loki 项目中。原生支持 journal、syslog、file、docker 类型的日志。采集器的本质无非都是根据模式找到要采集的文件,然后对着个文件进行类似 tail 的监控,再把写入文件的内容发送给存储端 promtail 也是这样,上面这些类型的本质也都是文件,只不过这些类型的文件的格式是公开稳定的规范,promtail 可以预先对其进行进行更深解析与封装。
(三) Promtail 服务发现
1、 找到文件作为一个采集器,其第一步自然是要找到文件在哪里,然后才能做下面的收集与打标签推送等功能。普通静态类型的日志是很好发现的,直接将你在配置文件中写的路径信息进行匹配即可,比如 promtail 中 path 为 "/var/log/*.log" 即将 /var/log 目录下所有的以.log 结尾的后缀文件作为要采集的对象即可。而要采集 k8s 模式内的日志就稍显麻烦。
首先我们想一下 k8s 上跑的服务的日志到底是在哪里?
所以我们需要将这 /var/log/pods 作为 hostpath 挂载进 k8s 的容器内部,才能让 promtail 访问到这些日志。
日志 promtail 可以访问到了,但是还有一个问题还是如何为区分这些日志,loki 采用类似 prometheus 的思想,将数据打上标签。也就是将日志打上 pod 的标签,那么单单凭借这个路径自然是无法知道该 pod 上有哪些标签信息的。这里就需要用到服务发现了。
promtail 的服务发现是直接采用的 prometheus 的服务发现做的。熟悉 prometheus 的同学肯定配置过 prometheus 的服务发现的配置,kubernetes_sd_configs 与 relabel_configs。
这里 promtail 直接引入 prometheus 的代码,与 prometheus 不同的是 prometheus 请求的资源对对象比较多,node、ingress、pod、deployment 等等都有,最终拼接的是 metric 的请求 url,而 promtail 请求的对象为 pod,并且过滤掉了不在该主机上的 pod。
拿到该主机的 pod 的信息后,再根据 namespace, pod 的 id 拼接路径,由于这个目录已经挂载进去容器了,那么 promtail 就可以关联起容器的标签与容器的日志了。剩下的就是监控与推送了。
(四) PLG 最佳实践
loki 官方推荐的最佳实践为采用 DamonSet 部署 promtail 的方式,将 node 的 /var/lib/pods 目录挂载进容器内部,借助 prometheus 的服务发现机制动态的为日志加上标签,无论是资源的占用程度还是部署维护难度都是非常低。这也是主流的云原生日志采集范式。
(一) 数栈日志需求
(二) ️ 主机模式
数栈主机模式日志聚合采用类似 PLG DameonSet 的模式。每台主机部署一台 promtail,然后整个集群部署一套服务端 loki 与可视化端 grafana。
promtail 采用 static_configs 定义采集的日志。但是 promtail 毕竟还是太年轻了,定位偏向于云原生,所以针对主机功能还不够完善,因此我们做了一些二次开发满足我们的需求:
1、logtail 模式
原生 promtail 并不支持从文件尾部开始收集,当 promtail 启动后,会将监控的所有文件的内容都进行推送,这样的情况在云原生并没有太大问题.
主机模式下如果要监控的日志已经存在并且有大量的内容的话,promtail 启动会将文件的内容从头开始推送,短时间内造成大量的日志往 loki 推送,很大的概率会被 loki 限流导致推送失败。
所以最好的方式就是有类似 filebeat 的 logtail 的模式,及只推送服务启动后的文件写入的日志。
这个地方我们对此作了二次开发,增加一个 logtail 模式的开关,如果该开关为 true,这第一次启动 promtail 的时候将不会从头推送日志。
2、path 支持多路径
原生 promtail 不支持多路径 path 参数只能写一个表达式,但是现实的需求可能是既要看业务的日志还要看 gc 的日志。
但是他们又是属于同一类别的标签。单个 path 的匹配无法涵盖其两个,不改代码的解决方法就是再为其写一个 target。
这样做繁琐且不利于维护。所以我们这里也对其做了二次开发
(三) 云原生模式
传统的云原生模式采用 PLG 的主流模式就好了,但是数栈作为一整套系统对企业交付的时候有诸多限制会导致 demoset 模式并不可用,最大的挑战是权限,只有一个 namespace 的权限,不能挂载 /var/lib/pods
在这种情况下如何使用 PLG 呢?
其实主要变化的地方在于 promtail 的使用,这里首先要声明的一点是,数栈的服务的日志都为文件输出。
首先是选择 damonset 模式部署还是 sidecar 模式部署,demonset 模式的优点是节省资源,缺点是权限有要求。sidecar 模式与之相反,为了适用更严格的交付条件,我们选择采用 sidecar 的模式进行采集。
sidecar 模式就是为当每个服务进行部署的时候就自动为其添加一个 log 容器,该容器与服务容器共同挂载一个共同的空的数据卷,服务容器将日志写入该数据卷中,log 容器对数据卷下的日志进行采集。
1、⛳ promtail 在数栈如何动态配置标签
通过 sidecar 的模式我们让 log Container 与 Master Container 共享一个日志目录,这样就 promtail 容器内就可以拿到了日志的文件,但是 promtail 还不知道要采集哪些日志,以及他的标签是什么。
因为你可能只想采集.log 的日志,也可能只想采集.json 的日志,或者都有的服务这个配置可能是不同的,所以也不能写死,那如何解决这个问题呢?
promtail 在 v2.10 中新增加了一个 feature ,就是可以在配置文件中引用环境变量,通过这个特性我们可以将 promtail 的 path 参数写成 ${LOG_PATH},然后将服务的 logpath 以环境变量的方式设置进去比如 LOG_PATH=/var/log/commonlog/*.log
既然我们可以通过环境变量的方式在服务创建的时候设置 path,那么标签我们也可以动态的设置进去。那么我们都需什么维度的标签呢?这个不同的公司肯定有不同的维度,但是必须遵循的一个原则就是可以唯一确定该 pod。一般的维度有 deployment、podid、node 等。这些标签在创建的时候就通过环境变量注入进去,而 podid 这些环境变量利用的是 k8s 的 downward api 的方式注入的。
注意:这里不可用使用 promtail 的服务发现机制配置标签,因为 promtail 的服务发现的原理是请求 APIServer 获取所有 pod 的标签。然后利用路径进行匹配,将标签与日志关联。在没有挂载宿主机 /var/log/pods 目录到 promtail 时,即使拿到了标签也无法与日志进行关联。
为每个服务增加一个 Log Container 如果手工操作的话实在是太繁琐了,而且不利于维护。最好的方式就将原本的服务抽象为是注册一个 CRD,然后编写 k8s operator 通过 list&watch 该类型的对象,在该对象创建的时候,动态的注入一个 LogContainer,以及相应的环境变量和为其挂载共同目录。
这样当该 CR 创建的时候,promtail 就作为 sidecar 注入了其中。并且读到的环境变量就是 operator 动态设置的环境变量,灵活度非常高。
(一) 数栈日志收集优势
(二)✈️未来规划
最后给大家分享一下数栈当前日志模块可视化的效果,是不是超级酷炫?
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack