京东到家Loki日志系统实践
背景
随着业务的高速发展,目前基于ELK架构的日志系统无法满足京东到家的日志存储和查询需求。这是因为ELK架构中要用全文索引来支撑搜索服务,需要为日志的原文建立反向索引,这会导致最终存储数据相较原始内容成倍增长,产生较高的存储成本。不管数据将来是否会被搜索,都会在写入时因为索引操作而占用大量的计算资源,这对于日志这种写多读少的服务也是一种计算资源的浪费。另外日志采集无法自动化,新的日志需要运维手动加入。针对这些痛点,我们调研了当前流行的日志系统,最终选择了Loki这个新兴日志系统作为ELK架构日志系统的替代。
以下是根据ELK与Loki优缺点进行对比。
Loki日志系统
Loki是Grafana Labs团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统。设计经济高效且易于操作,它不会为日志内容编制索引,而是为每个日志流编制一组标签。
Loki日志系统由以下3个部分组成:
Loki是主服务器,负责存储日志和处理查询。
Promtail是专为Loki定制的客户端,负责收集日志并将其发送Loki。
Grafana用于UI展示(可以自己开发前端页面来替代)。
Loki架构
Promtail开源客户端负责采集并上报日志;
Distributor:日志写入入口,将数据转发到Ingester;
Ingester:日志的写入服务,缓存并写入日志内容和索引到底层存储;
Querier:日志读取服务,执行搜索请求。
Distributor
Promtail收集日志并将其发送给Loki,Distributor就是第一个接收日志的组件。由于日志的写入量可能很大,所以不能在它们传入时就将它们写入存储中,需要批处理和压缩数据。Loki通过构建压缩数据块来实现这一点,方法是在日志进入时对其进行gzip操作,组件ingester是一个有状态的组件,负责构建和刷新chunck,当chunk达到一定的数量或者时间后,刷新到存储中去。每个流的日志对应一个ingester,当日志到达Distributor后,根据元数据和hash算法计算出应该到哪个ingester上面。
Ingester
接收到日志并开始构建chunk,基本上就是将日志进行压缩并附加到chunk上面。一旦chunk“填满”(数据达到一定数量或者过了一定期限),ingester将其刷新到数据库。对块和索引使用单独的数据库,因为它们存储的数据类型不同。
刷新一个chunk之后,ingester然后创建一个新的空chunk并将新条目添加到该chunk中。
Querier
读取就非常简单了,由Querier负责给定一个时间范围和标签选择器,Querier查看索引以确定哪些块匹配,并通过greps将结果显示出来。它还从Ingester获取尚未刷新的最新数据。
对于每个查询,一个查询器将为您显示所有相关日志。实现了查询并行化,提供分布式grep,使即使是大型查询也是足够的。
Loki读写组件
写数据
日志数据的写主要依托的是Distributor和Ingester两个组件,整体流程如下:
Distributor收到HTTP请求,用于存储流数据
通过hash环对数据流进行hash
Distributor将数据流发送到对应的Ingester及其副本上
Ingester新建Chunk或将数据追加到已有Chunk上
Distributor通过HTTP连接发送响应信息
读数据
Querier收到HTTP请求(来自Granfana或者自己开发的前端)
Querier将请求发送至Ingester用以获取内存数据
Ingester收到请求后返回符合条件的数据
如果没有Ingester返回数据,Querier从后端存储加载数据并执行查询
Querier遍历所有数据并进行去重处理,通过HTTP连接返回最终结果
Loki查询语法
选择器
对于查询表达式的标签部分,将放在{}中,多个标签表达式用逗号分隔:
{app="mysql",name="mysql-backup"}
支持的符号有:
= 完全相同。
!= 不平等。
=~ 正则表达式匹配。
!~ 不要正则表达式匹配
过滤表达式
编写日志流选择器后,您可以通过编写搜索表达式进一步过滤结果。搜索表达式可以文本或正则表达式。
如:{job=“mysql”} |= “error”
支持多个过滤:{job=“mysql”} |= “error” !=“timeout”
目前支持的操作符:
|= line包含字符串。
!= line不包含字符串。
|~ line匹配正则表达式。
!~ line与正则表达式不匹配
京东到家应用日志系统
到家运维部基于loki日志系统,开发出到家应用日志分析平台。具体的技术方案如下:
前端UI :官方是使用Grafana进行前端展示,我们使用python/flask开发前端页面,整合到运维管理平台中。
后端存储选择Cassandra分布式存储系统,可以横向扩容。
研发在页面自定义要收集的日志文件,利用Promtail文件发现机制自动进行日志收集。
支持实时展示日志内容,以及对历史日志文件进行自定义搜索。
架构图
日志接入
选择应用、主机,输入自定义的日志文件。
调用应用服务器上的SaltStack管理客户端,将待采集日志文件路径写入主机Promtail日志采集客户端对应文件中
利用PPromtail文件自动发现机制,发现新加入的日志,将日志采集发送到Loki server
实时日志
调用Loki API通过WebSocket实时展示日志内容。
历史日志
选择应用,主机及日志,输入多个关键字进行内容过滤,多个关键字中间使用空格间隔。
在实际生产环境中(1台48核/256G内存/12*6T sata硬盘的存储型物理机)对10T数据进行多关键字、多日志文件进行查询,5s内返回查询结果。
技术难点
日志路径采用文件监听技术,配置日志路径更加灵活,不需要重新启动客户端。
采用分布式数据存储Cassandra,可以横向扩容。
实时日志采用WebSocket技术实现。
在所有服务器部署Promtail客户端工作量比较大,我们采用将客户端打包到操作系统镜像中,这样新的机器无需再安装客户端。
配置文件示例
下面是到家的Loki相关配置文件,供大家参考。
总结及规划
目前已经有1000多台应用服务器的日志收集到了基于Loki的到家日志系统中,每天的日志量占用空间才1.4T,原来在ES中,这么多的日志量需要近30T的存储空间,极大节省了硬件成本。而且根据前端研发提交过来的日志名称,可以自动化日志收集,减少了运维日志相关的手工操作,提高了运维的工作效率。
整个日志平台只使用了1台48核/256G内存/12*6T sata硬盘的存储型物理机,Loki server和Cassandra存储都部署在这台机器上。后续日志客户端接入量增加后, Loki server可以通过增加节点进行扩容,提高日志接收能力。后端存储Cassandra也可以通过增加Cassandra集群节点来横向扩容。另外前端展示方面,后续要针对搜索结果进一步分析处理和展示。