輕量化日誌 Loki 全攻略,再也不會整懵了

文章來源:https://c1n.cn/0wHvF

目錄

前言

在對公司容器雲的日誌方案進行設計的時候,發現主流的 ELK(Elasticsearch,Logstash,Kibana)或者 EFK(Elasticsearch,Filebeat or Fluentd,Kibana)比較重,再加上現階段對於 ES 複雜的搜索功能很多都用不上,最終選擇了 Grafana 開源的 Loki 日誌系統。

下面我們來介紹下 Loki 的一些基本概念和架構,當然 EFK 作爲業界成熟的日誌聚合解決方案也是大家應該需要熟悉和掌握的。

簡介

Loki 是 Grafana Labs 團隊最新的開源項目,是一個水平可擴展,高可用性,多租戶的日誌聚合系統。

它的設計非常經濟高效且易於操作,因爲它不會爲日誌內容編制索引,而是爲每個日誌流編制一組標籤,專門爲 Prometheus 和 Kubernetes 用戶做了相關優化。

該項目受 Prometheus 啓發,官方的介紹就是:Like Prometheus,But For Logs。類似於 Prometheus 的日誌系統。

項目地址:

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

與其他日誌聚合系統相比,Loki 具有下面的一些特性:

架構說明

| 組件說明

說明如下:

Loki 進程包含四種角色:

可以通過 Loki 二進制的 -target 參數指定運行角色。

| read path

如下:

| write path

如上圖:

部署

| 本地化模式安裝

下載 Promtail 和 Loki:

wget  https://github.com/grafana/loki/releases/download/v2.2.1/loki-linux-amd64.zip
wget https://github.com/grafana/loki/releases/download/v2.2.1/promtail-linux-amd64.zip
安裝 Promtail:
$ mkdir /opt/app/{promtail,loki} -pv

# promtail配置文件
$ cat <<EOF> /opt/app/promtail/promtail.yaml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /var/log/positions.yaml # This location needs to be writeable by promtail.

client:
  url: http://localhost:3100/loki/api/v1/push

scrape_configs:
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: varlogs
      host: yourhost
      __path__: /var/log/*.log
EOF

# 解壓安裝包
unzip promtail-linux-amd64.zip
mv promtail-linux-amd64 /opt/app/promtail/promtail

# service文件
$ cat <<EOF >/etc/systemd/system/promtail.service
[Unit]
Description=promtail server
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/opt/app/promtail/promtail -config.file=/opt/app/promtail/promtail.yaml
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=promtail
[Install]
WantedBy=default.target
EOF

systemctl daemon-reload
systemctl restart promtail
systemctl status promtail
安裝 Loki:
$ mkdir /opt/app/{promtail,loki} -pv

# promtail配置文件
$ cat <<EOF> /opt/app/loki/loki.yaml
auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

ingester:
  wal:
    enabled: true
    dir: /opt/app/loki/wal
  lifecycler:
    address: 127.0.0.1
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
    final_sleep: 0s
  chunk_idle_period: 1h       # Any chunk not receiving new logs in this time will be flushed
  max_chunk_age: 1h           # All chunks will be flushed when they hit this age, default is 1h
  chunk_target_size: 1048576  # Loki will attempt to build chunks up to 1.5MB, flushing first if chunk_idle_period or max_chunk_age is reached first
  chunk_retain_period: 30s    # Must be greater than index read cache TTL if using an index cache (Default index read cache TTL is 5m)
  max_transfer_retries: 0     # Chunk transfers disabled

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

storage_config:
  boltdb_shipper:
    active_index_directory: /opt/app/loki/boltdb-shipper-active
    cache_location: /opt/app/loki/boltdb-shipper-cache
    cache_ttl: 24h         # Can be increased for faster performance over longer query periods, uses more disk space
    shared_store: filesystem
  filesystem:
    directory: /opt/app/loki/chunks

compactor:
  working_directory: /opt/app/loki/boltdb-shipper-compactor
  shared_store: filesystem

limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h

chunk_store_config:
  max_look_back_period: 0s

table_manager:
  retention_deletes_enabled: false
  retention_period: 0s


ruler:
  storage:
    type: local
    local:
      directory: /opt/app/loki/rules
  rule_path: /opt/app/loki/rules-temp
  alertmanager_url: http://localhost:9093
  ring:
    kvstore:
      store: inmemory
  enable_api: true
EOF

# 解壓包
unzip loki-linux-amd64.zip 
mv loki-linux-amd64 /opt/app/loki/loki

# service文件

$ cat <<EOF >/etc/systemd/system/loki.service
[Unit]
Description=loki server
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/opt/app/loki/loki -config.file=/opt/app/loki/loki.yaml
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=loki
[Install]
WantedBy=default.target
EOF

systemctl daemon-reload
systemctl restart loki
systemctl status loki

使用

| grafana 上配置 loki 數據源

如下圖:

grafana-loki-dashsource

在數據源列表中選擇 Loki,配置 Loki 源地址:

grafana-loki-dashsource-config

源地址配置 http://loki:3100 即可,保存。

保存完成後,切換到 grafana 左側區域的 Explore,即可進入到 Loki 的頁面:

grafana-loki

然後我們點擊 Log labels 就可以把當前系統採集的日誌標籤給顯示出來,可以根據這些標籤進行日誌的過濾查詢:

grafana-loki-log-labels

比如我們這裏選擇 /var/log/messages,就會把該文件下面的日誌過濾展示出來,不過由於時區的問題,可能還需要設置下時間纔可以看到數據:

grafana-loki-logs

這裏展示的是 promtail 容器裏面 / var/log 目錄中的日誌。

promtail 容器 /etc/promtail/config.yml:

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
- job_name: system
  static_configs:
  - targets:
      - localhost
    labels:
      job: varlogs
      __path__: /var/log/*log

這裏的 job 就是 varlog,文件路徑就是 /var/log/*log。

| 在 grafana explore 上配置查看日誌

查看日誌 rate({job="message"} |="kubelet"

算 qps rate({job=”message”} |=”kubelet” [1m])

| 只索引標籤

之前多次提到 loki 和 es 最大的不同是 loki 只對標籤進行索引而不對內容索引。下面我們舉例來看下。

靜態標籤匹配模式

以簡單的 promtail 配置舉例:

scrape_configs:
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: message
      __path__: /var/log/messages

配置解讀:

可以和使用 Prometheus 一樣的標籤匹配語句進行查詢。

{job="syslog"}:

scrape_configs:
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: syslog
      __path__: /var/log/syslog
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: apache
      __path__: /var/log/apache.log

如果我們配置了兩個 job,則可以使用 {job=~”apache|syslog”} 進行多 job 匹配;同時也支持正則和正則非匹配。

| 標籤匹配模式的特點

原理如下:

loki 處理日誌的模式和 prometheus 一致,loki 一組標籤值會生成一個 stream。日誌隨着時間的遞增會追加到這個 stream 中,最後壓縮爲 chunk。當有任意標籤發生變化時會產生新的 hash 值,對應新的 stream。

查詢過程:

| 動態標籤和高基數

所以有了上述知識,那麼就得談談動態標籤的問題了。

兩個概念:

比如 apache 的 access 日誌:

11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] "GET /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"

在 Promtail 中使用 regex 想要匹配 action 和 status_code 兩個標籤:

scrape_configs:
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: syslog
      __path__: /var/log/syslog
 - job_name: system
   pipeline_stages:
   static_configs:
   - targets:
      - localhost
     labels:
      job: apache
      __path__: /var/log/apache.log

  - job_name: system
    pipeline_stages:
       - regex:
         expression: "^(?P<ip>\\S+) (?P<identd>\\S+) (?P<user>\\S+) \\[(?P<timestamp>[\\w:/]+\\s[+\\-]\\d{4})\\] \"(?P<action>\\S+)\\s?(?P<path>\\S+)?\\s?(?P<protocol>\\S+)?\" (?P<status_code>\\d{3}|-) (?P<size>\\d+|-)\\s?\"?(?P<referer>[^\"]*)\"?\\s?\"?(?P<useragent>[^\"]*)?\"?$"
     - labels:
         action:
         status_code:
    static_configs:
    - targets:
       - localhost
      labels:
       job: apache
       env: dev
       __path__: /var/log/apache.log

那麼對應 action=get/post 和 status_code=200/400 則對應 4 個流:

11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] "GET /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"
11.11.11.12 - frank [25/Jan/2000:14:00:02 -0500] "POST /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"
11.11.11.13 - frank [25/Jan/2000:14:00:03 -0500] "GET /1986.js HTTP/1.1" 400 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"
11.11.11.14 - frank [25/Jan/2000:14:00:04 -0500] "POST /1986.js HTTP/1.1" 400 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"

那四個日誌行將變成四個單獨的流,並開始填充四個單獨的塊。

如果出現另一個獨特的標籤組合(例如 status_code =“500”),則會創建另一個新流。

**高基數問題:**就像上面,如果給 ip 設置一個標籤,現在想象一下,如果您爲設置了標籤 ip,來自用戶的每個不同的 ip 請求不僅成爲唯一的流。可以快速生成成千上萬的流,這是高基數,這可以殺死 Loki。

如果字段沒有被當做標籤被索引,會不會查詢很慢,Loki 的超級能力是將查詢分解爲小塊並並行分發,以便您可以在短時間內查詢大量日誌數據。

| 全文索引問題

大索引既複雜又昂貴。通常,日誌數據的全文索引的大小等於或大於日誌數據本身的大小。

要查詢日誌數據,需要加載此索引,並且爲了提高性能,它可能應該在內存中。這很難擴展,並且隨着您攝入更多日誌,索引會迅速變大。

Loki 的索引通常比攝取的日誌量小一個數量級,索引的增長非常緩慢。

加速查詢沒標籤字段:以上邊提到的 ip 字段爲例 - 使用過濾器表達式查詢。

{job="apache"} |= "11.11.11.11"

loki 查詢時的分片(按時間範圍分段 grep):

兩種索引模式對比:

日誌量少時少加標籤:
需要標籤時再去添加:
日誌應當按時間遞增:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lli6k0MnnnOExVcMsNGyBg