Hi~,各位读者朋友们,我是笠泱,感谢您阅读本文,本期分享基于Grafana+Loki+Alloy构建轻量级日志系统。本期内容灵感来源于B站@雷丰阳直播课(B站:https://www.bilibili.com/video/BV17ZyKYeEPh;雷神个人语雀:https://www.yuque.com/leifengyang)
本期导语
在生产实际中对日志的采集、加工、存储、检索、分析、可视化、监控告警等是不可缺失的,这就需要一套较为完整且统一的日志平台,一般而言企业会进行如下选择:
- 若企业IT系统已上云一般会选择云厂商自带的日志系统,比如阿里云的日志服务SLS(官网:https://help.aliyun.com/zh/sls)等等;
- 基于开源解决方案自建或二次开发的日志系统,比如ELK、Flume+Hadoop+Flink+Spuerset、ClickHouse等等;
顺便提一下阿里云的SLS产品功能确实挺强大,在创建索引前提下,能够对日志编写SQL和SPL(SLS产品自定义的分析处理语法)分析聚合日志并实现多样可视化以及告警等功能。所以笔者就寻思除了传统的EKL方案,是否还有更轻量级又能编写类似SQL语法的开源代替方案呢?毕竟如今的云原生生态如此庞大,总有优秀的开源代替方案,所以本期笔者来分享一套基于Grafana+Loki+Alloy构建轻量级日志系统。
Loki
Loki是什么?
咱们先看Grafana官网(https://grafana.com/docs/loki/latest/get-started/overview/)原文定义描述:“Loki is a horizontally scalable, highly available, multi-tenant log aggregation system inspired by Prometheus. It is designed to be very cost effective and easy to operate. It does not index the contents of the logs, but rather a set of labels for each log stream.”
翻译拓展即:Loki 项目于 2018 年在 Grafana Labs 启动,是Grafana生态的一部分,Loki 是一个可水平扩展、高可用性、多租户日志聚合系统,其灵感来自 Prometheus。与 Prometheus 的不同之处在于,它专注于日志而不是指标,并通过推送而不是拉取来收集日志。Loki 的设计非常经济高效,并且具有高度可扩展性。与其他日志系统不同,Loki 不会对日志内容进行索引,而只会对日志的元数据进行索引,将其作为每个日志流的一组标签。
日志流是一组共享相同标签的日志。标签帮助Loki在您的数据存储中找到日志流,因此拥有一组高质量的标签是高效查询执行的关键。然后,日志数据被压缩并以块的形式存储在对象存储中,如亚马逊简单存储服务(S3)或谷歌云存储(GCS),甚至为了开发或概念验证,存储在文件系统上。小索引和高度压缩的块简化了操作,显著降低了Loki的成本。
那为什么说Loki是一个轻量级日志系统呢?这里的轻量级不是说Loki的功能小,是而相对于传统的ELK方案而言,ELK是基于Java语言开发,由于GC机制天生占用内存资源大,而Grafana Labs提供的这套Loki基于Go语言开发,天生占用内存资源相对较低,在相同配置服务器情况运行起来更为轻量级,当然前文提到Loki 不会对日志内容进行索引,而只会对日志的元数据进行索引,这也是轻量级的一种表现,节省了不少的日志存储空间。
Loki的数据存储格式
Grafana Loki 有两种主要的文件类型:
○ index:索引;存储Loki标签,如日志级别、来源、分组
○ chunk:块;存储日志条目本身
上图源于https://grafana.com/docs/loki/latest/get-started/architecture/
Loki的架构
上图源于https://grafana.com/docs/loki/latest/get-started/overview/
一个典型的基于 Loki 的日志系统由 3 个组件组成:
Agent:代理或客户端,例如Grafana Alloy或Promtail,随Loki一起分发。代理抓取日志,通过添加标签将日志转换为流,并通过HTTP API将流推送到Loki。
Loki主服务器:负责摄取和存储日志以及处理查询。它可以部署在三种不同的配置中,有关更多信息,请参阅部署模式。
Grafana:用于查询和显示日志数据。您还可以使用LogCLI或直接使用Loki API从命令行查询日志。
Loki的工作流程
上图源于https://grafana.com/docs/loki/latest/get-started/architecture/
写流程
- distributor 接收带有流和日志行的HTTP POST请求。
- distributor 会 hash 计算请求中包含的每个流,决定发给 一致性 hash 环 中的哪个 ingester
- distributor 把每个流 发给合适处理它的 ingester 和其副本
- ingester 接收带有日志行的流,并为流的数据创建一个块或附加到现有块。每个租户和每个标签集,块都是唯一的
- ingester 回复写操作结果
- distributor 等待大多数 ingester 确认写入完成。
- distributor 在收到至少法定数量的确认写入时响应成功(2xx状态码)。或者在写入操作失败时响应错误(4xx或5xx状态码)。
读流程
- 查询前端(query frontend) 接受到 携带 LogQL 的 HTTP GET 请求
- 查询前端 将查询拆分为子查询并将它们传递给查询调度程序(query scheduler)。
- querier (查询器)从调度程序(scheduler)中提取子查询。
- querier 将查询传递给 所有保存数据的 ingester。
- ingester 返回与查询匹配的 记忆数据(如果有)。
- 如果 ingester 没有返回或返回的数据不足,querier 会延迟从后备存储加载数据并对其运行查询。
- querier 遍历所有接收到的数据并进行重复数据删除,将子查询的结果返回到查询前端。
- 查询前端 等待 查询的所有子查询完成 并由 querier 返回。
- 查询前端将两个结果合并为最终结果并将其返回给客户端。
Alloy
Alloy的架构
Grafna官网对Alloy的定义描述原文:“Alloy is a flexible, high performance, vendor-neutral distribution of the OpenTelemetry Collector. It’s fully compatible with the most popular open source observability standards such as OpenTelemetry and Prometheus.”
翻译拓展即:Grafana Alloy是一个多功能的可观测性收集器,也是Grafana生态的一部分,可以摄取各种格式的日志并将其发送到Loki。推荐Alloy作为向Loki发送日志的主要方法,因为它为构建高度可扩展和可靠的可观测性流水线提供了更强大和特征丰富的解决方案。
Alloy的组件
Type |
Component |
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Collector |
|
Transformer |
|
Transformer |
|
Writer |
|
Writer |
|
Writer |
快速搭建
参阅Grafana官网文档(https://grafana.com/docs/loki/latest/get-started/quick-start/),我们可以用Docker Compose 部署以下服务,快速体验Loki生态:
- flog:生成日志行。flog是常见日志格式的日志生成器。
- Grafana Alloy:它从flog上抓取日志线,并通过网关将它们推送到Loki。
- 网关(nginx):接收请求并根据请求的URL将它们重定向到适当的容器。
- Loki read组件:它运行一个查询前端和一个查询器。
- Loki write组件:它运行一个分发器和一个Ingester。
- Loki 后端组件:它运行索引网关、压缩器、标尺、Bloom压缩器(实验)和Bloom网关(实验)。
- Minio:Loki用来存储其索引和块。
- Grafana:它提供了在Loki中捕获的日志行的可视化。
上图源于https://grafana.com/docs/loki/latest/get-started/quick-start/
准备
# 1、准备目录 mkdir evaluate-loki cd evaluate-loki # 2、下载默认配置文件 wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/loki-config.yaml -O loki-config.yaml wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/alloy-local-config.yaml -O alloy-local-config.yaml wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/docker-compose.yaml -O docker-compose.yaml
部署
docker compose up -d
注:由于某些限制,Docker官方镜像仓库在国内访问受阻,导致拉取镜像失败,解决方案可以使用“魔法”,对于没条件使用“魔法”的读者朋友,笔者推荐您使用国内镜像加速和代理解决方案,详细配置可参照https://cloud.tencent.com/developer/article/2485043 该文档的作者在长期维护更新
验证
待所有容器都处于Up且healthy状态,可通过docker ps命令查看
Read组件: http://your_server_IP:3101/ready
Write组件:http://your_server_IP:3102/ready
Grafana Alloy UI:http://your_server_IP:12345
预期如下:
Grafana
查看日志
可以使用LogCli或者Grafana可视化界面查看日志
使用 Grafana 查询 Loki 数据源的数据
- 访问Grafana:http://your_server_IP:3000/
- 已经整合了Loki数据源
- 点击 Explore 查看
- 使用Code模式,编写 LogQL 查询
查询示例:
标签检索
# 比如我们要查看 container 标签值 为 evaluate-loki-flog-1 的日志 {container="evaluate-loki-flog-1"} {container="evaluate-loki-grafana-1"}
包含值
# 查看 container 标签值 为 evaluate-loki-flog-1 ,且 json 格式中 status字段值为404 {container="evaluate-loki-flog-1"} | json | status=`404`
计算
sum by(container) (rate({container="evaluate-loki-flog-1"} | json | status=`404` [$__auto]))
其他
{container="evaluate-loki-flog-1"} {container="evaluate-loki-flog-1"} |= "GET" {container="evaluate-loki-flog-1"} |= "POST" {container="evaluate-loki-flog-1"} | json | method="POST" {container="evaluate-loki-flog-1"} | json | status="401" {container="evaluate-loki-flog-1"} != "401"
更多 LogQL 语法请参阅:https://grafana.com/docs/loki/latest/query/
本期总结
本期内容简要介绍了基于Grafana+Loki+Alloy构建的开源日志系统的架构、快速入门,希望为大家在做日志采集监控分析场景需求时提供参考解决方案。
通过本期文章,笔者想再发散讲一下有关架构的内容,从Loki这套系统架构细心的读者可能发现Loki既能做write组件也能做reader组件还能做backend组件,Loki本身是一个融合了多个模块的单体应用,但它可以根据所需功能解耦,可以进行分布式部署,从部署层面看最终呈现出一些微服务特征。
其实上述,笔者想引出一种介于单体和微服务之间的另一种架构风格“巨型单体模块化”,即同一个应用封装了某个业务场景的多种功能模块,可单体部署也可分布式部署,分布式部署后整体协同实现了单体全部功能,这类架构风格可以进一步抽象出一种架构思想:“部分即整体,拥有自相似性”,这是一种可行且美妙的架构思想,其美妙程度不亚于数学中的分形,当然“巨型单体模块化”目前仅适用于某些特定业务场景,其也会带来一些挑战,比如在一个单体中融入太多模块,过于巨型臃肿,资源占用,代码重复率,耦合度增加,模块之间依赖等问题,可能还得引入可插拔技术工具去解耦,但这无疑增加了复杂度,但至少提供了一种软件架构思路。
最后,感谢您的阅读!系列文章会同步在微信公众号@云上的喵酱、阿里云开发者社区@云上的喵酱、CSDN@笠泱 更新,您的点赞+关注+转发是我后续更新的动力!