云原生日志神器之Loki

一 背景

1.1 需求

随着云原生技术的发展,传统的日志解决方案面临着新的挑战。日志量剧增、数据结构复杂、实时分析需求等都对传统的日志服务提出了更高的要求。Loki作为一款新兴的云原生日志聚合系统,凭借其高可扩展性、低成本和易用性,越来越受到业界的关注。京东智联云,作为国内领先的云服务提供商,也正在将云翼的日志服务底层逐步从ES替换为Loki。 本文将基于京东智联云对Loki的使用和理解,从其产生的背景、解决的问题、采用的方案、系统架构、实现逻辑等方面进行剖析,希望对关注Loki的小伙伴们提供一些帮助。

1.2 loki简介

Loki 是一个受 Prometheus 启发的水平可伸缩的、高可用的、多租户的日志聚合系统。它的设计非常具有成本效益和易于操作。它不索引日志的内容,而是索引每个日志流的一组标签。

1.3 痛点

传统日志解决方案,如基于ES的日志服务,在面对云原生环境下的海量日志数据时,面临着以下问题:

  • 成本高昂: ES作为一款功能强大的搜索引擎,在存储和查询日志数据时,需要大量的硬件资源,导致成本高昂。
  • 可扩展性不足: ES的扩展性受限于集群规模和节点数量,难以满足快速增长的日志数据需求。
  • 查询效率低: ES的查询效率会随着数据量和查询复杂度而下降,难以满足实时分析需求。

二 特点

2.1 Loki特点

Loki采用了一种独特的方法,只对元数据而不是日志行的全文进行索引:

云原生轻量级日志神器之Loki_自动化运维

Loki的最小索引方法意味着在Loki中存储相同的日志集所需的存储量远低于其他解决方案

  • 非常容易上手,因为您可以使用广泛的客户机阵列,从任何来源以任何格式发送日志
  • 构建度量标准并从日志行生成警报
  • 对对象存储的100%持久化意味着您可以获得Pabyte规模、高吞吐量和经济高效的持久存储
  • 没有摄取日志格式化要求,为您提供了更大的灵活性和在查询时格式化的选项
  • 实时跟踪您的日志,以查看进入系统的日志,每隔一段时间更新日志,查看特定日期的日志,等等。
  • 与Prometheus、Grafana和K8s原生集成,因此您可以在单个UI中无缝地在度量、日志和跟踪之间移动

2.2 Loki与其他系统对比

  • 不会对日志进行全文索引。通过存储压缩的、非结构化的日志,只索引元数据,Loki 操作更简单,运行成本也更低。
  • 索引和组日志流使用与 Prometheus 相同的标签,使您能够使用与 Prometheus 相同的标签在度量和日志之间无缝切换。
  • 是一个特别适合存储库伯尼特斯 Pod 原木。像 Pod 标签这样的元数据会被自动刮取并编入索引。
  • 在 Grafana 得到了本地支持(需要 Grafana v6.0)。

2.3 loki的组件

  • Distributor(分发器)

Distributor负责处理客户端请求,也就是将日志数据写入到指定路径之前,先要经过Distributor。Distributor会验证客户端输入流的合法性和正确性,确保传入的用户是合法的租户。然后将每一个chunk(块)拆分成多个批次,并行发送到多个ingester(摄取器中)

  • Ingester(摄取器)

Ingester摄取器负责将接收到的日志数据写入到 Storage Backend中(可以是S3、Cassandra或者本地文件系统中),并负责将日志数据读取到内存中供外部查询。

  • [可选]Query fronted(查询前端)

查询前端是可选的组件,它提供了一个日志数据查询的API端口。如果Loki部署了Query fronted组件,那么客户端会将查询发送给Query fronted,而不发给Querier(查询器)。而查询前端还是要将查询交给Querier(查询器)执行查询的。

  • Querier(查询器)

Querier组件用于接收LogQL语言进行日志数据查询,它会同时从Ingester组件以及后端存储中查询日志。

  • Chunk Store(块存储)

Chunk Store是用于长期存储Loki日志的存储,并要求块存储能够支持交互式查询和持续写入。Loki支持的chunk store有:Amazon DynamoDB/Google BigTable/Apache Cassandra/Amazon S3/Google Cloud Storage

Chunk Store并不是单独的服务,而是以库的形式提供(在Ingester和Querier使用到)

2.4 原理

Loki 类似于 Prometheus,但是对于日志: 我们更喜欢使用基于多维标签的索引方法,并且希望使用单一二进制文件,操作起来很方便,没有依赖关系。洛基与普罗米修斯不同,他们更关注日志而不是度量,通过推送而不是拉送日志。

云原生轻量级日志神器之Loki_数据_02

用Promtail拉入任何源日志:Promtail是专门为Loki构建的日志收集器。它使用与Prometheus相同的服务发现,并包括类似的特性,用于在将日志输入到Loki之前对日志进行标记、转换和过滤。

将日志存储在Loki中:Loki不对日志的文本进行索引。取而代之的是,条目被分组到流中,并用标签进行索引。这不仅降低了成本,还意味着日志行可以在Loki收到后的毫秒内查询。

使用LogQL来暴露:使用Loki强大的查询语言LogQL来查看日志。直接在Grafana中运行LogQL查询,以便将日志与其他数据源一起可视化,或者使用LogCLI来可视化,以便那些喜欢命令行体验的人使用。

日志上的警报:为Loki设置警报规则,以便对传入的日志数据进行评估。配置Loki将生成的警报发送到Prometheus Alertmanager,以便将它们路由到正确的团队。

2.5 Loki的架构与实现

Loki的系统架构主要由以下几个组件构成:

  • Promtail: 负责从各种来源收集日志数据,并将其转发到Loki服务器。
  • Loki服务器: 负责存储和索引日志数据,并提供查询接口。
  • Grafana: 可视化工具,用于展示和分析Loki中的日志数据。

Loki的实现逻辑可以概括为以下几个步骤:

  1. 日志采集: Promtail从应用、容器等来源收集日志数据,并将数据以文本格式存储在本地磁盘。
  2. 数据传输: Promtail将收集的日志数据压缩并传输到Loki服务器。
  3. 数据存储: Loki服务器将日志数据存储在分布式存储系统中,并使用索引结构来提高查询效率。
  4. 日志查询: 用户可以通过PromQL查询语言,对Loki中的日志数据进行筛选、聚合、排序等操作。
  5. 可视化展示: Grafana可以将Loki中的日志数据以图表、表格等形式展现出来,方便用户分析和理解。

三 部署

3.1 安装Loki

3.1.1 利用helm安装loki
helm repo add loki https://grafana.github.io/loki/charts
helm repo update
# 通过存储类部署

[root@master ~]# helm upgrade --install loki --namespace=loki loki/loki-stack  --set grafana.enabled=true,prometheus.enabled=true,prometheus.alertmanager.persistentVolume.enabled=false,prometheus.server.persistentVolume.enabled=false,loki.persistence.enabled=true,loki.persistence.storageClassName=rbd,loki.persistence.size=5Gi
Release "loki" does not exist. Installing it now.
NAME:   loki
LAST DEPLOYED: Fri Dec 18 15:33:33 2020
NAMESPACE: loki
STATUS: DEPLOYED

RESOURCES:
==> v1/ClusterRole
NAME                          AGE
loki-grafana-clusterrole      2s
loki-prometheus-alertmanager  2s
loki-prometheus-pushgateway   2s
loki-prometheus-server        2s
loki-promtail-clusterrole     2s

==> v1/ClusterRoleBinding
NAME                              AGE
loki-grafana-clusterrolebinding   2s
loki-prometheus-alertmanager      2s
loki-prometheus-pushgateway       2s
loki-prometheus-server            2s
loki-promtail-clusterrolebinding  2s

==> v1/ConfigMap
NAME                          DATA  AGE
loki-grafana                  1     1s
loki-grafana-test             1     1s
loki-loki-stack               1     1s
loki-loki-stack-test          1     1s
loki-prometheus-alertmanager  1     1s
loki-prometheus-server        5     1s
loki-promtail                 1     1s

==> v1/DaemonSet
NAME                           DESIRED  CURRENT  READY  UP-TO-DATE  AVAILABLE  NODE SELECTOR  AGE
loki-prometheus-node-exporter  4        4        0      4           0          <none>         1s
loki-promtail                  4        4        0      4           0          <none>         1s

==> v1/Deployment
NAME                          READY  UP-TO-DATE  AVAILABLE  AGE
loki-grafana                  0/1    1           0          1s
loki-kube-state-metrics       0/1    1           0          1s
loki-prometheus-alertmanager  0/1    1           0          1s
loki-prometheus-pushgateway   0/1    1           0          1s
loki-prometheus-server        0/1    1           0          1s

==> v1/Pod(related)
NAME    READY  STATUS   RESTARTS  AGE
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s
loki-0  0/1    Pending  0         2s

==> v1/Role
NAME               AGE
loki               2s
loki-grafana-test  2s
loki-promtail      2s

==> v1/RoleBinding
NAME               AGE
loki               2s
loki-grafana-test  2s
loki-promtail      2s

==> v1/Secret
NAME          TYPE    DATA  AGE
loki          Opaque  1     1s
loki-grafana  Opaque  3     1s

==> v1/Service
NAME                           TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)   AGE
loki                           ClusterIP  10.233.35.11   <none>       3100/TCP  1s
loki-grafana                   ClusterIP  10.233.55.139  <none>       80/TCP    1s
loki-headless                  ClusterIP  None           <none>       3100/TCP  1s
loki-kube-state-metrics        ClusterIP  10.233.17.40   <none>       8080/TCP  1s
loki-prometheus-alertmanager   ClusterIP  10.233.26.110  <none>       80/TCP    1s
loki-prometheus-node-exporter  ClusterIP  None           <none>       9100/TCP  1s
loki-prometheus-pushgateway    ClusterIP  10.233.25.28   <none>       9091/TCP  1s
loki-prometheus-server         ClusterIP  10.233.46.145  <none>       80/TCP    1s

==> v1/ServiceAccount
NAME                           SECRETS  AGE
loki                           1        1s
loki-grafana                   1        1s
loki-grafana-test              1        1s
loki-kube-state-metrics        1        1s
loki-prometheus-alertmanager   1        1s
loki-prometheus-node-exporter  1        1s
loki-prometheus-pushgateway    1        1s
loki-prometheus-server         1        1s
loki-promtail                  1        1s

==> v1/StatefulSet
NAME  READY  AGE
loki  0/1    1s

==> v1beta1/ClusterRole
NAME                     AGE
loki-kube-state-metrics  2s

==> v1beta1/ClusterRoleBinding
NAME                     AGE
loki-kube-state-metrics  2s

==> v1beta1/PodSecurityPolicy
NAME               PRIV   CAPS      SELINUX           RUNASUSER  FSGROUP    SUPGROUP  READONLYROOTFS  VOLUMES
loki               false  RunAsAny  MustRunAsNonRoot  MustRunAs  MustRunAs  true      configMap,emptyDir,persistentVolumeClaim,secret,projected,downwardAPI
loki-grafana       false  RunAsAny  RunAsAny          RunAsAny   RunAsAny   false     configMap,emptyDir,projected,secret,downwardAPI,persistentVolumeClaim
loki-grafana-test  false  RunAsAny  RunAsAny          RunAsAny   RunAsAny   false     configMap,downwardAPI,emptyDir,projected,secret
loki-promtail      false  RunAsAny  RunAsAny          RunAsAny   RunAsAny   true      secret,configMap,hostPath,projected,downwardAPI,emptyDir

==> v1beta1/Role
NAME          AGE
loki-grafana  2s

==> v1beta1/RoleBinding
NAME          AGE
loki-grafana  2s


NOTES:
The Loki stack has been deployed to your cluster. Loki can now be added as a datasource in Grafana.

See http://docs.grafana.org/features/datasources/loki/ for more detail.
  • 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.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.

部署完成

xuel@kaliarchmacbookpro  ~   master  k get all -n loki
NAME                                               READY   STATUS    RESTARTS   AGE
pod/loki-0                                         1/1     Running   0          116m
pod/loki-grafana-9c55b4589-mkvrh                   1/1     Running   0          116m
pod/loki-kube-state-metrics-7f9f667d7d-k9ctr       1/1     Running   0          116m
pod/loki-prometheus-alertmanager-9bb4c6f8f-t6vbk   2/2     Running   0          116m
pod/loki-prometheus-node-exporter-lcj2v            1/1     Running   0          116m
pod/loki-prometheus-pushgateway-664fd45795-5lz6f   1/1     Running   0          116m
pod/loki-prometheus-server-5d6f9d5c6c-25cfx        2/2     Running   0          116m
pod/loki-promtail-ndvzg                            1/1     Running   0          116m

NAME                                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/loki                            ClusterIP   10.96.196.180   <none>        3100/TCP   116m
service/loki-grafana                    ClusterIP   10.96.106.18    <none>        80/TCP     116m
service/loki-headless                   ClusterIP   None            <none>        3100/TCP   116m
service/loki-kube-state-metrics         ClusterIP   10.96.171.19    <none>        8080/TCP   116m
service/loki-prometheus-alertmanager    ClusterIP   10.96.184.187   <none>        80/TCP     116m
service/loki-prometheus-node-exporter   ClusterIP   None            <none>        9100/TCP   116m
service/loki-prometheus-pushgateway     ClusterIP   10.96.128.101   <none>        9091/TCP   116m
service/loki-prometheus-server          ClusterIP   10.96.41.33     <none>        80/TCP     116m

NAME                                           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/loki-prometheus-node-exporter   1         1         1       1            1           <none>          116m
daemonset.apps/loki-promtail                   1         1         1       1            1           <none>          116m

NAME                                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/loki-grafana                   1/1     1            1           116m
deployment.apps/loki-kube-state-metrics        1/1     1            1           116m
deployment.apps/loki-prometheus-alertmanager   1/1     1            1           116m
deployment.apps/loki-prometheus-pushgateway    1/1     1            1           116m
deployment.apps/loki-prometheus-server         1/1     1            1           116m

NAME                                                     DESIRED   CURRENT   READY   AGE
replicaset.apps/loki-grafana-9c55b4589                   1         1         1       116m
replicaset.apps/loki-kube-state-metrics-7f9f667d7d       1         1         1       116m
replicaset.apps/loki-prometheus-alertmanager-9bb4c6f8f   1         1         1       116m
replicaset.apps/loki-prometheus-pushgateway-664fd45795   1         1         1       116m
replicaset.apps/loki-prometheus-server-5d6f9d5c6c        1         1         1       116m

NAME                    READY   AGE
statefulset.apps/loki   1/1     116m
  • 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.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

查看服务

云原生轻量级日志神器之Loki_hg_03

3.2 访问

由于在本地测试,临时使用port-forward,线上环境可改为Lb或Nodeport

k port-forward -n loki --address 0.0.0.0 svc/loki-grafana 80:80
  • 1.

查看登录密码

kubectl get secret --namespace loki loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
  • 1.
  • 数据源已经添加好

云原生轻量级日志神器之Loki_TCP_04

四 使用

云原生轻量级日志神器之Loki_TCP_05

去重日志

云原生轻量级日志神器之Loki_云原生_06

实时查看

云原生轻量级日志神器之Loki_数据_07

云原生轻量级日志神器之Loki_云原生_08

可以查看查询历史,或starred 历史,方便后期查询

云原生轻量级日志神器之Loki_TCP_09

五 配置日志采集

loki 日志采集使用的promtail,

k get cm -n loki loki-promtail -oyaml
  • 1.

云原生轻量级日志神器之Loki_hg_10

其中对静态Pod/业务Pod/直接控制器Pod和非直接控制器Pod都有对应的日志收集配置。

六 日志选择与过滤

6.1、日志选择器

对于查询表达式的标签部分,将其用大括号括起来{},然后使用键值语法选择标签。多个标签表达式用逗号分隔:

{app="mysql",name="mysql-backup"}
  • 1.

当前支持以下标签匹配运算符:

  • = 完全相等。
  • != 不相等。
  • =~ 正则表达式匹配。
  • !~ 不进行正则表达式匹配。

例子:

{name=~"mysql.+"}
{name!~"mysql.+"}
  • 1.
  • 2.

6.2、日志过滤器

编写日志流选择器后,您可以通过编写搜索表达式来进一步过滤结果。搜索表达式可以只是文本或正则表达式。 查询示例:

{job="mysql"} |= "error"
{name="kafka"} |~ "tsdb-ops.*io:2003"
{instance=~"kafka-[23]",name="kafka"} != kafka.server:type=ReplicaManager
  • 1.
  • 2.
  • 3.

过滤器运算符可以被链接,并将顺序过滤表达式-结果日志行将满足每个过滤器。例如:

{job="mysql"} |= "error" != "timeout"
  • 1.

已实现以下过滤器类型:

  • |= 行包含字符串。
  • != 行不包含字符串。
  • |~ 行匹配正则表达式。
  • !~ 行与正则表达式不匹配。 regex表达式接受RE2语法。默认情况下,匹配项区分大小写,并且可以将regex切换为不区分大小写的前缀(?i)。

注意事项

  • 数据格式: Loki 主要使用 Promtail 采集日志,并以 文本格式 进行存储。确保您的日志格式符合 Loki 的要求,可以使用 promtail-client 工具进行测试。
  • 数据量: Loki 擅长处理大量日志数据,但仍需注意存储和查询性能。对于超大规模的日志,可以考虑使用数据压缩、数据切片等优化策略。
  • 日志查询: Loki 使用 PromQL 语法进行日志查询,这与 Prometheus 的监控查询语言相同。熟悉 PromQL 是高效使用 Loki 的前提。
  • 安全性: Loki 支持多种身份验证和授权机制,可以有效保护日志数据安全。注意配置相关安全策略,并定期进行安全审计。
  • 日志保留策略: 设置合理的日志保留策略,确保日志数据既能满足您的需求,又不造成存储空间浪费。
  • 日志可视化: Loki 提供丰富的可视化工具,可以帮助您直观地分析日志数据。选择合适的可视化工具,并根据您的需求进行配置。
  • 与其他工具整合: Loki 可以与其他云原生工具,如 Prometheus、Grafana 等进行整合,实现更加强大的日志分析和监控功能。

总结

Loki 是一款强大的云原生日志聚合系统,可以帮助您高效地收集、存储、查询和分析日志数据。但在使用 Loki 时,需注意数据格式、数据量、日志查询、安全性、日志保留策略、日志可视化以及与其他工具的整合等方面,才能充分发挥 Loki 的优势。

参考链接

  • grafana.com/docs/loki/l…
  • prometheus.io/docs/promet…
  • grafana.com/