Grafana Loki —— 只索引标签的轻量日志聚合系统
Grafana Loki —— 只索引标签的轻量日志聚合系统
分类:04 日志监控与分析 适用:K8s/云原生、已用 Prometheus+Grafana、成本敏感;日均 GB~TB 级、按标签检索为主 一句话:不建全文索引、只给日志打标签并把原始行压缩成块,用 LogQL 查询,与 Grafana 原生一体 官方:https://grafana.com/oss/loki/ 授权:Apache 2.0(Loki/Alloy/Grafana 核心开源;Grafana Enterprise 与 Grafana Cloud 为商业/托管增值)
1. 它解决什么问题
ELK 把每个字段建倒排索引,存储与 JVM 成本高、运维重;而多数排障只需要"某 app/namespace/pod 在这段时间的日志 + 关键字过滤"。Loki 的思路:
- 只索引标签(label),不索引正文:标签组合形成"流(stream)",正文压缩成 chunk 存对象存储/文件系统 → 成本低、写入快。
- 与 Prometheus/Grafana 同构:标签模型、服务发现、Alertmanager、Explore 面板复用,指标与日志一键跳转。
- LogQL:类 PromQL,流选择器 + 行过滤 + 解析器 + 范围聚合,能算错误率、P99 延迟。
- 水平扩展与多租户:读写分离(distributor/ingester/querier)、
X-Scope-OrgID隔离;compactor + retention 按时间删除,无需 ES 式 ILM。
代价:全文任意检索弱(只能按标签缩小范围再逐行过滤)、高基数标签会拖垮系统、聚合维度依赖解析出的字段。它不替代需要复杂全文检索/多维分析的 ES。
2. 核心概念与架构
术语表
| 术语 | 含义 |
|---|---|
| Label 标签 / Stream 流 | app/job/namespace 等低基数维度,标签值完全相同的一组日志构成一条流(索引与压缩最小单位) |
| Chunk 块 / Entry 条目 | 某流一段时间的压缩日志块(存对象存储/文件系统);一行日志 = 时间戳 + 行内容 |
| LogQL | 查询语言;流选择器 + 可选管道操作 |
| 流选择器 / 行过滤 / 解析器 | {app="nginx"}(=/!=/=~/!~);|=/!=/|~/!~;json/logfmt/regexp/pattern |
| Distributor / Ingester | 写入分发校验 / 内存中攒 chunk 并刷盘 |
| Querier / Query-frontend | 查索引定位流再取 chunk 过滤 / 查询拆分缓存 |
| Compactor / Ruler | 合并索引、执行 retention 删除 / 按 LogQL 规则求值并把告警发给 Alertmanager |
| Alloy / Promtail | 采集 Agent;Promtail 已于 2026-03-02 EOL,新版一律用 Alloy |
两套链路中的位置与完整数据流(ASCII)
【PLG 链路 —— Loki 是"存储检索层",采集由 Alloy 承担】
应用写日志文件 / 容器 stdout / systemd journal
│ ① 采集:★ Alloy ★(旧称 Promtail,已 EOL)读文件/容器/journal → 加标签 → 记录 positions
▼
Alloy(loki.write) ── HTTP push /loki/api/v1/push ──► ★ Loki ★
distributor → ingester(攒 chunk) → 标签索引 + 压缩 chunk 落对象存储
│ 只索引标签,正文不建倒排索引
▼
Grafana(Explore / Dashboard / Alerting,LogQL)
Ruler → Alertmanager → 邮件/钉钉/企微/Webhook
【ELK / EFK 链路 —— 不含 Loki;对照用】
文件 → Filebeat/Fluentd(F)/Fluent Bit → Logstash(重解析) → Elasticsearch(全字段倒排索引) → Kibana(Query DSL)
对照:ELK 写入前重解析并全字段索引;PLG 只加标签、检索时行过滤。Loki 不在 ELK 链路,二者替代关系。
日志从文件到可检索的完整路径(PLG):
① 文件产生 → ② Alloy 采集(记录 positions)、附标签 → ③ 轻量 stage 解析/丢弃/改标签 → ④ push 到 Loki 3100
→ ⑤ distributor 校验 → ingester 按流攒 chunk、写标签索引、压缩落盘
→ ⑥ Grafana 用 LogQL 流选择器定位流 → 拉 chunk → 行过滤/解析/聚合
→ ⑦ Explore 展示、Dashboard 可视化、Ruler/Alertmanager 告警
ELK 等价路径:文件 → Beats/Fluent → Logstash 解析 → ES 倒排索引 → Kibana Query DSL。差异:ES 全字段索引、查询强但重;Loki 仅标签索引、轻但全文检索弱。
3. 安装与部署
版本差异:Promtail 已于 2026-03-02 EOL(不再更新/支持),官方要求迁移到 Grafana Alloy;
alloy convert --source-format=promtail可一键转换配置,过渡期可用--config.format=promtail直接跑旧 YAML。lambda-promtail 不受 EOL 影响。Loki 当前大版本为 3.x,具体版本号以官方最新稳定版为准。
3.1 环境准备
| 项 | 要求 / 命令 |
|---|---|
| Java/内存 / vm.max_map_count | Loki/Alloy 均为 Go 二进制,无需 JVM,单机 2–4GB 起;vm.max_map_count 仅同机部署 ES 才需 262144 |
| 文件句柄 / 权限 | 大量采集调高 ulimit -n;Alloy 需加入 adm、systemd-journal、docker 组读 journal/容器日志 |
| 端口 | Loki 3100、Alertmanager 9093、Grafana 3000、Alloy UI 12345(可选)、对象存储按后端 |
| 磁盘 | chunks 目录(或 S3/GCS/Azure)按"日增量 × 留存 × 压缩比"估;索引盘用 SSD |
3.2 多种安装方式
A. 官方仓库 / 二进制包
# Grafana APT 仓库(Debian/Ubuntu),安装 Alloy 与 Grafana
wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list
apt-get update && apt-get install -y alloy grafana
usermod -aG adm,systemd-journal,docker alloy # 读 journal/容器日志必需
loki -config.file=/etc/loki/loki-config.yaml -verify-config # Loki 二进制从官方 releases 获取
B. Docker / docker compose(单机最快)
docker run -d --name loki -p 3100:3100 -v "$PWD/loki-config.yaml:/etc/loki/local-config.yaml:ro" \
grafana/loki:<版本> -config.file=/etc/loki/local-config.yaml # tag 以官方最新稳定版为准
docker run -d --name grafana -p 3000:3000 grafana/grafana:<版本>
docker run -d --name alloy -p 12345:12345 -v "$PWD/config.alloy:/etc/alloy/config.alloy:ro" \
-v "/var/log:/var/log:ro" -v "/var/run/docker.sock:/var/run/docker.sock:ro" \
grafana/alloy:<版本> run /etc/alloy/config.alloy --server.http.listen-addr=0.0.0.0:12345
C. 二进制 + systemd
tar -xzf loki-linux-amd64.zip -C /usr/local/bin && chmod +x /usr/local/bin/loki
# 生产写 systemd unit:ExecStart=/usr/local/bin/loki -config.file=/etc/loki/loki-config.yaml;Alloy 同理 User=alloy、Restart=on-failure
alloy run /etc/alloy/config.alloy # Alloy 前台调试
D. K8s(Helm):官方 grafana/loki Helm chart(单体/分布式两种,另有 loki-stack 一键带 Promtail),配合 grafana/alloy chart 以 DaemonSet 采集;ECK/Operator 不适用。⚠️ 旧 chart 默认装 Promtail 已过时,部署时改用 Alloy。具体 chart/版本以官方文档为准。
3.3 首次启动与验证
loki -config.file=/etc/loki/loki-config.yaml -verify-config && systemctl enable --now loki
curl -s http://localhost:3100/ready ; curl -s http://localhost:3100/metrics | head
# 写入验证(auth_enabled:false 单租户时);Grafana 加 Loki 数据源 URL=http://<loki>:3100 → Explore
curl -s -H 'Content-Type: application/json' -X POST http://localhost:3100/loki/api/v1/push \
-d '{"streams":[{"stream":{"app":"test","job":"manual"},"values":[["<纳秒时间戳>","hello loki"]]}]}'
logcli query '{app="test"}' --since=1h --addr=http://localhost:3100
4. 配置文件详解(loki-config.yaml + Alloy 采集配置)
路径:Loki /etc/loki/loki-config.yaml;Alloy /etc/alloy/config.alloy(自有组件语言,非 YAML);旧 Promtail /etc/promtail/promtail.yaml(仅迁移参考)。
| Loki 关键项 | 说明 |
|---|---|
auth_enabled / server.http_listen_port |
多租户开关(请求带 X-Scope-OrgID)/ HTTP 端口默认 3100 |
common.path_prefix / common.storage |
数据根目录与 chunk/rules 后端(filesystem / s3 / gcs) |
common.replication_factor / ring.kvstore.store |
副本数(单机 1、生产 3)/ ring 存储(inmemory/memberlist/consul) |
schema_config.configs[] |
store(tsdb)、object_store、schema 版本、index.period(通常 24h) |
limits_config |
ingestion 速率、查询系列数、单行大小、retention_period |
compactor / ruler.alertmanager_url |
合并索引 + retention_enabled(retention 生效前提)/ 告警发往 Alertmanager |
server.http_tls_config / grpc_tls_config |
TLS(生产用反向代理或原生 TLS) |
完整可运行最小示例(单机 filesystem,示意;schema/retention 参数随版本变化,以官方最新稳定版为准):
# /etc/loki/loki-config.yaml
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:
kvstore: { store: inmemory }
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index: { prefix: index_, period: 24h }
limits_config:
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
max_query_series: 500
retention_period: 720h # 30 天;需 compactor 开启 retention 才生效
compactor:
working_directory: /var/lib/loki/compactor
compaction_interval: 10m
retention_enabled: true
delete_request_store: filesystem
ruler:
alertmanager_url: http://<alertmanager>:9093
Alloy 采集配置(推荐,替代 Promtail):
// /etc/alloy/config.alloy —— 组件用 forward_to 串联
loki.write "default" {
endpoint { url = "http://<loki>:3100/loki/api/v1/push" }
external_labels = { cluster = "prod-a" }
}
// 文件日志:local.file_match 发现 → loki.source.file 读取 → loki.write 推送
local.file_match "app" { path_targets = [{ __address__ = "localhost", __path__ = "/var/log/app/*.log" }] }
loki.source.file "app" {
targets = local.file_match.app.targets
forward_to = [loki.write.default.receiver]
labels = { job = "app", host = "node-1" }
}
// 其它源:loki.source.journal(需 adm/systemd-journal 组)、loki.source.docker(需 docker 组);
// 用 loki.relabel 把 __journal__systemd_unit / __meta_docker_container_name 映射成 unit / container 标签。
旧 Promtail 配置(迁移参考,勿新建):
# 用 alloy convert --source-format=promtail --output=config.alloy 转换
positions: { filename: /positions/positions.yaml }
clients: [ { url: http://<loki>:3100/loki/api/v1/push } ]
scrape_configs:
- job_name: app
static_configs: [ { targets: [localhost], labels: { job: app, __path__: /var/log/app/*.log } } ]
pipeline_stages:
- regex: { expression: '^(?P<ts>\S+) level=(?P<level>\w+)' }
- labels: { level: }
- drop: { source: level, value: debug }
5. 核心使用方法(LogQL 系统讲解 + CLI + HTTP API + UI)
A. LogQL 结构:{流选择器} │ 行过滤 │ 解析器 │ 标签过滤 │ 聚合(range/unwrap)
① 流选择器(必须;=/!=/=~/!~):
{app="nginx"} {app="nginx", env="prod", namespace=~"prod-.*"}
{job!~"debug.*", container!=""}
② 行过滤(对正文,链式):
{app="nginx"} |= "error" # 包含 {app="nginx"} != "healthcheck" # 不包含
{app="nginx"} |~ "5[0-9]{2}" # 正则匹配 {app="nginx"} !~ "GET /health" # 正则排除
{app="nginx"} |= "error" != "debug" |~ "timeout|refused"
③ 解析器(把正文抽成标签,供过滤/聚合):
{app="api"} | json # JSON 行字段自动变标签;| json status="http.status" 重命名
{app="api"} | logfmt # k=v 格式
{app="api"} | regexp "code=(?P<code>\\d+) cost=(?P<cost_ms>\\d+)"
{app="api"} | pattern "<ip> <_> <_> [<ts>] \"<method> <path>\" <status> <size>"
{app="api"} | json | line_format "{{.status}} {{.msg}}" | label_format level=level
{app="api"} | json | keep status, path | drop debug_info
④ 解析后标签过滤:| status >= 500、| duration > 1s、| level="ERROR"。
⑤ 聚合(范围向量 + unwrap):
count_over_time({app="nginx"} |~ "5[0-9]{2}" [5m]) # 每流 5xx 行数
sum by (app) (count_over_time({job=~".+"} |= "error" [5m])) # 按 app 汇总错误数
rate({app="nginx"} [5m]) ; bytes_rate({app="nginx"} [5m]) ; bytes_over_time({app="nginx"} [1h])
sum(rate({app="nginx"} |~ "5\\d\\d" [5m])) / sum(rate({app="nginx"} [5m])) # 5xx 占比
quantile_over_time(0.99, {app="api"} | json | unwrap duration_ms [5m]) by (path) # P99 延迟
sum_over_time({app="api"} | json | unwrap bytes [5m]) ; avg_over_time({app="api"} | logfmt | unwrap latency [5m])
topk(10, sum by (pod) (count_over_time({namespace="prod"} [1h]))) # 日志量 Top10 Pod
absent_over_time({app="critical"} [5m]) # 无日志告警
常用生产查询模板(错误率/关键字/状态码/慢请求/日志量):
sum(rate({app="checkout"} |= "ERROR" [5m])) by (pod)
{namespace="prod"} |= "trace_id=abc123"
{app="nginx"} | pattern "<_> <_> <_> [<_>] \"<_> <path>\" <status> <_>" | status >= 500
{app="api"} | json | duration_ms > 1000
sum by (level) (count_over_time({app="api"} | json [1h]))
B. logcli / curl / HTTP API(≥20 条)
logcli query '{app="nginx"}' --since=1h --limit=100 --addr=http://<loki>:3100
logcli query '{app="nginx"} |= "error"' --from=2026-01-01T00:00:00Z --to=2026-01-02T00:00:00Z
logcli query 'sum(rate({app="nginx"} |~ "5.." [5m]))' --output=jsonl --forward --tail
logcli query '{app="nginx"}' --output=raw --timezone=UTC --quiet ; logcli instant-query 'sum(count_over_time({app="nginx"} [5m]))'
logcli labels ; logcli labels app ; logcli series '{app="nginx"}' --since=1h ; logcli --help
logcli --addr=https://<loki>:3100 --org-id=tenant-a --bearer-token=<token> --tls-skip-verify query '{job="x"}' --since=15m
# HTTP API
curl -s http://<loki>:3100/ready ; curl -s http://<loki>:3100/metrics | grep loki_
curl -sG http://<loki>:3100/loki/api/v1/query --data-urlencode 'query={app="nginx"}' --data-urlencode 'limit=100'
curl -sG http://<loki>:3100/loki/api/v1/query_range --data-urlencode 'query=sum(rate({app="nginx"}[5m]))' --data-urlencode 'start=<ns>' --data-urlencode 'end=<ns>' --data-urlencode 'step=60s'
curl -s http://<loki>:3100/loki/api/v1/labels ; curl -s http://<loki>:3100/loki/api/v1/label/app/values
curl -sG http://<loki>:3100/loki/api/v1/series --data-urlencode 'match[]={app="nginx"}' --data-urlencode 'start=<ns>'
curl -sG http://<loki>:3100/loki/api/v1/tail --data-urlencode 'query={app="nginx"}' # 实时 tail(WebSocket/SSE)
curl -sG http://<loki>:3100/loki/api/v1/index/stats --data-urlencode 'query={app="nginx"}'
curl -s -H 'X-Scope-OrgID: tenant-a' -H 'Content-Type: application/json' -XPOST http://<loki>:3100/loki/api/v1/push -d '{"streams":[...]}'
# Loki / Alloy 服务与校验
loki -config.file=/etc/loki/loki-config.yaml -verify-config ; loki -print-config-stderr -config.file=<cfg>
alloy fmt /etc/alloy/config.alloy ; alloy run /etc/alloy/config.alloy --server.http.listen-addr=0.0.0.0:12345
alloy convert --source-format=promtail --output=/etc/alloy/config.alloy /etc/promtail/promtail.yaml
alloy convert --source-format=promtail --bypass-errors --report=report.txt --output=<out> <in>
alloy run --config.format=promtail /etc/promtail/promtail.yaml ; systemctl start|stop|restart|status loki alloy
curl -s http://localhost:12345/-/healthy # Alloy UI
C. Web UI(Grafana):加 Loki 数据源 → Explore 选标签写 LogQL、切换 metric/range;Dashboard 用 stat/timeseries/logs 面板;Alerting 建规则;显示上下文 查相邻行;指标面板可一键"从指标跳到日志"。
6. 告警与通知
Loki 告警两条路:Loki Ruler(LogQL 规则 → Alertmanager) 与 Grafana Alerting(对 Loki 数据源建规则)。
# Loki Ruler 规则(/var/lib/loki/rules/<tenant>/rules.yaml)
groups:
- name: app-alerts
rules:
- alert: HighErrorRate
expr: sum(rate({app="checkout"} |= "ERROR" [5m])) by (pod) > 5
for: 5m
labels: { severity: warning }
annotations: { summary: "{{ $labels.pod }} 错误日志突增" }
- alert: TooMany5xx
expr: sum(count_over_time({app="nginx"} | pattern "<_> <_> <_> [<_>] \"<_> <_>\" <status> <_>" | status >= 500 [5m])) > 50
for: 2m
labels: { severity: critical }
ruler.alertmanager_url: http://<alertmanager>:9093、规则目录由 rules_directory 指定;Alertmanager 侧用 route/receiver 定义通知,Grafana 侧用 Alerting → Contact Point → Notification policies。
通知渠道:Alertmanager email_configs、webhook_configs、slack_configs、wechat_configs(企业微信)、pagerduty_configs;钉钉无原生 receiver,用 webhook 指向钉钉机器人;Grafana Contact Point 支持 Email/Webhook/钉钉/企业微信/Slack/Teams。⚠️ 机器人需在各自平台建好并拿到 webhook URL,消息体按其 JSON 格式拼装。Elastic 侧对照用 Kibana Connector。
7. 实战场景
7.1 Nginx/应用日志接入并做状态码统计
目标:Nginx access log → Alloy 采集+解析 → Loki → LogQL 统计状态码 + 5xx 趋势 → Grafana 面板。
步骤 + 命令/配置:
// config.alloy:采集 Nginx,标签只放低基数字段;status 用 pattern 在查询时解析,不提升为标签
local.file_match "nginx" { path_targets = [{ __address__ = "localhost", __path__ = "/var/log/nginx/access.log" }] }
loki.source.file "nginx" {
targets = local.file_match.nginx.targets
forward_to = [loki.write.default.receiver]
labels = { job = "nginx", host = "web-01" }
}
loki.write "default" { endpoint { url = "http://<loki>:3100/loki/api/v1/push" } }
alloy fmt /etc/alloy/config.alloy && systemctl restart alloy
echo '127.0.0.1 - - [05/Nov/2026:10:00:00 +0800] "GET /api HTTP/1.1" 500 123' >> /var/log/nginx/access.log
logcli query '{job="nginx"}' --since=5m --addr=http://<loki>:3100
# 状态码统计:查询时用 pattern 解析并聚合(不污染标签)
logcli query 'sum by (status) (count_over_time({job="nginx"} | pattern "<_> <_> <_> [<_>] \"<_> <path>\" <status> <_>" [5m]))'
验证:logcli labels 出现 job;Grafana Explore 能查到;timeseries 面板出 5xx 曲线。
回滚:注释 loki.process 把 source 直连 write、恢复原 config.alloy 并 restart;⚠️ 保留 positions 文件避免重复推送。
7.2 日志暴增导致磁盘打满的处置
目标:日志突增撑满 chunks 盘 → 止血 → 恢复 → 根治。
# ① 定位:磁盘、各流日志量、ingester 状态;② 止血:Alloy 侧 stage.drop 丢弃 debug/healthcheck 或限速
df -h /var/lib/loki ; curl -s http://<loki>:3100/ready
logcli query 'topk(10, sum by (job,pod) (bytes_over_time({job=~".+"} [1h])))' --since=1h
# ③ Loki 侧:缩短 retention_period 并让 compactor 生效,或扩容/清最旧 chunk 目录 ⚠️ 删数据不可恢复
# ④ 根治:应用降日志级别、去掉高基数标签、对象存储扩容、limits_config 限 ingestion_rate_mb
验证:df -h 回落;/ready 正常;bytes_rate 回到基线;新日志可查。
回滚:恢复 Alloy drop 规则与 retention_period;⚠️ 已删 chunk 需从对象存储快照恢复(见 10)。
7.3 从 Promtail 迁移到 Alloy(版本差异必做)
目标:Promtail 已 EOL,无损迁移到 Alloy。
alloy convert --source-format=promtail --output=/etc/alloy/config.alloy /etc/promtail/promtail.yaml
alloy convert --source-format=promtail --report=/tmp/report.txt --output=<out> <in> # 出诊断报告
alloy fmt /etc/alloy/config.alloy ; systemctl stop promtail && systemctl enable --now alloy
curl -s http://localhost:12345/-/healthy ; logcli query '{job="app"}' --since=10m
验证:对比切换前后同一时间窗的日志条数(0 丢失、0 重复);loki.source.file 用 legacy_positions_file 续读。
回滚:systemctl stop alloy && systemctl start promtail(过渡期也可 alloy run --config.format=promtail <旧yaml>);⚠️ loki.source.docker 不继承 positions,迁移期用 stage.drop older_than 去重(官方迁移文档)。
8. 日常运维与故障排查
| 项 | 命令 / 路径 |
|---|---|
| 服务管理 / 配置校验 | systemctl start|stop|restart|status loki alloy、K8s kubectl rollout restart sts/loki;loki -verify-config、alloy fmt |
| 健康检查 / 日志 | curl :3100/ready、curl :12345/-/healthy;journalctl -u loki -f、journalctl -u alloy -f、Alloy UI :12345 |
| 查询/标签 / 指标 | logcli labels、logcli series、logcli query;curl :3100/metrics(loki_ingester_*) |
故障「现象 → 原因 → 处置」表
| 现象 | 原因 | 处置 |
|---|---|---|
/ready 非 ready、ingester 反复重启 |
ring 未稳定 / 存储不可写 / 配置 schema 不兼容 | 看 journal 与 loki -verify-config,检查 chunks 目录权限 |
push 返回 429 RATE_LIMITED |
超 ingestion_rate_mb/ingestion_burst_size_mb |
调限额、Alloy 限速、扩容 distributor |
| 查不到刚写的日志 | 标签不匹配 / 时间范围错 / positions 未更新 | logcli labels 核对标签、--since 放宽、查 Alloy positions |
| 查询极慢 / OOM | 高基数标签导致流爆炸、范围过大 | 降标签基数、缩小选择器与时间窗、加 query-frontend 缓存 |
alloy 无日志 / 组件红 |
未加 adm/systemd-journal/docker 组;forward_to 未接 |
usermod -aG ... alloy 重启;看 :12345 组件图、alloy fmt |
| Ruler 告警不触发 | alertmanager_url 不通 / 规则目录未加载 / expr 无数据 |
查规则文件权限、logcli instant-query 验证 expr |
| 多租户 404/空数据 | auth_enabled: true 未带 X-Scope-OrgID |
请求/Grafana 数据源补 tenant header |
9. 性能、容量与高可用
容量估算公式:
压缩后日增 ≈ 原始日增 × 压缩比(经验 0.1~0.3,取决于日志重复度)
总存储 ≈ 压缩后日增 × 留存天数 × replication_factor + 索引开销(约 1%~5%)
写入速率上限 ≈ limits_config.ingestion_rate_mb × distributor 副本数
经验:流数(标签组合)比行数更影响成本,务必控制标签基数
Loki 无 ES 式分片/JVM 堆/ILM:对应概念是
replication_factor(副本)、index.period(索引表周期,通常 24h)、compactor retention(生命周期)、ingester 内存(Go,无 JVM 堆调优)。
| 维度 | 建议 |
|---|---|
| 标签设计 / 写入优化 | 只放低基数维度(host/job/namespace/container/level),绝不把 trace_id/user_id/IP/URL 做标签;Alloy 批量 push,超限先调 ingestion_rate_mb |
| 副本/HA | common.replication_factor: 3、ring 用 memberlist/consul、分布式拆 distributor/ingester/querier;auth_enabled 多租户 |
| 索引/生命周期 / 查询优化 | schema_config 的 store/schema 版本随版本变化(以官方为准);compactor.retention_enabled + retention_period 管留存;选择器窄、配 query-frontend 缓存 |
| 自监控 | /metrics + Prometheus(loki_ingester_memory_chunks、loki_request_duration_seconds、丢弃/限流计数)+ Grafana dashboard |
10. 安全加固、备份与升级
认证鉴权 / TLS / 最小权限
# 多租户:auth_enabled: true,请求/数据源带 X-Scope-OrgID;tenant 间数据隔离
# TLS:server.http_tls_config / grpc_tls_config 配证书,或前置 Nginx/OAuth 反向代理做 Basic/Bearer 认证
# grafana Loki 数据源:URL + HTTP Header X-Scope-OrgID + TLS/认证;只读账号仅给查询权限
- 最小权限:采集账号仅能 push、查询账号仅能 query;Grafana 数据源按租户隔离;对象存储桶用最小 IAM。
:3100、:12345仅内网可达,auth_enabled: true时禁止匿名访问。
备份与恢复:Loki 无 ES SLM 概念,等价做法是对 chunks/索引后端做快照(filesystem 目录快照 / S3 版本控制与跨区复制)+ 导出 ruler 规则与 Alloy 配置。⚠️ 恢复前停写入、按 chunk 时间范围回滚,避免索引与 chunk 不一致;retention 删除不可逆。
滚动升级顺序与回滚:① 备份对象存储/配置 → ② 查 /ready 与 ring 稳定 → ③ 按顺序逐组件滚动(一般 querier/query-frontend → distributor → ingester 最后且逐台,每台等 ring 同步与 handoff 完成)→ ④ 逐台验证查询与写入 → ⑤ 更新 Alloy(与 Loki 版本兼容)。回滚:不支持跨大版本降级,回滚需从快照恢复到旧版本实例;⚠️ schema/store 参数变更(如 boltdb-shipper→tsdb)不可逆,升级前务必在测试集群验证,具体以官方升级文档为准。
11. 常见坑
| 现象 | 原因 | 解决 / 备注 |
|---|---|---|
| ⚠️ "分片过多"/"映射爆炸"类比:流爆炸、字段过多 | 把 trace_id/user_id/IP/URL 做成标签,或 | json 把所有字段提升为标签 |
标签只留低基数维度;高基数字段放正文用 json/regexp 按需解析,用 keep/drop 精简 |
| ⚠️ 时区错误、时间差 8 小时 | 日志无时区/解析 timestamp 时区不对 | Alloy stage.timestamp 指定 format 与 location(如 Asia/Shanghai);统一输出带时区时间戳 |
| ⚠️ 日志重复 | positions 丢失/未持久化、Alloy 迁移重读历史、loki.source.docker 不继承 positions |
持久化 positions 卷、迁移期 stage.drop older_than 去重、校验切换前后条数 |
| Promtail 仍在使用 | 不知 2026-03-02 已 EOL、无安全更新 | 用 alloy convert 迁移到 Alloy,勿再新建 Promtail 部署 |
429 RATE_LIMITED |
超 ingestion 限额 | 调 limits_config、采集端限速/采样、加 distributor |
alloy 采不到 journal/容器 |
用户组缺失 | usermod -aG adm,systemd-journal,docker alloy 并重启 |
| LogQL 聚合报类型错 / 全文检索搜不到任意词 | 对 string 直接 unwrap;Loki 不索引正文 |
先 | json 再 unwrap <numeric_field>;先用标签缩小流再 |=/|~ 过滤,此类场景应评估 ES |
| retention 不生效 | 只设 retention_period 未开 compactor |
同时配 compactor.retention_enabled: true 与 delete_request_store |
12. 速查表
| 项 | 值 / 命令 |
|---|---|
| 默认端口 | Loki 3100、Grafana 3000、Alertmanager 9093、Alloy UI 12345 |
| 健康端点 | GET /ready、GET /metrics、Alloy /-/healthy |
| Loki 配置 | /etc/loki/loki-config.yaml,校验 loki -verify-config -config.file=<cfg> |
| Alloy 配置 | /etc/alloy/config.alloy(组件语法,非 YAML);alloy fmt |
| 旧 Promtail | /etc/promtail/promtail.yaml(EOL,仅迁移参考) |
| 推送 / 查询 | POST /loki/api/v1/push、GET /loki/api/v1/query、/query_range |
| 标签 / 流 / 实时 | /loki/api/v1/labels、/label/<name>/values、/series、/tail、/index/stats |
| 流选择器 | {app="nginx", env="prod"}、=~/!~ 正则 |
| 行过滤 | |= 包含、!= 排除、|~/!~ 正则 |
| 解析器 | | json、| logfmt、| regexp、| pattern、| unpack、| label_format |
| 范围聚合 / unwrap | count_over_time/rate/bytes_rate/sum_over_time/avg_over_time/quantile_over_time;unwrap duration [5m] |
| 告警 | Loki Ruler → ruler.alertmanager_url;Grafana Alerting |
| 迁移 | alloy convert --source-format=promtail --output=<out> <in> |
| 多租户 | auth_enabled: true + header X-Scope-OrgID |
| 服务管理 | systemctl start|stop|restart|status loki alloy |
13. 选型建议与同类对比
| 维度 | PLG(Loki+Alloy+Grafana) | ELK(ES+Logstash+Kibana) | Graylog | Fluent Bit + 对象存储 |
|---|---|---|---|---|
| 索引方式 / 成本 / 授权 | 仅标签+压缩 chunk / ★★★ 低 / Apache 2.0 | 全字段倒排 / 高 / EL 2.0 | 基于 ES(旧) / 中 / GPL | 无索引 / 极低 / Apache 2.0 |
| 全文任意检索 | ★ 弱(行过滤) | ★★★ 强 | ★★ | 无 |
| 聚合分析 / Grafana 一体 | ★★ LogQL 够用 / ★★★ 原生 | ★★★ 极强 / 弱 | ★★ / 弱 | 需额外计算 / 中 |
何时选 Loki:已用 Prometheus+Grafana、K8s 原生、日志按标签查、成本敏感、不需要复杂全文检索与多维分析。何时不选:需全文任意词检索、复杂多字段聚合、统一存日志+指标+APM → 选 ES;要开箱即用 Web 后台 → Graylog;只需冷归档 → 对象存储。边界:Loki 是 PLG 链路的存储检索层,ELK/EFK 链路不含 Loki;采集端 Alloy 取代 EOL 的 Promtail,二者是替代而非串联关系。
14. 参考链接
- 官网 / Loki 文档 / LogQL:https://grafana.com/oss/loki/ 、https://grafana.com/docs/loki/latest/ 、https://grafana.com/docs/loki/latest/query/
- Promtail(EOL) / Alloy 文档 / 迁移:https://grafana.com/docs/loki/latest/send-data/promtail/ 、https://grafana.com/docs/alloy/latest/ 、https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/
- Grafana Alerting:https://grafana.com/docs/grafana/latest/alerting/
- 源码 / APT 仓库:https://github.com/grafana/loki 、https://github.com/grafana/alloy 、https://apt.grafana.com
Promtail 已于 2026-03-02 EOL,新版推荐 Alloy;Loki 当前大版本 3.x,
schema_config的 store/schema 版本、retention 参数随版本变化。具体版本与参数以官方最新稳定版文档为准。