京东到家Loki日志系统实践


背景

随着业务的高速发展,目前基于ELK架构的日志系统无法满足京东到家的日志存储和查询需求。这是因为ELK架构中要用全文索引来支撑搜索服务,需要为日志的原文建立反向索引,这会导致最终存储数据相较原始内容成倍增长,产生较高的存储成本。不管数据将来是否会被搜索,都会在写入时因为索引操作而占用大量的计算资源,这对于日志这种写多读少的服务也是一种计算资源的浪费。另外日志采集无法自动化,新的日志需要运维手动加入。针对这些痛点,我们调研了当前流行的日志系统,最终选择了Loki这个新兴日志系统作为ELK架构日志系统的替代。

以下是根据ELK与Loki优缺点进行对比。
1.png

Loki日志系统

Loki是Grafana Labs团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统。设计经济高效且易于操作,它不会为日志内容编制索引,而是为每个日志流编制一组标签。

Loki日志系统由以下3个部分组成:
  • Loki是主服务器,负责存储日志和处理查询。
  • Promtail是专为Loki定制的客户端,负责收集日志并将其发送Loki。
  • Grafana用于UI展示(可以自己开发前端页面来替代)。


Loki架构

2.png

  • 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两个组件,整体流程如下:
  1. Distributor收到HTTP请求,用于存储流数据
  2. 通过hash环对数据流进行hash
  3. Distributor将数据流发送到对应的Ingester及其副本上
  4. Ingester新建Chunk或将数据追加到已有Chunk上
  5. Distributor通过HTTP连接发送响应信息


读数据
  1. Querier收到HTTP请求(来自Granfana或者自己开发的前端)
  2. Querier将请求发送至Ingester用以获取内存数据
  3. Ingester收到请求后返回符合条件的数据
  4. 如果没有Ingester返回数据,Querier从后端存储加载数据并执行查询
  5. 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文件发现机制自动进行日志收集。
  • 支持实时展示日志内容,以及对历史日志文件进行自定义搜索。


架构图

3.png

日志接入

4.png

  • 选择应用、主机,输入自定义的日志文件。
  • 调用应用服务器上的SaltStack管理客户端,将待采集日志文件路径写入主机Promtail日志采集客户端对应文件中
  • 利用PPromtail文件自动发现机制,发现新加入的日志,将日志采集发送到Loki server


实时日志

5.png

调用Loki API通过WebSocket实时展示日志内容。

历史日志

6.png

选择应用,主机及日志,输入多个关键字进行内容过滤,多个关键字中间使用空格间隔。
7.png

在实际生产环境中(1台48核/256G内存/12*6T sata硬盘的存储型物理机)对10T数据进行多关键字、多日志文件进行查询,5s内返回查询结果。

技术难点

  • 日志路径采用文件监听技术,配置日志路径更加灵活,不需要重新启动客户端。
  • 采用分布式数据存储Cassandra,可以横向扩容。
  • 实时日志采用WebSocket技术实现。
  • 在所有服务器部署Promtail客户端工作量比较大,我们采用将客户端打包到操作系统镜像中,这样新的机器无需再安装客户端。


配置文件示例

下面是到家的Loki相关配置文件,供大家参考。
LokiServer配置:
auth_enabled: false  #关闭认证,对数据来源客户端不做认证
server:
http_listen_port: 3101  #loki http端口 
ingester:
lifecycler:
address: 127.0.0.1   #ingester地址,默认为本机127.0.0.1,如果有多台server也可以写多个
ring:
  kvstore:
    store: inmemory  #使用内存做为ingester存储
  replication_factor: 1
     final_sleep: 0s
chunk_idle_period: 5m #在没有更新之前chunk在内存中的时间
chunk_retain_period: 30s  #刷新后应在内存中保留多长时间  
storage_config:#存储配置
cassandra:#使用Cassandra
addresses: x.x.x.x #cassandra的IP
keyspace: lokiindex  #cassandra的keyspace
auth: false    #关闭Cassandra认证 
schema_config:
configs:
- from: 2020-07-01
 store: cassandra   #数据存储方式为Cassandra   
 object_store: cassandra
 schema: v11
 index:
   prefix: index_  #index表的前缀
   period: 168h   #index每张表的时间范围7天
 chunks:
   prefix: chunk_  #chunks表的前缀
   period: 168h  #chunks每张表的时间范围7天 
limits_config:
ingestion_rate_mb: 50 #每个用户每秒的采样率限制
enforce_metric_name: false
reject_old_samples: true
reject_old_samples_max_age:168h 
chunk_store_config:
# 最大可查询历史日期 7天
max_look_back_period: 168h 
# 表的保留期7天
table_manager:
retention_deletes_enabled: true  #超过retention_period时间历史数据可以删除
retention_period: 168h

Promtail客户端:

server:
http_listen_port: 0 #http端口,为0表示端口随机
grpc_listen_port: 0 #grpc端口,为0表示端口随机  
# Positions
positions:
filename:/export/servers/promtail/tmp/positions.yaml #记录采集的文件路径与日志采集位置 
# Loki服务器的地址
clients:
- url:http://xx.xx.xxx/loki/api/v1/push 
scrape_configs:
- job_name: daojia #job_name 用于标识抓取配置
file_sd_configs:#用于文件发现
   - files:
        -'/export/servers/promtail/logpath.yaml'#具体文件
     refresh_interval: 10s #文件检测间隔,当有新日志加入会自动发现并自动采集日志,无须客户端重启 
- targets:
- localhost  #收集的主机
labels:
host: 1.1.1.1 #给收集主机打标签host:1.1.1.1
log: gw           #给收集主机打标签log: gw
__path__:/export/servers/nginx/logs/gw.o2o.jd.local/gw.o2o.jd.local_access.log          #要收集的日志路径

总结及规划

目前已经有1000多台应用服务器的日志收集到了基于Loki的到家日志系统中,每天的日志量占用空间才1.4T,原来在ES中,这么多的日志量需要近30T的存储空间,极大节省了硬件成本。而且根据前端研发提交过来的日志名称,可以自动化日志收集,减少了运维日志相关的手工操作,提高了运维的工作效率。

整个日志平台只使用了1台48核/256G内存/12*6T sata硬盘的存储型物理机,Loki server和Cassandra存储都部署在这台机器上。后续日志客户端接入量增加后, Loki server可以通过增加节点进行扩容,提高日志接收能力。后端存储Cassandra也可以通过增加Cassandra集群节点来横向扩容。另外前端展示方面,后续要针对搜索结果进一步分析处理和展示。

原文链接:https://mp.weixin.qq.com/s/-IvWfhXbm3WXFIBtbrhX-A

0 个评论

要回复文章请先登录注册