一、常规打法ELK
谈到日志收集,估计大家第一个想到的就是ELK这个比较成熟的方案,如果是特别针对云原生上的,那么将采集器稍微变一下为 Fluentd 组成 EFK 即可。以上两种方案其实没有本质上的区别,采集器换了换而已。最终存储、查询等等采用的还是 elasticsearch 这一套。
elasticsearch确实功能丰富、非常强大,但是代价也是极其昂贵的,elasticsearch 采用全文索引的方式,对存储以及内存的要求比较高,而这些代价换来的功能对于日常日志管理来说却是不常用的。这些缺点在主机模式下其实是可以容忍的,但在云原生模式下就显得比较臃肿了。
二、不讲武德PLG
PLG 是 promtail+loki+grafana 的合称,这是一套非常适合云原生下日志的采集方案。grafana 大家会比较熟悉,一个非常棒的可视化的框架,支持多种数据源。最常见的就是将prometheus的数据进行可视化展示。而loki就是今天我们要谈的主角,这个也是grafana 家的产品,promtail 则是 loki 的官方日志采集器。
与elk相比这一套方案非常轻量级,功能实用,使用起来简单易用,并且在展示上采用 grafana 减少引入可视化的框架,展示终端上的统一也有利于用户的使用。
Loki是受Prometheus启发的水平可扩展,高度可用的多租户日志聚合系统。它的设计具有很高的成本效益,并且易于操作。它不索引日志的内容,而是为每个日志流设置一组标签。
不对日志进行全文本索引。通过存储压缩的,非结构化的日志以及仅索引元数据,Loki更加易于操作且运行成本更低。使用与Prometheus相同的标签对日志流进行索引和分组,从而使您能够使用与Prometheus相同的标签在指标和日志之间无缝切换。特别适合存储Kubernetes Pod日志。诸如Pod标签之类的元数据会自动被抓取并建立索引。
Grafana原生支持(需要Grafana v6.0以上)。
三、数栈日志实践
全局 grep
根据关键字,搜索系统中所有出现的地方
快速定位日志
根据机器名、ip、服务名等条件快速定位日志
主机与云原生统一技术栈
减少使用学习成本,降低系统复杂性
1、logtail 模式
原生 promtail 并不支持从文件尾部开始收集,当 promtail 启动后,会将监控的所有文件的内容都进行推送,这样的情况在云原生并没有太大问题.
主机模式下如果要监控的日志已经存在并且有大量的内容的话,promtail 启动会将文件的内容从头开始推送,短时间内造成大量的日志往loki推送,很大的概率会被 loki 限流导致推送失败。
所以最好的方式就是有类似 filebeat 的 logtail 的模式,及只推送服务启动后的文件写入的日志。
这个地方我们对此作了二次开发,增加一个logtail 模式的开关,如果该开关为 true,这第一次启动 promtail 的时候将不会从头推送日志。
2、path 支持多路径
原生 promtail 不支持多路径 path 参数只能写一个表达式,但是现实的需求可能是既要看业务的日志还要看 gc 的日志。
但是他们又是属于同一类别的标签。单个path的匹配无法涵盖其两个,不改代码的解决方法就是再为其写一个 target。
这样做繁琐且不利于维护。所以我们这里也对其做了二次开发
通过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 动态设置的环境变量,灵活度非常高
四、总结
一套日志聚合分析框架解决主机与云原生两种场景,减少了系统复杂度
日志可视化采用 grafana,可视化效果较好,而且grafana 与 prometheus已经是云原生监控的是事实上的标准了,开发运维都比较熟悉,减少了学习成本
loki 查询语法简单,但是功能强大
与 ELK 相比,更加轻量级
(二)✈️未来规划
当前使用 sidecar 模式,资源占用较多,后续考虑在进一步优化
loki 分布式部署优化