# 日志聚合工具Loki

本文章简要介绍了如何在腾讯云的TKE集群中搭建Loki日志采集工具。

## Loki日志系统介绍

Loki是受Prometheus启发的水平可扩展，高度可用的多租户日志聚合系统。它的设计具有很高的成本效益，并且易于操作。它不索引日志的内容，而是为每个日志流设置一组标签。

与其他日志聚合系统相比，Loki的特点

* 不对日志进行全文本索引。通过存储压缩的，非结构化的日志以及仅索引元数据，Loki更加易于操作且运行成本更低。
* 使用与Prometheus相同的标签对日志流进行索引和分组，从而使您能够使用与Prometheus相同的标签在指标和日志之间无缝切换。
* 特别适合存储Kubernetes Pod日志。诸如Pod标签之类的元数据会自动被抓取并建立索引。
* 在Grafana中具有本机支持（需要Grafana v6.0）。

基于Loki的日志记录堆栈包含3个组件：

* promtail是代理，负责收集日志并将其发送给Loki。
* loki是主服务器，负责存储日志和处理查询。
* Grafana用于查询和显示日志。

![upload-image](/files/-MN2J9-4Llqef9Ylpp3v)

Loki其实类似于elk和prometheus的架构，部署在k8s中，通过DaemonSet的方式部署promtail用来采集节点的日志，将采集的日志数据投递到loki，最后通过grafana来进行前端的界面查询和展示。

Loki像Prometheus一样，但是对于日志：我们更喜欢基于多维标签的索引方法，并且想要一个没有依赖性的，易于操作的单二进制系统。 Loki与Prometheus的不同之处在于，它侧重于日志而不是指标，并通过推送而不是拉动交付日志。

## 部署Loki和grafana到k8s集群

### 部署loki和promtail

这里我们通过helm来部署loki和promtail到集群中，这里使用的环境是腾讯云上的托管TKE集群，在腾讯云上的容器服务有直接提供loki应用让我们直接部署到集群中，下面我们说一下部署方式

#### 控制台部署loki应用到集群

点击腾讯云容器服务，点击应用，选择你的集群，点击新建。

![upload-image](/files/-MN2J9-64puMeODG_81H)

输入你的应用名和应用部署的命名空间，在日志分析中找到loki-stack，然后点击完成即可。

![upload-image](/files/-MN2J9-7YlKktWilLjsn)

这边部署成功后可以去控制台进入loki查看应用，你会发现部署loki和对应的promtail，这些pod都正常启动，说明loki和promtail都部署成功了。

![upload-image](/files/-MN2J9-8aPaaIhyMkcdh)

![upload-image](/files/-MN2J9-9iUJGl7rGJCue)

#### helm命令部署loki应用到集群

当然如果你习惯用helm命令操作部署也是可以的，下面讲一下helm命名的部署方式

```
//添加loki仓库
$ helm repo add loki https://grafana.github.io/loki/charts
$ helm repo update
//采用默认配置部署
$ helm upgrade --install loki --namespace=loki --name=nwx-loki loki/loki-stack 
//部署Loki和Fluent Bit到您的集群
$ helm upgrade --install loki --namespace=loki --name=nwx-loki \
    --set fluent-bit.enabled=true,promtail.enabled=false
```

### 部署grafana

#### 部署grafana及对应的service

下面我们部署grafana作为前端界面来查询loki所采集的日志，这里在选择grafana版本需要注意，版本需要6.0以上的版本。

```
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  labels:
    k8s-app: gafana
    qcloud-app: gafana
  name: gafana
  namespace: loki
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: gafana
      qcloud-app: gafana
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        k8s-app: gafana
        qcloud-app: gafana
    spec:
      containers:
      - image: grafana/grafana:master
        imagePullPolicy: Always
        name: gafana
        resources:
          limits:
            cpu: 500m
            memory: 1Gi
          requests:
            cpu: 250m
            memory: 256Mi
        securityContext:
          privileged: false
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      imagePullSecrets:
      - name: qcloudregistrykey
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
```

```
apiVersion: v1
kind: Service
metadata:
  name: gafana
  namespace: loki
spec:
  externalTrafficPolicy: Cluster
  ports:
  - name: 3000-3000-tcp
    nodePort: 32074
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    k8s-app: gafana
    qcloud-app: gafana
  sessionAffinity: None
  type: NodePort
```

直接kubectl apply上面2个yaml文件创建grafana应用和对应的service，也可以控制台创建

![upload-image](/files/-MN2J9-DNbCy5e71Mcbh)

![upload-image](/files/-MN2J9-E58ADyBuMCmn0)

#### 通过treafik给grafana配置域名访问

这里集群部署的treafik是2.0版本，关于treafik2.0部署可以参考 <https://cloud.tencent.com/developer/article/1642306>

```
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: loki
  namespace: loki
spec:
  entryPoints:
    - web
  routes:
  - match: Host(`loki.tx.niewx.cn`)
    kind: Rule
    services:
    - name: gafana
      port: 3000
```

直接添加一个IngressRoute就可以了，下面我们通过配置的域名看看是否可以成功。

![upload-image](/files/-MN2J9-HsEkWk4UmbBuy)

可以进入登陆界面表示部署成功。

## 通过grafana界面查询日志

### grafana配置loki数据源

下面我们需要在grafana配置一下loki的数据源，这样我们才可以在grafana上查询loki的日志，首先我们登录grafana，初始化账号密码是admin/admin，第一次登录会要求你修改密码，登录成功后有一个grafana介绍。

![upload-image](/files/-MN2J9-If-7DFFUf-jQR)

找到配置选项的Data Sources，点击添加数据源，选择loki，在loki数据源的url配置上你的service名称地址，我的service名称是nwx-loki，因此我们这里配置的是这个地址 [http://nwx-loki:3100，配置好后点击测试，如果能连接成功说明添加成功。](http://nwx-loki/:3100，配置好后点击测试，如果能连接成功说明添加成功。)

![upload-image](/files/-MN2J9-JE9b4pDMR-3UY)

![upload-image](/files/-MN2J9-KZWMbseKlW_a3)

![upload-image](/files/-MN2J9-LXoczgE8ILGQv)

![upload-image](/files/-MN2J9-MLzCg4UBWkSLq)

### 界面上查询日志

添加数据源后，点击explore可以直接进到loki的查询界面

![upload-image](/files/-MN2J9-NO9bkvvaD-DZo)

![upload-image](/files/-MN2J9-Ou4-HkMJHL7at)

我们这边查询一下loki的日志看看，点击log label选择app中的loki就可以看到loki的业务日志

![upload-image](/files/-MN2J9-PyZ3TYxOqpY-Z)

![upload-image](/files/-MN2J9-Q6ArprBONPHh2)

### loki查询语法

下面我们简单的测试一下loki的查询语法

#### job日志流

```
{job="efk/elasticsearch-master"}
```

![upload-image](/files/-MN2J9-RqENJUIeQo4S_)

#### 合并流选择器

```
{app ="elasticsearch-master",namespace ="efk"}
```

![upload-image](/files/-MN2J9-SmbvUyIhWJZ1_)

#### 标签匹配的运算符

* \=： 完全相等。
* !=： 不相等。
* \=\~： 正则表达式匹配。
* !\~： 正则表达式不匹配。

  ```
  {app != "kibana",namespace ="efk"}
  ```

![upload-image](/files/-MN2J9-TZHrYZlvs3X20)

#### 行筛选器表达式

* |=：日志行包含字符串。
* !=：日志行不包含字符串。
* |\~：日志行与正则表达式匹配。
* !\~：日志行与正则表达式不匹配。

```
{app = "kibana",namespace ="efk"} |= "172.16.2.108"
```

![upload-image](/files/-MN2J9-ULb3l_0X-05zd)

筛选器运算符可以多个并将按顺序筛选表达式 - 生成的日志行必须同时满足所有条件。

```
{app = "kibana",namespace ="efk"} |= "172.16.2.108" != "04:58:40"
```

![upload-image](/files/-MN2J9-VVxOKDrpqUjPi)

#### 统计某一时间段的日志数

```
count_over_time({app = "kibana",namespace ="efk" }[1m])
```

![upload-image](/files/-MN2J9-W3gm4mOtXtmIM)

获取过去1个小时的日志计数，按级别分组

```
sum(count_over_time({job="default/nginx"}[60m])) by (level)
```

![upload-image](/files/-MN2J9-XNzLwhdDEjKR7)

loki配备了自己的查询LogQL，详细的查询语法参考<https://github.com/grafana/loki/blob/master/docs/sources/logql/_index.md>

有两种类型的 LogQL 查询：

* 日志查询返回日志行的内容。
* 指标查询扩展日志查询，并基于日志查询中的日志内容计算示例值。

logQL基本上能满足我们的日常查询需求。大家可以多多学习实际操作下。

## 参考链接

<https://github.com/grafana/loki>

<https://github.com/grafana/loki/blob/master/docs/sources/logql/_index.md>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.niewx.cn/20201105-ri-zhi-ju-he-gong-ju-loki.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
