vlambda博客
学习文章列表

后起之秀Loki,帮你快速落地日志系统

一、背景

最近在准备我们各个业务系统的容器化服务,通过K8S来统一调度处理,然遇到一个问题:我们各个业务系统的日志怎么搜集?之前我们用的ELK,通过固定目录搜集的,现在采用的是Serverless On K8S,挂载本地目录有些问题,就在寻找一些解决方案,网上调研了一番,突然发现目前有个小巧的日志系统Loki,说实话网上目前的资料不多,即使官网也不是很详细,尤其是对一个新人想尝试的时候。但是功夫不负有心人,最终还是找到一种解决方案,以下就分享下本次的调研结果。


二、Loki 初识


Loki是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统。它的设计非常经济高效且易于操作,因为它不会为日志内容编制索引,而是为每个日志流编制一组标签。项目受 Prometheus 启发,官方的介绍就是:Like Prometheus, but for logs.,类似于 Prometheus 的日志系统。


2.1 架构之美


先上图,看下loki的整体架构:



2.2 组件之Distributor


    一旦promtail(官方用于搜集日志,推送到loki的组件)收集日志并将其发送给loki,Distributor就是第一个接收日志的组件。由于日志的写入量可能很大,所以不能在它们传入时将它们写入数据库。这会毁掉数据库。我们需要批处理和压缩数据。

    Loki通过构建压缩数据块来实现这一点,方法是在日志进入时对其进行gzip操作,组件ingester是一个有状态的组件,负责构建和刷新chunck,当chunk达到一定的数量或者时间后,刷新到存储中去。每个流的日志对应一个ingester,当日志到达Distributor后,根据元数据和hash算法计算出应该到哪个ingester上面。


2.3 组件之Ingester


ingester接收到日志并开始构建chunk:

后起之秀Loki,帮你快速落地日志系统

基本上就是将日志进行压缩并附加到chunk上面。一旦chunk“填满”(数据达到一定数量或者过了一定期限),ingester将其刷新到数据库。我们对块和索引使用单独的数据库,因为它们存储的数据类型不同。

后起之秀Loki,帮你快速落地日志系统


刷新一个chunk之后,ingester然后创建一个新的空chunk并将新条目添加到该chunk中。


2.4 组件之Querier


读取就非常简单了,由Querier负责给定一个时间范围和标签选择器,Querier查看索引以确定哪些块匹配,并通过greps将结果显示出来。它还从Ingester获取尚未刷新的最新数据。

对于每个查询,一个查询器将为您显示所有相关日志。实现了查询并行化,提供分布式grep,使即使是大型查询也是足够的。

后起之秀Loki,帮你快速落地日志系统



三、实践


3.1 安装Loki:

因是在本地实验,所以采用的是docker-compose进行编排,新建docker-compose.yml 文件,其内容为:


version: "3"
networks:
loki:

services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki

promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
- loki

grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki



启动

docker-compose up -d


验证下是否启动成功:

docker-compose ps 


后起之秀Loki,帮你快速落地日志系统


3.2 项目中引入依赖:(此部分才是关键)


在pom文件里添加:

后起之秀Loki,帮你快速落地日志系统


<dependency>
<groupId>com.github.loki4j</groupId>
<artifactId>loki-logback-appender-jdk8</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>


配置日志文件loback.xml:


<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" debug="false">
<contextName>report-module</contextName>
<property name="log.charset" value="utf-8" />
<property name="log.pattern" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)" />
<property name="log.path" value="/Users/ckitadmin/micro-oms/log" />
<!-- <property name="log.path" value="/usr/local/micro-oms/log" />-->
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
<charset>${log.charset}</charset>
</encoder>
</appender>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http class="com.github.loki4j.logback.ApacheHttpSender">
<url>http://localhost:3100/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=report-module,host=${HOSTNAME},level=%level</pattern>
</label>
<message>
<pattern>l=%level h=${HOSTNAME} c=%logger{20} t=%thread | %msg %ex</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>


<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">

<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${log.pattern}</pattern>
</encoder>

<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>
${log.path}/tb-o2o-module-%d{yyyy-MM-dd-HH}-%i.log
</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>10</maxHistory>
</rollingPolicy>

</appender>

<!--普通日志输出到控制台-->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="file" />
<appender-ref ref="LOKI" />
</root>

<!--监控sql日志输出 -->
<logger name="jdbc.sqlonly" level="INFO" additivity="false">
<appender-ref ref="LOKI" />
</logger>

<logger name="jdbc.resultset" level="ERROR" additivity="false">
<appender-ref ref="LOKI" />
</logger>

<!-- 如想看到表格数据,将OFF改为INFO -->
<logger name="jdbc.resultsettable" level="OFF" additivity="false">
<!-- <appender-ref ref="console" />-->
</logger>

<logger name="jdbc.connection" level="OFF" additivity="false">
<!-- <appender-ref ref="console" />-->
</logger>

<logger name="jdbc.sqltiming" level="INFO" additivity="false">
<appender-ref ref="LOKI" />
</logger>

<logger name="jdbc.audit" level="OFF" additivity="false">
<appender-ref ref="LOKI" />
</logger>
</configuration>


接下来就是见证奇迹的时刻了~


访问http://localhost:3000,初始用户密令为admin/admin。设置数据源为loki:


接下来导航到Explore,设置查询条件为{app="report-module"}


可以看到日志已经搜集到grafana。大功告成!


四、总结


初次体验完loki,感觉很棒!它还有LogQL语法,支持一些复杂的查询过滤,作为一款后起之秀的日志系统还是很不错的,跟ELK、EFK比起来算是轻量级的,接下来会尝试部署到生产环境。如果Loki让你心动了,赶紧行动吧