目录

构建loki日志系统

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

1. 介绍

与其他日志聚合系统相比,Loki具有下面的一些特性:

  • 不对日志进行全文索引。Loki中存储的是压缩后的非结构化日志,并且只对元数据建立索引,因此Loki 具有操作简单、低成本的优势。
  • 使用与 Prometheus 相同的标签。Loki通过标签对日志进行索引和分组,这使得日志的扩展和操作效率更高。
  • 特别适合储存 Kubernetes Pod 日志。诸如 Pod 标签之类的元数据会被自动删除和编入索引。
  • Grafana 原生支持。

Loki 日志系统由以下3个部分组成:

  • loki是主服务器,负责存储日志和处理查询。
  • promtail是专为loki定制的代理,负责收集日志并将其发送给 loki
  • Grafana用于 UI展示。

2. 架构

../images/loki-1.jpg

如图所示,Loki 包含DistributorIngesterQuerier和可选的Query frontend五个组件。每个组件都会起一个用于处理内部请求的 gRPC 服务器和一个用于处理外部 API 请求的 HTTP/1服务器。

i. Distributor

Distributor 是客户端连接的组件,用于收集日志。

../images/loki-2.jpg

promtail 收集并将日志发送给Loki 之后, Distributor 就是第一个接收它们的组件,每秒可以接收数百万次写入。Distributor会对接收到的日志流进行正确性校验,并将验证后的chunk日志块分批并行发送到Ingester

Loki使用一致性哈希来保证数据流和Ingester的一致性,他们共同在一个哈希环上,哈希环的信息可以存放到etcdConsul或者内存中。当使用*Consul作为哈希环的实现时,所有Ingester通过一组token注册到环中,每个token是一个随机的32-bit无符号整数,同时Ingester会上报其状态到哈希环中。由于所有的Distributor使用相同的hash环,写请求可以发送至任意节点。为了保证结果的一致性,Distributor会等待收到至少一半加一个Ingester*的回复后才响应客户端。

ii. Ingester Ingester 接收来自Distributor的日志流,并将日志压缩后存放到所连接的存储后端。

../images/loki-3.jpg

Ingester接受日志流并构建数据块,其操作通常是压缩和追加日志。每个Ingester 的生命周期有PENDING, JOINING, ACTIVE, LEAVINGUNHEALTHY 五种状态。处于JOININGACTIVE状态的Ingester可以接受写请求,处于ACTIVELEAVING状态时可以接受读请求。 Ingester 将收到的日志流在内存中打包成 chunks ,并定期同步到存储后端。由于存储的数据类型不同,Loki 的数据块和索引可以使用不同的存储。

../images/loki-4.jpg

当满足以下条件时,chunks 会被标记为只读

  1. 当前 chunk 达到配置的最大容量
  2. 当前 chunk 长时间没有更新
  3. 发生了定期同步

当旧的 chunk 经过了压缩并被打上了只读标志后,新的可写的 chunk 就会生成。

iii. Querier Querier 用来查询日志,可以直接从 Ingester 和后端存储中查询数据。当客户端给定时间区间和标签选择器之后,Querier 就会查找索引来确定所有匹配 chunk ,然后对选中的日志进行 grep并返回查询结果。查询时,Querier先访问所有Ingester用于获取其内存数据,只有当内存中没有符合条件的数据时,才会向存储后端发起同样的查询请求。 需要注意的是,对于每个查询,单个 Querier 会 grep 所有相关的日志。目前 Cortex 中已经实现了并行查询,该功能可以扩展到 Loki,通过分布式的 grep 加速查询。此外,由于副本因子的存在,Querier可能会接收到重复的数据,所以其内置了去重的功能,对拥有同样时间戳、标签组和消息内容的日志进行去重处理。

iv. Query Frontend Query frontend 是可选组件,其提供了Querier的API并可用于读加速。当系统中有该组件时,所有的读请求都会经由Query frontend而非Querier处理。 Query frontend是无状态的,生产环境中推荐 2 副本来达到调度的均衡。Query frontend会对请求做一些调整,并将请求放入一个内部的队列中。在该场景中,Querier作为workers 不断从队列中获取任务、执行任务,并将结果返回给Query frontend用于聚合。

3. 读写

i. Read Path

../images/loki-5.jpg

  1. Querier 收到 HTTP 请求
  2. Querier 将请求发送至Ingester 用以获取内存数据
  3. Ingester 收到请求后返回符合条件的数据
  4. 如果没有Ingester 返回数据,Querier 从后端存储加载数据并执行查询
  5. Querier 遍历所有数据并进行去重处理,通过HTTP连接返回最终结果

ii. Write Path 日志数据的写主要依托的是Distributor和Ingester两个组件,整体流程如下:

../images/loki-6.jpg

  1. Distributor 收到 HTTP 请求,用于存储流数据
  2. 通过 hash 环对数据流进行 hash
  3. Distributor将数据流发送到对应的Ingester及其副本上
  4. Ingester 新建 Chunk 或将数据追加到已有Chunk
  5. Distributor通过 HTTP连接发送响应信息

3. 部署

本篇通过目前最新版微服务架构进行部署,部署工具是helm

需要一套k8s集群,客户端,helm工具

helm repo资源

1
2
3
grafana/grafana
grafana/loki-distributed
grafana/promtail

创建namespace

1
kubectl create ns loki

部署loki微服务

下载repo到本地

1
helm pull grafana/loki-distributed

添加s3存储,修改value.yaml

未修改部分已省略,非删除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    ruler:
      storage:
        type: s3
        s3:
          s3: s3://AKID:AKSECRET@ENDPOINT/BUCKET
          s3forcepathstyle: true
          insecure: true
          http_config:
            insecure_skip_verify: true
      # rule_path是指容器内的目录,必须容器内存在
      rule_path: /var/loki/scratch
      alertmanager_url: https://alertmanager.xx
      external_url: https://alertmanager.xx

    schema_config:
      configs:
        - from: 2018-04-15
          store: boltdb
          object_store: aws
          schema: v11
          index:
            prefix: index_
            period: 0

    storage_config:
      boltdb:
         # directory是指容器内的目录,必须容器内存在
         directory: /var/loki/index
      aws:
        s3: s3://AKID:AKSECRET@ENDPOINT/BUCKET
        s3forcepathstyle: true

部署loki-distributed

1
2
3
4
5
6
7
8
helm upgrade --install loki --namespace=loki grafana/loki-distributed \
--set ingester.persistence.enabled=true \
--set ingester.persistence.size=20Gi \
--set ingester.persistence.storageClass=alicloud-disk-essd \
--set querier.persistence.enabled=true \
--set querier.persistence.size=20Gi \
--set querier.persistence.storageClass=alicloud-disk-essd \
-f values.yaml

查看状态

1
2
3
4
5
6
7
kubectl get pod -n loki
NAME                                                    READY   STATUS    RESTARTS   AGE
loki-loki-distributed-distributor-674dd4d4f7-qk5s5      1/1     Running   0          3m
loki-loki-distributed-gateway-68dcfb74f-zlsq4           1/1     Running   0          3m
loki-loki-distributed-ingester-0                        1/1     Running   0          3m
loki-loki-distributed-querier-0                         1/1     Running   0          3m
loki-loki-distributed-query-frontend-58d697b855-dw9r7   1/1     Running   0          3m

部署promtail

1
helm install promtail -n loki grafana/promtail --set config.clients[0].url=http://loki-loki-distributed-gateway/loki/api/v1/push

查看状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
kubectl get pod -n loki
NAME                                                    READY   STATUS    RESTARTS   AGE
loki-loki-distributed-distributor-674dd4d4f7-qk5s5      1/1     Running   0          4m
loki-loki-distributed-gateway-68dcfb74f-zlsq4           1/1     Running   0          4m
loki-loki-distributed-ingester-0                        1/1     Running   0          4m
loki-loki-distributed-querier-0                         1/1     Running   0          4m
loki-loki-distributed-query-frontend-58d697b855-dw9r7   1/1     Running   0          4m
promtail-4b55p                                          1/1     Running   0          42s
promtail-4jv22                                          1/1     Running   0          42s
promtail-5cv55                                          1/1     Running   0          42s
promtail-bgwph                                          1/1     Running   0          42s
promtail-jbhgl                                          1/1     Running   0          42s
promtail-krgr7                                          1/1     Running   0          42s
promtail-ldwcq                                          1/1     Running   0          42s
promtail-nd58l                                          1/1     Running   0          42s
promtail-ps8cz                                          1/1     Running   0          42s
promtail-s5d6p                                          1/1     Running   0          42s
promtail-s8b2s                                          1/1     Running   0          42s

部署grafana

1
helm install loki-grafana --namespace=loki grafana/grafana

查看admin密码

1
kubectl get secret --namespace loki loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

添加数据源

../images/loki-7.png

查询日志

../images/loki-8.png

  • 报错:loki level=error ts=2020-05-06T07:12:06.390260138Z caller=main.go:70 msg=“error initialising loki” err=“error initialising module: table-manager: Must set -dynamodb.url in aws mode”

    参考:https://github.com/grafana/loki/issues/1434

  • 报错:level=error ts=2021-11-09T11:42:37.712125923Z caller=log.go:106 msg=“error running loki” err=“mkdir /loki/index: read-only file system······” 参考:https://github.com/grafana/loki/issues/4715

更多问题参考:https://www.jianshu.com/p/6b24340c2cf1

只要按照本文中配置安装,不会遇到这些坑