Loki + Promtail 做日志聚合(轻量、与 Grafana 同生态)

ELK Stack 重量级(Java + Elasticsearch 几个 GB 内存),
小规模团队用 Loki 更合适:

  • Go 写的,单机版几百 MB 内存
  • 不全文索引,只按 label 索引(像 Prometheus)
  • 存储用对象存储(S3 / 本地磁盘)
  • Grafana 一等公民支持,UI 体验和 Prometheus 一致

架构

节点 → promtail (agent) → Loki (中心) → Grafana

1. 装 Loki

sudo useradd -rs /bin/false loki
sudo mkdir -p /var/lib/loki /etc/loki
sudo chown -R loki:loki /var/lib/loki

curl -fsSL https://github.com/grafana/loki/releases/latest/download/loki-linux-amd64.zip \
  -o /tmp/loki.zip
sudo unzip /tmp/loki.zip -d /usr/local/bin
sudo mv /usr/local/bin/loki-linux-amd64 /usr/local/bin/loki
sudo chmod +x /usr/local/bin/loki

最简单的"all-in-one"配置 /etc/loki/config.yml

auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /var/lib/loki
  storage:
    filesystem:
      chunks_directory: /var/lib/loki/chunks
      rules_directory: /var/lib/loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 30d
  reject_old_samples: true
  reject_old_samples_max_age: 168h

systemd /etc/systemd/system/loki.service

[Unit]
Description=Loki
After=network.target

[Service]
User=loki
ExecStart=/usr/local/bin/loki -config.file=/etc/loki/config.yml
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now loki
curl localhost:3100/ready

2. 装 promtail(每台被采集机器)

curl -fsSL https://github.com/grafana/loki/releases/latest/download/promtail-linux-amd64.zip \
  -o /tmp/promtail.zip
sudo unzip /tmp/promtail.zip -d /usr/local/bin
sudo mv /usr/local/bin/promtail-linux-amd64 /usr/local/bin/promtail
sudo chmod +x /usr/local/bin/promtail

/etc/promtail/config.yml

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /var/lib/promtail/positions.yaml

clients:
  - url: http://loki.example.com:3100/loki/api/v1/push

scrape_configs:
  # systemd journal
  - job_name: journal
    journal:
      max_age: 12h
      labels:
        job: systemd-journal
        host: ${HOSTNAME}
    relabel_configs:
      - source_labels: ['__journal__systemd_unit']
        target_label: unit

  # nginx access log
  - job_name: nginx
    static_configs:
      - targets: [localhost]
        labels:
          job: nginx
          host: ${HOSTNAME}
          __path__: /var/log/nginx/*.log

  # 自定义应用日志
  - job_name: myapp
    static_configs:
      - targets: [localhost]
        labels:
          job: myapp
          host: ${HOSTNAME}
          __path__: /var/log/myapp/*.log

systemd 类似,启动:

sudo systemctl enable --now promtail
journalctl -u promtail -n 20

3. Grafana 加数据源

Connections → Data sources → Loki
URL: http://loki.example.com:3100

4. LogQL 查询语法

# 看某 unit 的所有日志
{unit="nginx.service"}

# 多 label 过滤
{job="nginx", host="server1"} |= "500"   # 含 "500"

# 排除
{job="nginx"} != "kube-probe"

# 正则匹配
{job="myapp"} |~ "ERROR|FATAL"

# JSON 日志解析后过滤
{job="myapp"} | json | level="error" | line_format "{{.timestamp}} {{.msg}}"

# 错误率 / 量
sum by (host) (rate({job="nginx"} |= "500" [5m]))

# Top N 出错的 unit
topk(5, sum by (unit) (count_over_time({job="systemd-journal"} |= "ERROR" [1h])))

|=!=|~!~ 是按字符串 / 正则过滤;| json| logfmt
解析。

5. 仪表盘

Grafana Explore 写 LogQL 查询;满意了用 "Add to dashboard" 保存。

社区仪表盘:搜 "Loki" 在 grafana.com/dashboards,有 nginx / docker /
k8s 现成版本。

6. 告警(log-based alerting)

rules/myapp.yml (Loki rule):

groups:
  - name: myapp-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate({job="myapp"} |~ "ERROR|FATAL" [5m])) > 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'myapp 错误率高'

Loki 内置 Ruler 跑这个规则,触发告警发给 Alertmanager(同 Prometheus 那套)。

7. retention / 存储

limits_config:
  retention_period: 30d        # 全局 30 天
  per_stream_retention:
    - selector: '{job="audit"}'
      period: 1y               # audit 日志保留 1 年

底层用 chunks(默认 filesystem)。生产建议 S3 / GCS:

common:
  storage:
    s3:
      bucketnames: my-loki-logs
      region: us-east-1

按需 prune 老 chunks 来控制成本。

8. JSON 结构化日志(推荐)

应用直接输出 JSON 日志:

import structlog
log = structlog.get_logger()
log.info('user signed up', user_id=42, plan='pro')
# {"event": "user signed up", "user_id": 42, "plan": "pro", "level": "info"}

Loki 里 {job="myapp"} | json | user_id="42" 直接过滤字段。

不要在 label 上加 user_id 这种高基数的 —— 用 query-time | json filter。

9. 资源占用

Loki + Promtail 在 4 core / 8GB 机器上能处理 100k logs/sec 或 5GB/day。
比 ES 节省 5-10x 资源。

10. 与 Prometheus 互补

Prometheus = 时序指标(数字)
Loki = 日志(文本)

同一仪表盘里:上面是请求数(Prom),下面是错误日志(Loki)。
按时间对齐看故障。

踩过的坑

  • promtail 没权限读 /var/log/...:默认 root 才读;要么 user=root
    跑 promtail,要么把日志 chmod。
  • label cardinality:跟 Prometheus 一样,label 取 user_id 把 Loki 拖死。
    日志的"高基数"信息应该在 line 里(用 | json 解析),不该在 label 上。
  • 时区:Loki 内部 UTC;UI 按浏览器时区显示。日志原文里的时间戳格式不一
    时需要 pipeline_stages 解析 timestamp。
  • "all-in-one" 配置不适合多副本 / HA。生产规模上去后拆成 distributor /
    ingester / querier 三类组件。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。