本文整理自:浅谈云原生系统日志收集在数栈的实践node
数栈是云原生—站式数据中台PaaS,咱们在github上有一个有趣的开源项目:FlinkX,欢迎给咱们点个star!star!star!git
https://github.com/DTStack/flinkxgithub
FlinkX是一个基于Flink的批流统一的数据同步工具,既能够采集静态的数据,好比MySQL,HDFS等,也能够采集实时变化的数据,好比MySQL binlog,Kafka等,是全域、异构、批流一体的数据同步引擎,你们若是有兴趣,欢迎来github社区找咱们玩~docker
1、常规打法ELK
谈到日志收集,估计你们第一个想到的就是ELK这个比较成熟的方案,若是是特别针对云原生上的,那么将采集器稍微变一下为 Fluentd 组成 EFK 便可。以上两种方案其实没有本质上的区别,采集器换了换而已。最终存储、查询等等采用的仍是 elasticsearch 这一套。json
elasticsearch确实功能丰富、很是强大,可是代价也是极其昂贵的,elasticsearch 采用全文索引的方式,对存储以及内存的要求比较高,而这些代价换来的功能对于平常日志管理来讲倒是不经常使用的。这些缺点在主机模式下实际上是能够容忍的,但在云原生模式下就显得比较臃肿了。api
2、不讲武德PLG
PLG 是 promtail+loki+grafana 的合称,这是一套很是适合云原生下日志的采集方案。grafana 你们会比较熟悉,一个很是棒的可视化的框架,支持多种数据源。最多见的就是将prometheus的数据进行可视化展现。而loki就是今天咱们要谈的主角,这个也是grafana 家的产品,promtail 则是 loki 的官方日志采集器。框架
与elk相比这一套方案很是轻量级,功能实用,使用起来简单易用,而且在展现上采用 grafana 减小引入可视化的框架,展现终端上的统一也有利于用户的使用。运维
(一) 日志新贵lokielasticsearch
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 服务发现
一、 找到文件做为一个采集器,其第一步天然是要找到文件在哪里,而后才能作下面的收集与打标签推送等功能。普通静态类型的日志是很好发现的,直接将你在配置文件中写的路径信息进行匹配便可,好比 promtail 中path为 "/var/log/*.log"即将 /var/log目录下全部的以.log 结尾的后缀文件做为要采集的对象便可。而要采集 k8s 模式内的日志就稍显麻烦。
首先咱们想一下k8s 上跑的服务的日志究竟是在哪里?
- 文件类型的日志
这种天然是还在你自定义的路径上,若是这个路径目录没有被挂载出来,那么就在容器内部,若是挂载到了宿主机或者 pv 内,那么在 宿主机与 pv 内也是可见的,这种类型的日志 promtail 是没法动态发现的,必须手工设置进去。 - 标准输出的日志
这类日志实际上是k8s推荐的日志输出方式,这类日志实际上是咱们平常用 kubectl log 看到的日志,这类日志存储路径在宿主机遵循/var/log/pods/
{namespace}_/{pod_id}_UUID/{container_name}/*.log这种格式
因此咱们须要将这/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 的服务发现机制动态的为日志加上标签,不管是资源的占用程度仍是部署维护难度都是很是低。这也是主流的云原生日志采集范式。
3、数栈日志实践
(一) 数栈日志需求
- 全局 grep
根据关键字,搜索系统中全部出现的地方 - 快速定位日志
根据机器名、ip、服务名等条件快速定位日志 - 主机与云原生统一技术栈
减小使用学习成本,下降系统复杂性
(二) ️ 主机模式
数栈主机模式日志聚合采用相似PLG DameonSet 的模式。每台主机部署一台 promtail,而后整个集群部署一套服务端 loki 与可视化端grafana。
promtail 采用static_configs定义采集的日志。可是promtail 毕竟仍是太年轻了,定位偏向于云原生,因此针对主机功能还不够完善,所以咱们作了一些二次开发知足咱们的需求:
一、logtail 模式
原生 promtail 并不支持从文件尾部开始收集,当 promtail 启动后,会将监控的全部文件的内容都进行推送,这样的状况在云原生并无太大问题.
主机模式下若是要监控的日志已经存在而且有大量的内容的话,promtail 启动会将文件的内容从头开始推送,短期内形成大量的日志往loki推送,很大的几率会被 loki 限流致使推送失败。
因此最好的方式就是有相似 filebeat 的 logtail 的模式,及只推送服务启动后的文件写入的日志。
这个地方咱们对此做了二次开发,增长一个logtail 模式的开关,若是该开关为 true,这第一次启动 promtail 的时候将不会从头推送日志。
二、path 支持多路径
原生 promtail 不支持多路径 path 参数只能写一个表达式,可是现实的需求多是既要看业务的日志还要看 gc 的日志。
可是他们又是属于同一类别的标签。单个path的匹配没法涵盖其两个,不改代码的解决方法就是再为其写一个 target。
这样作繁琐且不利于维护。因此咱们这里也对其作了二次开发
(三) 云原生模式
传统的云原生模式采用 PLG 的主流模式就行了,可是数栈做为一整套系统对企业交付的时候有诸多限制会致使demoset模式并不可用,最大的挑战是权限,只有一个 namespace 的权限,不能挂载/var/lib/pods
在这种状况下如何使用 PLG呢?
其实主要变化的地方在于promtail的使用,这里首先要声明的一点是,数栈的服务的日志都为文件输出。
首先是选择damonset 模式部署仍是sidecar模式部署,demonset模式的优势是节省资源,缺点是权限有要求。sidecar模式与之相反,为了适用更严格的交付条件,咱们选择采用 sidecar 的模式进行采集。
sidecar 模式就是为当每一个服务进行部署的时候就自动为其添加一个log容器,该容器与服务容器共同挂载一个共同的空的数据卷,服务容器将日志写入该数据卷中,log容器对数据卷下的日志进行采集。
一、⛳ 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 时,即便拿到了标签也没法与日志进行关联。
二、⏰ promtail 在数栈如何部署
为每一个服务增长一个Log Container若是手工操做的话实在是太繁琐了,并且不利于维护。最好的方式就将本来的服务抽象为是注册一个CRD,而后编写 k8s operator经过list&watch该类型的对象,在该对象建立的时候,动态的注入一个LogContainer,以及相应的环境变量和为其挂载共同目录。
这样当该CR建立的时候,promtail就做为sidecar注入了其中。而且读到的环境变量就是operator 动态设置的环境变量,灵活度很是高。
4、总结
(一) 数栈日志收集优点
- 一套日志聚合分析框架解决主机与云原生两种场景,减小了系统复杂度
- 日志可视化采用 grafana,可视化效果较好,并且grafana 与 prometheus已是云原生监控的是事实上的标准了,开发运维都比较熟悉,减小了学习成本
- loki 查询语法简单,可是功能强大
- 与 ELK 相比,更加轻量级
(二)✈️将来规划
- 当前使用 sidecar 模式,资源占用较多,后续考虑在进一步优化
- loki 分布式部署优化
最后给你们分享一下数栈当前日志模块可视化的效果,是否是超级酷炫?