[TOC]

0x00 快速入门

1.什么是 Loki ?

描述:在对公司容器的日志存储查询方案进行设计的时候,发现主流的 ELK (Elasticsearch, Logstash, Kibana) 或者 EFK (Elasticsearch, Filebeat or Fluentd, Kibana) 比较重,再加上现阶段对于 ES 复杂的搜索功能很多都用不上,最终选择了 Grafana 开源的 Loki 日志系统即 PLG 日志解决方案。

本篇文章将主要介绍使用 Loki 实现 Kubernetes 容器日志监控的相关知识,感兴趣的朋友一起看看吧!

本章完整原文首发地址: 如何使用Grafana+Loki+Promtail日志聚合系统针对Kubernetes集群中Pods应用日志采集搜索展示-https://mp.weixin.qq.com/s/zRg9ePFbVKyLxgy-K2TJHg


什么是 Grafana Loki?

官方介绍: Like Prometheus, but for logs.
Grafana Loki 是一个日志聚合系统,旨在存储和查询来自所有应用程序和基础架构的日志。
Grafana Loki 项目由 Grafana Labs 团队开发,并在 2018 年的 KubeCon 上发布 (在 AGPLv3 许可下发布), 其基于 Go 语言实现,它是一个受 Prometheus 启发的水平可扩展,高可用性,多租户的日志聚合系统, 其相对比于ELK更加轻量级,它的设计非常具有成本效益且易于操作。


那 Loki 与 Promethus 区别在什么地方呢?

答:Loki 与 Prometheus 的不同之处在于专注于日志而不是指标,并且通过推送而不是拉取来传递日志。

在生产实践环境中 Loki 常常与 Grafana 联合使用, 提供了一种界面化对 Loki 中存储的日志进行查询并可视化查询结果的方法,所以 Grafana Labs提供的另一个日志解决方案 PLG 目前也逐渐变得流行起来。

PLG 架构为 Promtail + Loki + Grafana 的组合,整体架构图下所示:

WeiyiGeek.PLG

WeiyiGeek.PLG

其中所有组件说明:

  • Grafana 大家应该都比较熟悉,它是一款开源的可视化和分析软件,它允许用户查询、可视化、警告和探索监控指标.
  • Loki 是一组可以组成一个功能齐全的日志堆栈组件, 负责存储日志和处理查询。
  • Promtail 是一个日志收集的代理, 其类似于 ELk 中的 filebeat 的 日志采集端 agent,负责收集日志并将其发送给 Loki。


2.为什么要使用 Grafana Loki?

描述:Loki 采用了一种独特的方法,它只索引元数据而不是日志行的全文,并且为每个日志流设置一组标签,Loki 的最小索引方法意味着在 Loki 中存储同一组日志所需的存储空间比其他解决方案少得多,例如下图

WeiyiGeek.Loki-元数据

WeiyiGeek.Loki-元数据

温馨提示: 从图纸中可以看到Loki针对时间戳和标签键值对进行了索引, 这极大的方便我们进行日志查询。

Loki 优点介绍

  • 容易上手, 因为您可以使用各种客户端从任何来源以任何格式发送日志。
  • 对象存储持久化, 意味着您可以获得 PB 级规模、高吞吐量和经济高效且耐用的存储。
  • 告警通知, 可以通过采用的日志对比预定的告警阈值进行相应告警通知。
  • 灵活的日志格式, 为您提供更大的灵活性和在查询时进行格式设置的选项。
  • 实时跟踪应用日志, 以查看进入系统的日志、每隔特定时间更新日志、查看特定日期的日志等。
  • 云原生环境集成, 可以非常方便的与 Prometheus、Grafana 和 K8s 原生集成.


与其他日志聚合系统相比

  • 不像ELK对日志进行全文索引, 通过存储压缩的非结构化日志和仅索引元数据Loki 操作更简单,运行成本更低、效率也提升了。
  • 使用已在 Prometheus 中使用的相同标签对日志流进行索引和分组,使您能够使用已在 Prometheus 中使用的相同标签在指标和日志之间无缝切换。
  • 特别适合存储 Kubernetes Pod 日志, Pod 标签等元数据会被自动抓取和索引。
  • 在 Grafana 中有原生支持(需要 Grafana v6.0)。


在 Loki 架构中类比与 EFK 几个概念:

  • Grafana:相当于 EFK 中的 Kibana ,用于 UI 的展示。
  • Loki:相当于 EFK 中的 ElasticSearch ,用于存储日志和处理查询。
  • Promtail:相当于 EFK 中的 Filebeat/Fluentd ,用于采集日志并将其发送给 Loki 。
  • LogQL:Loki 提供的日志查询语言,类似 Prometheus 的 PromQL,而且 Loki 支持 LogQL 查询直接转换为 Prometheus 指标。


3.Grafana Loki 是如何工作的?

描述: 我们可以官方图示中看出 Grafana Loki 的工作流程。

WeiyiGeek.Grafana Loki工作流程

WeiyiGeek.Grafana Loki工作流程

步骤 01.Promtail 是专门为 Loki 构建的日志收集器,我们使用 Promtail 拉入任何日志,它使用与 Prometheus 相同的服务发现功能,并包括在将日志摄取到 Loki 之前标记、转换和过滤日志的类似功能。

步骤 02.promtail 将采集到的日志存储在 Loki 存储空间中,Loki 不索引日志文本,只将时间戳与标签进行索引,这不仅可以降低成本,还意味着可以在 Loki 接收到的几毫秒内查询日志行。

步骤 03.使用 Grfana 接入 Loki 数据源, 然后直接在 Grafana 中运行 LogQL 查询,以将您的日志与其他数据源一起可视化,或者使用 LogCLI 来显示查询到可视化数据。

步骤 04.最后我们还可以为 Loki 设置警报规则以评估传入的日志数据,配置 Loki 以将生成的警报发送到 Prometheus Alertmanager 中,从而进行告警。


4.Grafana Loki 架构简述

描述: Loki 整体架构也是由不同的组件来协同完成日志收集、索引、存储等工作的各个组件如下图所示。

WeiyiGeek.Loki Architecture

WeiyiGeek.Loki Architecture

架构图剖析:
Loki 采用读写分离架构,关键组件有:

  • Distributor 分发器(日志分发):日志数据传输的“第一站”,Distributor 分发器接收到日志数据后,根据元数据和 hash 算法,将日志分批并行地发送到多个 Ingester 接收器上

  • Ingester 接收器(日志持久化):接收器是一个有状态的组件,在日志进入时对其进行 gzip 压缩操作,并负责构建和刷新 chunck 块,当 chunk 块达到一定的数量或者时间后,就会刷新 chunk 块和对应的 Index 索引存储到数据库中

  • Querier 查询器 (查询解析):给定一个时间范围和标签选择器,Querier 查询器可以从数据库中查看 Index 索引以确定哪些 chunck 块匹配,并通过 greps 将结果显示出来,它还会直接从 Ingester 接收器获取尚未刷新的最新数据

  • Query Frontend 查询前端(查询API):查询前端是一个可选的组件,运行在 Querier 查询器之前,起到缓存,均衡调度的功能,用于加速日志查询

  • Ruler 预警规则: Ruler 规则规定了何种情况向 altermanger 发送,其语法规则近似于 Prometheus Alertmanager 中告警规则。

  • Object Store:存储采集到的日志index索引与Chunks。

  • Memcaches: 缓存写入、查询结果,加快查询效率。

  • Cluster Services: 包含 compactor 与 consul 服务发现组件。

温馨提示: 我们可以通过 loki 二进制的 -target 参数指定运行角色(querier/inester/query-frontend/distributor).


5.Grafana Loki 参考来源

官方地址:https://grafana.com/oss/loki/

项目地址: https://github.com/grafana/loki

开始: https://grafana.com/docs/loki/latest/getting-started/

安装参考: https://grafana.com/docs/loki/latest/installation/


0x01 安装部署

描述: 我们可以使用多种方式进行安装 Loki 和 Promtail 组件,其中最常用的是使用 Helm 部署微服务、使用 Helm 进行简单的可扩展部署、通过 Docker 或 Docker Compose 安装、在本地安装和运行或者从源代码安装。

常规使用
Step 1.下载并安装 Loki 和 Promtail。
Step 2.下载两个程序的配置文件。
Step 3.启动 Loki。
Step 4.更新 Promtail 配置文件,让您的日志进入 Loki。
Step 5.开始在Grafana中浏览日志。


Loki 三种部署方式

单体模式 (Monolithic mode),ALL IN ONE : Loki 支持单一进程模式,可在一个进程中运行所有必需的组件。单进程模式非常适合测试 Loki 或以小规模运行。不过尽管每个组件都以相同的进程运行,但它们仍将通过本地网络相互连接进行组件之间的通信(grpc)。使用 Helm 部署就是采用的该模式。

简单可扩展模式 (Simple scalable deployment mode): 如果您每天的日志量超过几百 GB,或者如果您想分离读写关注点, Loki 提供了简单的可扩展部署模式, 该模式下每天存储的日志量可到TB级别,其分离读写路径有以下优点通过提供专用节点提高写入路径的可用性,可单独扩展的读取路径以按需添加/删除查询性能。

微服务模式 (Microservices mode):为了实现水平可伸缩性,Loki 支持组件拆分为单独的组件分开部署,从而使它们彼此独立地扩展。每个组件都产生一个用于内部请求的 gRPC 服务器和一个用于外部 API 请求的 HTTP 服务,所有组件都带有 HTTP 服务器,但是大多数只暴露就绪接口、运行状况和指标端点。

1
2
3
4
5
6
7
8
9
# 包含的组件
distributor
ingester
querier
query-scheduler
query-frontend
index-gateway
ruler
compactor

三种部署模式参考: https://grafana.com/docs/loki/latest/fundamentals/architecture/deployment-modes/


以 二进制方式 安装 Loki 与 Promtail

描述: 在进行容器化部署时,建议初学者先使用单节点主机模式部署各个组件,熟悉一下整个流程,然后在进行容器化部署。

实践环境

1
2
3
4
~$ cat /etc/issue.net
Ubuntu 20.04.4 LTS
~$ uname -a
Linux devtest-102 5.4.0-59-generic #65-Ubuntu SMP Thu Dec 10 12:01:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

实践步骤

  • Step 1.下载 Loki 与 Promtail 最新而二进制文件并准备安装配置文件, 当前最新版本release查看(https://github.com/grafana/loki/releases/)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    mkdir -vp /usr/local/loki/{config,data}; mkdir -vp /usr/local/loki/data/{loki,promtail}

    cd /usr/local/loki

    # 下载二进制包
    LOKI_VERSION=v2.6.0
    curl -O -L https://github.com/grafana/loki/releases/download/${LOKI_VERSION}/loki-linux-amd64.zip
    curl -O -L https://github.com/grafana/loki/releases/download/${LOKI_VERSION}/promtail-linux-amd64.zip
    curl -O -L https://github.com/grafana/loki/releases/download/${LOKI_VERSION}/logcli-linux-amd64.zip

    $ ls
    logcli-linux-amd64.zip loki-linux-amd64.zip promtail-linux-amd64.zip

    # 解压与可执行权限赋予
    unzip "loki-linux-amd64.zip"
    unzip promtail-linux-amd64.zip
    unzip logcli-linux-amd64.zip

    chmod a+x logcli-linux-amd64 loki-linux-amd64 promtail-linux-amd64

    # 软连接创建
    ln -s $(pwd)/loki-linux-amd64 /usr/local/bin/loki
    ln -s $(pwd)/promtail-linux-amd64 /usr/local/bin/promtail
    ln -s $(pwd)/logcli-linux-amd64 /usr/local/bin/logcli


  • Step 2.使用 systemd 管理 loki 与 Promtail 准备的服务器清单如下。
    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
    cat <<EOF > /usr/lib/systemd/system/loki.service
    [Unit]
    Description=loki.service
    Wants=network-online.target
    After=network-online.target

    [Service]
    Type=simple
    ExecStart=/usr/local/loki/loki-linux-amd64 -log.level=info -target all -config.file=/usr/local/loki/config/loki-config.yaml
    WorkingDirectory=/usr/local/loki/
    LimitMEMLOCK=infinity
    LimitNPROC=65536
    LimitNOFILE=65536

    [Install]
    WantedBy=multi-user.target
    EOF

    cat <<EOF > /usr/lib/systemd/system/promtail.service
    [Unit]
    Description=promtail.service
    Wants=network-online.target
    After=network-online.target

    [Service]
    Type=simple
    ExecStart=/usr/local/loki/promtail-linux-amd64 -config.file=/usr/local/loki/config/promtail.conf
    WorkingDirectory=/usr/local/loki/
    LimitMEMLOCK=infinity
    LimitNPROC=65536
    LimitNOFILE=65536

    [Install]
    WantedBy=multi-user.target
    EOF

扩展补充: 我们也可使用 supervisor 管理进程, Supervisor 是Python编写的 Client/Server 模式的系统,通过supervisor,可以对类Unix操作系统的进程进行监控和管理,可以方便地进行进程集中管理,并监控进程的状态, 当程序异常退出时,可以自动拉起程序,起到守护进程的作用。

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
# CentOS
sudo yum install epel-release -y
sudo yum install supervisor -y

# 修改内存、进程、文件限制
sed -i '/forking/a LimitNOFILE=65536' /usr/lib/systemd/system/supervisord.service;
sed -i '/forking/a LimitNPROC=65536' /usr/lib/systemd/system/supervisord.service;
sed -i '/forking/a LimitMEMLOCK=infinity' /usr/lib/systemd/system/supervisord.service;

# 设置开机启动,启动服务
systemctl daemon-reload
systemctl enable supervisord
systemctl start supervisord

# 配置 supervisor 管理 loki进程
mkdir -vp /usr/local/loki/{loki,promtail,config} /var/log/loki/
cat <<EOF> /etc/supervisord.d/loki.ini
[program:loki]
command=/usr/local/loki/loki-linux-amd64 -log.level=info -target all -config.file=/usr/local/loki/config/loki-config.yaml
autorestart=true
autostart=true
stderr_logfile=/var/log/loki/loki_err.log
stdout_logfile=/var/log/loki/loki_out.log
user=root
stopsignal=INT
startsecs=10
startretries=3
directory=/usr/local/loki/
EOF


cat << EOF > /etc/supervisord.d/promtail.ini
[program:promtail]
command=/usr/local/loki/promtail-linux-amd64 -config.expand-env=true -config.file=/usr/local/loki/config/promtail.conf
autorestart=true
autostart=true
stderr_logfile=/var/log/loki/promtail_err.log
stdout_logfile=/var/log/loki/promtail_out.log
user=root
stopsignal=INT
startsecs=10
startretries=3
directory=/usr/local/loki/
EOF


  • Step 3.Loki 配置文件(loki-config.yaml) 与 promtail 配置文件(promtail.conf) 分别如下:
    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
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    tee /usr/local/loki/config/loki-config.yaml <<'EOF'
    auth_enabled: false
    server:
    http_listen_port: 3100
    grpc_listen_port: 9096

    # 日志接收器,配置ingester的生命周期将如何运行以及在何处注册以进行发现
    ingester:
    wal:
    enabled: true
    dir: /usr/local/loki/data/loki/wal
    lifecycler:
    address: 127.0.0.1
    ring:
    kvstore:
    store: inmemory
    replication_factor: 1
    final_sleep: 0s
    chunk_idle_period: 1h # 此时未接收到新日志的任何区块都将被刷新
    max_chunk_age: 1h # 所有区块在达到该年龄时都将被刷新,默认值为1h
    chunk_target_size: 10485760 # Loki将尝试构建高达1.5MB的块,如果首先达到 chunk_idle_period or max_chunk_age 值则刷新。
    chunk_retain_period: 30s # 如果使用索引缓存,则必须大于索引读缓存TTL(默认索引读缓存TTL为5m)
    max_transfer_retries: 0 # 禁用区块传输

    # 块索引架构配置
    schema_config:
    configs:
    - from: 2022-07-15
    store: boltdb-shipper
    object_store: filesystem
    schema: v11
    index:
    prefix: index_
    period: 24h

    # 存储配置
    storage_config:
    boltdb_shipper:
    active_index_directory: /usr/local/loki/data/loki/boltdb-shipper-active
    cache_location: /usr/local/loki/data/loki/boltdb-shipper-cache
    cache_ttl: 24h # 缓存周期与存储空间成正比关系。
    shared_store: filesystem
    filesystem:
    directory: /usr/local/loki/data/loki/chunks

    # 压缩配置
    compactor:
    working_directory: /usr/local/loki/data/loki/boltdb-shipper-compactor
    shared_store: filesystem

    # 限制配置
    limits_config:
    enforce_metric_name: false
    reject_old_samples: true
    reject_old_samples_max_age: 168h
    # 每秒允许promtail传输200MB,默认为4
    ingestion_rate_mb: 200
    # ingestion_burst_size_mb: 400
    # max_streams_per_user: 0
    # max_chunks_per_query: 20000000
    # max_query_parallelism: 140
    # max_query_series: 5000
    # cardinality_limit: 1000000
    # max_streams_matchers_per_query: 10000

    chunk_store_config:
    max_look_back_period: 0s

    # 数据保留时间, 等保要求应用日志需存放180天以上,此处保存为26周。
    table_manager:
    retention_deletes_enabled: true
    retention_period: 4368h

    # prometheus 告警配置
    ruler:
    storage:
    type: local
    local:
    directory: /usr/local/loki/data/loki/rules
    rule_path: /usr/local/loki/data/loki/rules-temp
    alertmanager_url: http://10.0.30.200:9093
    ring:
    kvstore:
    store: inmemory
    enable_api: true
    EOF

    tee /usr/local/loki/config/promtail.conf <<'EOF'
    server:
    log_level: error
    http_listen_address: 0.0.0.0
    http_listen_port: 9080
    grpc_listen_port: 0
    # gRPC服务侦听端口(0表示随机端口)
    # 记录日志位置
    positions:
    filename: /usr/local/loki/data/promtail/positions.yaml
    # Loki API 地址服务
    client:
    url: http://localhost:3100/loki/api/v1/push
    # 抓取配置
    scrape_configs:
    - job_name: system
    pipeline_stages:
    static_configs:
    - targets:
    - localhost
    labels:
    job: systemd-logs
    host: 10.20.176.102
    __path__: /var/log/*.log
    - job_name: messages
    static_configs:
    - targets:
    - localhost
    labels:
    job: messages-logs
    host: 10.20.176.102
    __path__: /var/log/messages
    - job_name: dmesg
    static_configs:
    - targets:
    - localhost
    labels:
    job: dmesg-logs
    host: 10.20.176.102
    __path__: /var/log/dmesg
    - job_name: journal
    journal:
    max_age: 12h
    # 从进程开始读取并发送最近12小时的数据至Loki
    labels:
    job: systemd-journal
    relabel_configs:
    - source_labels: ['__journal__systemd_unit']
    target_label: 'unit'
    EOF


    # 配置 Loki 的报警规则
    # “fake”是Loki以单租户模式运行时默认的租户Id, 所以安装上述配置需要在/usr/local/loki/data/loki/rules/目录下创建一个fake目录并将 rules.yaml 规则文件放在该目录下。
    mkdir -vp /usr/local/loki/data/loki/rules/fake
    tee /usr/local/loki/data/loki/rules/fake/rules.yaml <<'EOF'
    groups:
    - name: service OutOfMemoryError
    rules:
    # 关键字监控报警
    - alert: loki check words java.lang.OutOfMemoryError
    expr: sum by (env, hostname, log_type, filename) (count_over_time({env=~"\\w+"} |= "java.lang.OutOfMemoryError" [5m]) > 0)
    labels:
    severity: critical
    annotations:
    description: '{{$labels.env}} {{$labels.hostname}} file {{$labels.filename}} has {{ $value }} error'
    summary: java.lang.OutOfMemoryError
    # java 程序日志性能报警
    - alert: loki java full gc count check
    expr: sum by (env, hostname, log_type, filename) (count_over_time({env=~"\\w+"} |= "Full GC (Allocation" [5m]) > 5)
    labels:
    severity: warning
    annotations:
    description: '{{$labels.env}} {{$labels.hostname}} {{$labels.filename}} {{ $value }}'
    summary: java full gc count check
    # 使用正则表达式报警
    - alert: dbperform slowlog sql 慢查询
    expr: 'sum by (env, hostname, log_type, filename) (count_over_time({env=~"\\w+"} |~ "time: [1-9]\\d{4,}" [5m]) > 5)'
    labels:
    severity: warning
    annotations:
    description: '{{$labels.env}} {{$labels.hostname}} file {{$labels.filename}} has {{ $value }} error'
    summary: sql slowlog
    EOF

温馨提示: 关于labels,官网建议labels个数越少越好,过多的labels将会影响Loki日志的检索速度。


  • Step 4.设置Loki与Promtail服务开机启动并启动服务。
    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
    systemctl daemon-reload 
    systemctl enable loki.service;systemctl enable promtail.service
    systemctl restart loki.service promtail.service

    $ systemctl status loki.service
    ● loki.service
    Loaded: loaded (/lib/systemd/system/loki.service; enabled; vendor preset: enabled)
    Active: active (running) since Sat 2022-07-16 04:39:52 UTC; 26s ago
    Main PID: 174310 (loki-linux-amd6)
    Tasks: 11 (limit: 19112)
    Memory: 16.8M
    CGroup: /system.slice/loki.service
    └─174310 /usr/local/loki/loki-linux-amd64 -log.level=info -target all -config.file=/usr/local/loki/config/loki-config.yaml

    $ systemctl status promtail.service
    ● promtail.service
    Loaded: loaded (/lib/systemd/system/promtail.service; enabled; vendor preset: enabled)
    Active: active (running) since Sat 2022-07-16 04:41:50 UTC; 47s ago
    Main PID: 174401 (promtail-linux-)
    Tasks: 15 (limit: 19112)
    Memory: 70.7M
    CGroup: /system.slice/promtail.service
    └─174401 /usr/local/loki/promtail-linux-amd64 -config.file=/usr/local/loki/config/promtail.conf

    Jul 16 04:41:50 devtest-102 systemd[1]: Started promtail.service.

分别访问Loki与Promtail监听服务部署结果验证如下命令与下图:

1
2
$ curl http://10.20.176.102:3100/loki/api/v1/labels
{"status":"success","data":["filename","host","job","unit"]}

WeiyiGeek.loki-API与Promtail

WeiyiGeek.loki-API与Promtail

温馨提示: 在启动Loki后当其日志中出现component=ruler关键字集群rules时以及在/usr/local/loki/data/loki/rules-temp/fake/自动生成rules.yaml表示告警规则已经加载成功。

1
2
$ journalctl -u loki.service | grep "component=ruler"
Jul 17 08:35:12 devtest-102 loki-linux-amd64[178834]: level=info ts=2022-07-17T08:35:12.851099815Z caller=metrics.go:133 component=ruler org_id=fake latency=fast query="sum by(env,hostname,log_type,filename)((count_over_time({env=~\"\\\\w+\"} |= \"java.lang.OutOfMemoryError\"[5m]) > 0))" query_type=metric range_type=instant length=0s step=0s duration=3.590441ms status=200 limit=0 returned_lines=0 throughput=0B total_bytes=0B total_entries=0 queue_time=0s subqueries=1



  • Step 6.使用 Grafana 显示 Loki 与 Promtail 采集的日志, 添加 Loki 数据源。
WeiyiGeek.Loki 数据源

WeiyiGeek.Loki 数据源


  • Step 7.使用 Explore 显示 Loki 采集的数据,例如 查询的表达式为{filename="/var/log/auth.log"}, 当然我们也可使用其它标签进行查看对应服务采集到的日志。
WeiyiGeek.Loki 采集的数据展示

WeiyiGeek.Loki 采集的数据展示


  • Step 8.除了使用Gafana查询Loki之外,它还提供了linux终端的查询客户端logcli, 下面简单实践一下上面logcli客户端命令的使用。
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
# 1.查看指定Loki采集的标签
$ logcli labels --addr="http://localhost:3100" # 或者 logcli labels -q
http://localhost:3100/loki/api/v1/labels?end=1657983404379212368&start=1657979804379212368
filename
host
job
unit

# 2.查看指定标签详情
$ logcli labels filename --addr="http://localhost:3100"
http://localhost:3100/loki/api/v1/label/filename/values?end=1657983492060973446&start=1657979892060973446
/var/log/auth.log
/var/log/bootstrap.log
/var/log/cloud-init-output.log
/var/log/cloud-init.log
/var/log/dmesg
/var/log/dpkg.log
/var/log/fail2ban.log
/var/log/ubuntu-advantage-timer.log
/var/log/vmware-network.1.log
/var/log/vmware-network.log
/var/log/vmware-vmsvc-root.1.log
/var/log/vmware-vmsvc-root.2.log
/var/log/vmware-vmsvc-root.3.log
/var/log/vmware-vmsvc-root.log
/var/log/vmware-vmtoolsd-root.log

# 3.查看指定标签下的日志,最大显示行数为100,每批次限制5条
logcli query '{host="10.20.176.102",filename="/var/log/auth.log"}' --limit 100 --batch 5


# 4.指定时间输出日志并指定中国时间输出,--from 和--to 后面的时间需要符合IETF RFC-3339[4]格式
logcli query '{host="10.20.176.102",filename="/var/log/auth.log"}' --limit=10000 --from="2022-03-12T14:00:05+08:00" --to="2022-03-18T14:30:05+08:00"

# 5.logcli query同样支持Logql语法
logcli query '{host="10.20.176.102",filename="/var/log/auth.log"}|="ssh"' --limit=10000 --since=2h --timezone=UTC --output jsonl
# http://localhost:3100/loki/api/v1/query_range?direction=BACKWARD&end=1658022708817779154&limit=1000&query=%7Bhost%3D%2210.20.176.102%22%2Cfilename%3D%22%2Fvar%2Flog%2Fauth.log%22%7D%7C%3D%22ssh%22&start=1658015508817779154
# Common labels: {filename="/var/log/auth.log", host="10.20.176.102", job="systemd-logs"}
# {"labels":{},"line":"Jul 17 01:40:41 devtest-102 sshd[176420]: pam_unix(sshd:session): session opened for user weiyigeek by (uid=0)","timestamp":"2022-07-17T01:40:41.611039896Z"}
# {"labels":{},"line":"Jul 17 01:40:41 devtest-102 sshd[176420]: Accepted publickey for weiyigeek from 10.20.176.101 port 18792 ssh2: ED25519 SHA256:9ayZkGn8V1koh8rm8MsYIh16hOTtpg+cZJdqSdJsTfM","timestamp":"2022-07-17T01:40:41.611004156Z"}

# 6.获取所有标签的摘要信息, 此处获取filename为/var/log/auth.log的日志流信息
logcli series '{}' -q --analyze-labels
# Total Streams: 9
# Unique Labels: 4
# Label Name Unique Values Found In Streams
# unit 8 8
# job 2 9
# host 1 1
# filename 1 1
logcli series '{filename="/var/log/auth.log"}' -q # 显示相关联的标签
# {filename="/var/log/auth.log", host="10.20.176.102", job="systemd-logs"}
WeiyiGeek.logcli简单使用

WeiyiGeek.logcli简单使用


以 Helm3 在K8S集群里安装 Loki 与 Promtail

环境说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
~$ kubectl get node
NAME STATUS ROLES AGE VERSION
devtest-master-212 Ready control-plane,master 32d v1.23.7
devtest-master-213 Ready control-plane,master 32d v1.23.7
devtest-master-214 Ready control-plane,master 32d v1.23.7
devtest-work-215 Ready work 32d v1.23.7

~$ helm3 version
version.BuildInfo{Version:"v3.9.0", GitCommit:"7ceeda6c585217a19a1131663d8cd1f7d641b2a7", GitTreeState:"clean", GoVersion:"go1.17.5"}

~$ helm3 search repo grafana | egrep "loki|promtail"
grafana/loki 2.13.1 v2.6.0 Loki: like Prometheus, but for logs.
......
grafana/promtail 6.2.1 2.6.0 Promtail is an agent which ships the contents o...

安装部署

描述: 以 Helm 部署 Loki (StatefulSet 方式) 和 Promtail(DaemonSet 方式)采集 k8s pod 应用的日志为例

1
2
3
4
5
6
7
8
9
10
# 1.Add Loki’s chart repository to Helm:
helm repo add grafana https://grafana.github.io/helm-charts

# 2.Update the chart repository and list Loki or Promtail
helm repo update && helm search repo grafana/loki
helm search repo -l grafana/promtail

# 3.Download Loki’s and Promtail chart in local (拉取图表到本地并解压)
helm pull grafana/loki --untar --version 2.13.1 # 2.13.1
helm pull grafana/promtail --untar --version 6.2.1 # 6.2.1

loki values.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
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
# 4.Loki 图表配置文件
/storage/dev/webapp/logging/loki# ls
Chart.yaml README.md templates values.yaml

# values.yaml (主要部分)
$ grep -E -v "^$|#" values.yaml
image:
repository: grafana/loki
tag: 2.6.0
pullPolicy: IfNotPresent
......
tracing:
jaegerAgentHost:
config:
auth_enabled: false
memberlist:
join_members:
- '{{ include "loki.fullname" . }}-memberlist'
ingester:
chunk_idle_period: 3m
chunk_block_size: 262144
chunk_retain_period: 1m
max_transfer_retries: 0
wal:
dir: /usr/local/loki/data/loki/wal
lifecycler:
ring:
replication_factor: 1
limits_config:
enforce_metric_name: false
reject_old_samples: true # 关键点: 是否拒绝老样本
reject_old_samples_max_age: 168h # 关键点: 168小时之前的样本将会被删除
max_entries_limit_per_query: 5000
schema_config:
configs:
- from: 2022-07-15 # 关键点: 索引架构配置
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
server:
http_listen_port: 3100
grpc_listen_port: 9095
storage_config:
boltdb_shipper:
active_index_directory: /usr/local/loki/data/loki/boltdb-shipper-active # 关键点: 索引存储目录
cache_location: /usr/local/loki/data/loki/boltdb-shipper-cache
shared_store: filesystem
filesystem:
directory: /usr/local/loki/data/loki/chunks # 关键点: 块存储路径
chunk_store_config:
max_look_back_period: 0s
table_manager:
retention_deletes_enabled: true # 关键点: 启用循环删除
retention_period: 4368h # 关键点: 日志保留期限为180天以上且必须是168的倍数约定于26周*168
compactor:
working_directory: /usr/local/loki/data/loki/boltdb-shipper-compactor
shared_store: filesystem
....
persistence:
enabled: true # 关键点: 启用持久化卷
accessModes:
- ReadWriteOnce
size: 10Gi
labels: {}
annotations: {}
storageClassName: nfs-dev
podLabels: # 关键点: pod 标签
app: "loki"
rbac: # 关键点: rbac 权限创建
create: true
pspEnabled: true
resources: # 关键点: 资源限制
limits:
cpu: 1
memory: 2Gi
requests:
cpu: 100m
memory: 128Mi
serviceMonitor:
enabled: true # 关键点: 启用服务监控即 Promethus 抓取指标
interval: 10s
additionalLabels: {}
annotations: {}
scrapeTimeout: 10s
path: /metrics
prometheusRule:
enabled: false
additionalLabels: {}
rules: []

温馨提示: Loki 更多配置项解析参考地址 https://grafana.com/docs/loki/latest/configuration。

温馨提示: Storage中bolt-shipper与bolt的区别, Loki2.0版本之后,对于使用boltdb存储索引部分做了较大的重构,采用新的boltdb-shipper模式,可以让Loki的索引存储在S3上,而彻底摆脱Cassandra或者谷歌的BigTable。此后服务的横向扩展将变得更加容易。详见:https://grafana.com/docs/loki/latest/operations/storage/boltdb-shipper/


promtail values.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
32
33
34
35
36
37
38
39
40
41
# 5.promtail 图表配置文件。
/storage/dev/webapp/logging/promtail# ls
Chart.yaml ci README.md README.md.gotmpl templates values.yaml

# values.yaml
daemonset: # 关键点: 采用 daemonset 资源清单部署 promtail。
enabled: true
.....
defaultVolumes:
- name: run
hostPath:
path: /run/promtail
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: pods
hostPath:
path: /var/log/pods
defaultVolumeMounts:
- name: run
mountPath: /run/promtail
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
- name: pods
mountPath: /var/log/pods
readOnly: true
extraVolumes:
- name: host-time # 关键点: promtail 采集时间时区设置
hostPath:
path: /etc/localtime
extraVolumeMounts:
- name: host-time
mountPath: /etc/localtime
......
config:
logLevel: error # 关键点: 日志等级我调整为了error。
serverPort: 3101
clients: # 关键点: loki 服务 api 地址 , 此处采用 svc 服务名称。
- url: http://loki:3100/loki/api/v1/push
.....


开始在K8S中部署 Loki 与 Promtail

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
# 6.create namespace 
kubectl create ns logging

# 7.安装 loki 指定前面pull下载到本地的图表目录 loki/
helm install --namespace logging loki loki/

# 8.安装 promtail 指定前面pull下载到本地的图表目录 promtail/
helm install --namespace logging promtail loki/

# 9.查看 helm 的部署情况
helm list -n logging
# NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
# loki logging 1 2022-07-15 10:23:45.694349558 +0800 CST deployed loki-2.13.1 v2.6.0
# promtail logging 1 2022-07-15 10:33:30.229611294 +0800 CST deployed promtail-6.2.1 2.6.0

# 查看资源清单部署状态
$ kubectl get sts,daemonsets.apps,pod,svc -n logging
NAME READY AGE
statefulset.apps/loki 1/1 4h55m

NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/promtail 4 4 4 4 4 <none> 4h45m

NAME READY STATUS RESTARTS AGE
pod/loki-0 1/1 Running 0 4h55m
pod/promtail-75xzp 1/1 Running 0 4h45m
pod/promtail-hjttw 1/1 Running 0 4h45m
pod/promtail-z9tcn 1/1 Running 0 4h45m
pod/promtail-zr4hq 1/1 Running 0 4h45m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/loki ClusterIP 10.102.118.31 <none> 3100/TCP 4h55m
service/loki-headless ClusterIP None <none> 3100/TCP 4h55m
service/loki-memberlist ClusterIP None <none> 7946/TCP 4h55m

# 10.访问 Loki api 接口验证。
root@devtest-master-212:/storage/dev/webapp/logging# curl 10.102.118.31:3100/loki/api/v1/labels
# {"status":"success","data":["app","component","container","filename","instance","job","namespace","node_name","pod","stream"]}

root@devtest-master-212:/storage/dev/webapp/logging# curl -s 10.102.118.31:3100/metrics | head -n 10
# HELP cortex_distributor_ingester_clients The current number of ingester clients.
# TYPE cortex_distributor_ingester_clients gauge
cortex_distributor_ingester_clients 2
# HELP cortex_dns_failures_total The number of DNS lookup failures
# TYPE cortex_dns_failures_total counter
cortex_dns_failures_total{name="memberlist"} 0
# HELP cortex_dns_lookups_total The number of DNS lookups resolutions attempts
# TYPE cortex_dns_lookups_total counter
cortex_dns_lookups_total{name="memberlist"} 0
# HELP cortex_frontend_query_range_duration_seconds Total time spent in seconds doing query range requests.


0x02 实践使用

使用 Grafana 连接到 Loki 数据源

此处Grafana的安装可以参考Grafana可视化快速入门[https://blog.weiyigeek.top/2019/5-17-39.html].

此处 Grafana 我也是安装在K8S集群中, 所以我们可以直接采用SVC名称服务(loki.logging.svc:3100-服务名称.名称空间.svc) 的方式接入Loki。

点击 设置⚙齿轮 -> Data Sources -> Loki -> 在 HTTP 选项下输入 http://loki.logging.svc:3100 -> 在 Maximum lines 最大显示行数为 5000 -> 最后点击 Save & test, 如出现下图提示则表示连接配置成功。

WeiyiGeek.add Loki Data Sources

WeiyiGeek.add Loki Data Sources

点击 Explore -> 选择 Loki (默认其实会选择) -> 在 Log Browser 选择查看指定标签的日志 -> 例如,此处查看 {app="loki"} 标签日志。

WeiyiGeek.Explore Loki日志查看

WeiyiGeek.Explore Loki日志查看


温馨提示: 在 Grafana 9.0 中,你将在 Explore 中看到一个全新的可视化查询生成器界面,允许任何人编写、编辑和理解一个查询的作用。你已经熟悉的 Explore 界面现在增加了切换字段,可以选择在文本编辑模式(Code)可视化生成器模式(Builder)中编写 PromQL 查询。当你选择 Builder 模式时,一个新的可视化界面允许你通过多词搜索下拉菜单选择感兴趣的指标来制作你的查询。


使用 Grafana Dashboard 显示集群中 pods 日志

描述: grafana dashboard 商城 (https://grafana.com/grafana/dashboards/?plcmt=footer&search=Loki) 中提供需要关于Loki的Dashboard数据显示面板。

其中 Lok i常用的 Dashboard 如下:

Grafana 添加 Dashboar -> 左边菜单应用 -> +Import -> 在 Import via grafana.com 下输入 15141 -> 点击 Load -> 然后选择我们添加的Loki数据源即可。

最后我们便可查看到如下界面选择查看对应Labels标签的日志:

WeiyiGeek.Loki Kubernetes Logs

WeiyiGeek.Loki Kubernetes Logs


扩展: 使用 Grafana Loki 进行日志告警
Loki 支持三种模式创建日志告警:

  • 在 Promtail 中的 pipeline 管道的 metrics 的阶段,根据需求增加一个监控指标,然后使用 Prometheus 结合 Alertmanager 完成监控报警。
  • 通过 Loki 自带的报警功能( Ruler 组件)可以持续查询一个 rules 规则,并将超过阈值的事件推送给 AlertManager 或者其他 Webhook 服务。
  • 将 LogQL 查询转换为 Prometheus 指标。可以通过 Grafana 自带的 Alert rules & notifications,定义有关 LogQL 指标的报警,推送到 Notification channels( Prometheus Alertmanager , Webhook )

以下主要介绍 LogQL 转化为 Prometheus 指标的方式实现告警。

首先,在 Grafana 添加 Prometheus 数据源,URL 只需要填入 http://loki.logging.svc:3100 即可将 LogQL 查询转换为 Prometheus 指标。

然后, 在告警规则配置中配置 Notification channels 。

之后, 新建一个 Dashboard ,配置一个面板,例如:当 nginx 出现 404 状态码,触发告警:

至此,在Kubernetes集群中查看Pod应用日志到此结束。