Loki 面试题
30 道题- 分类
- 可观测性
- 子分类
- logs
- 题目数
- 30 道
1 Loki 的核心架构由哪些组件构成?
答案:
Loki 采用微服务架构,核心组件包括 Distributor、Ingester、Querier、Query Frontend 和 Compactor。
| 组件 | 职责 | 说明 |
|---|---|---|
| Distributor | 日志写入入口 | 接收推流,验证数据,分发到 Ingester |
| Ingester | 日志数据写入和临时存储 | 对写入数据进行压缩和索引,定期上传到对象存储 |
| Querier | 日志查询 | 从 Ingester 和对象存储读取日志数据 |
| Query Frontend | 查询前端 | 查询缓存、分片、重试和并发控制 |
| Compactor | 数据压缩和保留 | 合并小文件、应用保留策略、数据清理 |
| Index Gateway | 索引服务(可选) | 存储和查询日志索引(TSDB 模式) |
数据流:
Promtail/其他采集端 → Distributor → Ingester → 对象存储(S3/MinIO/GCS)
↑ ↓
Querier ←→ 对象存储 + Index Gateway
↑
Query Frontend ← Grafana
2 Loki 与 Elasticsearch 的核心区别是什么?
答案:
| 维度 | Loki | Elasticsearch |
|---|---|---|
| 数据模型 | Label + 日志行(索引元数据) | 全文索引(所有字段可索引) |
| 存储 | 日志内容存对象存储,元数据存索引 | 所有数据存本地/云磁盘 |
| 写入成本 | 低(元数据索引,日志内容不索引) | 高(全文索引 + 倒排索引) |
| 查询语言 | LogQL | Lucene / EQL / SQL |
| 压缩比 | 高(仅索引 Label,日志内容块压缩) | 中(全文索引 + 源数据) |
| 资源消耗 | 低-中 | 高 |
| 存储成本 | 极低(对象存储) | 高(SSD + 多副本) |
| 全文检索 | 不支持(需要额外配置) | 原生支持 |
| 部署复杂度 | 中(微服务架构) | 高(集群 + 分片管理) |
| K8s 原生 | 原生支持(Promtail + K8s服务发现) | 需额外配置 |
选型建议:
- K8s 环境、已有 Prometheus 栈:Loki(统一 Grafana 可视化)
- 需要全文搜索、复杂文本分析:Elasticsearch
- 日志量大但查询简单:Loki(成本优势明显)
3 LogQL 的核心语法结构是怎样的?
答案:
LogQL 分为日志查询和指标查询两大类,语法与 PromQL 类似。
日志查询(返回日志行):
# 基础查询:Label 匹配 + 关键词过滤
{job="nginx", namespace="production"} |= "error"
# 正则匹配
{job="nginx"} |~ "status=(5\\d{2})"
# 排除匹配
{job="nginx"} != "healthcheck"
# JSON 解析
{job="nginx"} | json | status >= 500
# 日志模式解析
{job="nginx"} | logfmt | level = "error"
# 标签提取(regex)
{namespace="prod"} | regex "(?P<method>\\w+) (?P<path>[\\w/]+)"
指标查询(返回 Prometheus 格式指标):
# 日志速率
rate({job="nginx"} |= "error" [5m])
# 计数
count_over_time({job="nginx"}[5m])
# 标签值聚合
sum by (namespace) (rate({job="nginx"} |= "error" [5m]))
查询操作符链:
日志流选择器 → 行过滤器 → 解析器 → 标签过滤 → 指标函数
{job="nginx"} |= "500" | json | status > 500 | rate[5m]
4 Loki 的数据索引机制是如何演进的?
答案:
Loki 经历三种索引模式,当前主流是 TSDB 模式。
索引演进历史:
| 模式 | 版本 | 说明 | 现状 |
|---|---|---|---|
| BoltDB | v1.x | 本地 boltdb 文件存储索引 | 废弃 |
| BoltDB Shim | v2.0+ | 索引存对象存储,本地只做缓存 | 兼容 |
| TSDB | v2.8+(默认) | 自研 TSDB 索引,类似 Prometheus | 当前推荐 |
TSDB 模式优势:
1. 查询性能:比 BoltDB 模式快 10-100 倍
2. 存储效率:索引压缩比更高
3. 内存使用:索引查询可流式处理,不要求全量加载
4. 扩展性:支持索引分片和并发查询
5. 简化运维:无需 Index Gateway 组件
配置启用:
# Helm values.yaml
loki:
schemaConfig:
configs:
- from: "2024-01-01"
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
5 Loki 的 Ingester 组件是如何工作的?
答案:
Ingester 是 Loki 的核心写入组件,负责接收日志并暂时缓存后上传到对象存储。
写入流程:
Distributor 分发日志到 Ingester
→ Ingester 将日志追加到内存中的 Chunk
→ Chunk 达到阈值(大小/时间/行数)后:
1. 关闭 Chunk(不再接受新数据)
2. 压缩 Chunk(Snappy 或 gzip)
3. 上传到对象存储
4. 元数据写入索引(TSDB)
Chunk 刷盘策略:
| 参数 | 默认值 | 说明 |
|---|---|---|
chunk_idle_period | 30m | 空闲 Chunk 自动关闭 |
chunk_block_size | 262144 | Chunk 块大小(256KB) |
chunk_target_size | 1.5MB | 目标 Chunk 压缩后大小 |
max_chunk_age | 2h | Chunk 最大存活时间 |
Ingester 故障处理:
Ingester 无状态(依赖 Replication 和 Receiver)
→ 数据写入前先复制到 3 个 Ingester(默认 replication_factor=3)
→ Ingester 宕机 → 其他 Ingester 有副本
→ 启动后从对象存储恢复数据(wal 目录)
Ingester 扩缩容:
水平扩展:增加 Ingester 副本数
缩容前需等待 Chunk 刷盘完成(drain)
Ingester 无状态设计使其适合 K8s 部署
6 Promtail 是什么?如何采集 K8s 日志?
答案:
Promtail 是 Loki 的官方日志采集端,负责从节点上采集容器日志并推送。
采集架构:
每个 Kubernetes 节点运行一个 Promtail DaemonSet
→ 读取 /var/log/pods/*.log(容器标准输出)
→ 为日志添加 K8s Label(pod_name, namespace, container 等)
→ 通过 HTTP/gRPC 推送到 Loki Distributor
配置文件:
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
pipeline_stages:
# 添加 K8s 元数据标签
- labels:
pod: ""
namespace: ""
container: ""
# 解析 Docker 日志格式
- docker: {}
# 匹配多行日志(如 Java 异常栈)
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
# 丢弃不需要的日志
- drop:
expression: "healthcheck"
- job_name: kubernetes-events
pipeline_stages:
- json: {}
relabel_configs:
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
配置说明:
| 阶段 | 功能 | 说明 |
|---|---|---|
docker | Docker 日志格式解析 | 将 json 行拆分为多字段 |
cri | CRI 日志格式解析 | containerd 日志格式 |
regex | 正则提取字段 | 从日志内容提取自定义字段 |
json | JSON 解析 | 将 JSON 日志解析为字段 |
logfmt | logfmt 格式解析 | key=value 格式解析 |
multiline | 多行合并 | Java 异常栈、SQL 语句 |
drop | 丢弃匹配的日志 | 减少存储和传输 |
labels | 添加 Label | 为日志添加元数据 |
7 LogQL 如何实现日志指标化和告警?
答案:
LogQL 提供多种指标函数,可以将日志内容转换为 Prometheus 格式指标,并集成到告警规则。
日志指标函数:
| 函数 | 作用 | 示例 |
|---|---|---|
rate() | 日志产生速率 | rate({job="nginx"}[5m]) |
count_over_time() | 日志行计数 | count_over_time({job="nginx"}[5m]) |
bytes_rate() | 日志字节速率 | bytes_rate({job="nginx"}[5m]) |
bytes_over_time() | 日志字节总量 | bytes_over_time({job="nginx"}[5m]) |
absent_over_time() | 日志是否不存在 | absent_over_time({job="nginx"}[5m]) |
提取字段后指标化:
# 按状态码统计请求数
sum by (status) (count_over_time({job="nginx"} | json [5m]))
# 计算 P99 响应时间
quantile_over_time(0.99,
{job="nginx"} | json | unwrap duration [5m]
)
告警规则配置:
groups:
- name: loki-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate({job="nginx"} | json | status >= 500 [5m])) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "Nginx 5xx error rate > 10/s"
- alert: NoLogsForApp
expr: |
absent_over_time({job="myapp", namespace="production"}[10m])
for: 5m
labels:
severity: critical
annotations:
summary: "No logs from myapp for 10 minutes"
Grafana Alerting 集成:
Grafana → Loki 数据源 → LogQL 查询 → 基于日志的告警规则
8 Loki 的对象存储配置方式及推荐后端?
答案:
Loki 原生支持多种对象存储后端,日志数据和索引均可存储。
# 通用对象存储配置
storage_config:
# S3 / MinIO 兼容
aws:
s3: s3://region/bucket
endpoint: s3.amazonaws.com
access_key_id: AKIA...
secret_access_key: ...
insecure: false
s3forcepathstyle: true # MinIO 需要
# GCS
gcs:
bucket_name: loki-logs
service_account: /etc/gcs/sa.json
# Azure Blob
azure:
storage_account_name: lokilogs
storage_account_key: ...
container_name: loki
# 本地文件系统(单节点测试用)
filesystem:
directory: /tmp/loki/chunks
Schema 配置(多周期保留策略):
schema_config:
configs:
- from: "2020-10-01"
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
生产推荐:
| 规模 | 推荐后端 | 说明 |
|---|---|---|
| 单节点测试 | 本地文件系统 | 简单但不可持久 |
| 小规模(<1TB/天) | MinIO | 本地部署、S3 兼容 |
| 中大规模 | S3 / GCS | 稳定、低成本、无需运维 |
| 极大规模 | S3 + 生命周期策略 | 自动归档到 Glacier |
9 Loki 如何配置数据保留策略?
答案:
Loki 通过 Compactor 组件和 Table Manager 实现数据保留和清理。
# Compactor 保留策略
compactor:
working_directory: /data/loki/compactor
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 10
# 按天配置保留期
retention_rules:
# 命名空间粒度
- selector:
namespace: "production"
priority: 1
period: 30d
- selector:
namespace: "staging"
priority: 2
period: 14d
# 所有日志默认保留
- selector: {}
priority: 10
period: 7d
通过 Schema 配置保留:
# Table Manager 方式(旧版)
table_manager:
retention_deletes_enabled: true
retention_period: 720h # 30 天
Ingester 数据保留(Chunk 缓存):
ingester:
# 内存中 Chunk 的最长保留
max_chunk_age: 2h
# Chunk 上传对象存储后保留本地
# 本地保留用于快速查询
# 长期保留依赖对象存储
保留删除机制:
Compactor 定时扫描所有 Index/Chunk
→ 检查 retention_rules
→ 超期的数据标记删除
→ 清理对象存储中的已删除数据
10 Loki 的 Query Frontend 如何加速查询?
答案:
Query Frontend 作为查询入口,通过缓存、分片和并发优化加速查询。
query_frontend:
# 查询分片
max_outstanding_per_tenant: 2048
log_queries_longer_than: 5s
# 结果缓存
cache_results: true
results_cache:
cache:
# Redis 缓存
redis:
endpoint: redis:6379
expiration: 1h
# 查询分片配置
parallelize_shardable_queries: true
querier:
max_concurrent: 10
加速机制说明:
| 机制 | 说明 | 效果 |
|---|---|---|
| 查询分片 | 将大查询按时间或标签拆分为多个子查询并行执行 | 大规模日志查询秒级响应 |
| 结果缓存 | 相同查询的缓存命中直接返回 | 重复查询 10ms 级响应 |
| 查询重试 | Querier 失败自动重试 | 故障容错 |
| 主动取消 | 客户端取消时终止后端查询 | 节省资源 |
典型查询优化:
# ❌ 慢查询:大范围无过滤
{namespace="prod"} |= "error"
# ✅ 快查询:缩小时间范围 + 精确匹配
{namespace="prod", app="nginx"} |= "error"
# 也可以指定时间范围
{namespace="prod", app="nginx"} |= "500"
# 查询 Grafana 侧限定时间如 1h
11 Loki 如何处理多租户隔离?
答案:
Loki 通过租户 ID 实现数据和查询的隔离,多租户为可选项。
# 启用多租户模式
loki:
auth_enabled: true # 开启后每个请求必须携带 X-Scope-OrgID Header
写入时指定租户:
# Promtail 配置(每个租户独立实例)
clients:
- url: http://loki:3100/loki/api/v1/push
tenant_id: team-a
# 或 single tenant 模式
# 如果 Promtail 不能携带租户 ID,可通过代理添加 Header
查询时指定租户:
# Grafana 数据源中设置 Tenant ID
# 每个租户只能查自己的数据
# API 直接查询
curl -H "X-Scope-OrgID: team-a" http://loki:3100/loki/api/v1/query_range
多租户部署场景:
租户 A(生产团队):tenant_id = "prod-team"
租户 B(开发团队):tenant_id = "dev-team"
同一 Loki 集群,数据和查询完全隔离
全局管理员通过跳过租户检查查看所有数据
12 Loki 与 Grafana 的集成方式是什么?
答案:
Grafana 原生支持 Loki 数据源,配置简单并支持日志面板和变量联动。
数据源配置:
apiVersion: 1
datasources:
- name: Loki
type: loki
url: http://loki:3100
access: proxy
jsonData:
maxLines: 1000
derivedFields:
# 从日志提取字段跳转到 Tempo(链路追踪)
- name: traceID
type: string
matcherType: regex
matcherRegex: "trace_id=(\\w+)"
url: "$${__value.raw}"
datasourceName: Tempo
Grafana 日志面板特性:
| 特性 | 说明 |
|---|---|
| 日志卷 | 显示日志速率的时间序列图 |
| 实时流 | 查看实时日志流 |
| 高亮 | 匹配关键词高亮 |
| 显示上下文 | 查看选中日志行前后的上下文 |
| 日志详情 | 解析后的结构化字段展示 |
| 跳转到 Trace | 从 traceID 跳转到追踪系统 |
标签联动:
# Grafana 变量
- name: namespace
type: query
query: label_values(namespace)
- name: pod
type: query
query: label_values({namespace="$namespace"}, pod)
13 Loki 的 Ruler 组件作用是什么?
答案:
Loki Ruler 组件负责定期评估 LogQL 规则并触发告警,与 Alertmanager 集成。
ruler:
storage:
type: s3
s3:
bucket: loki-rules
rule_path: /tmp/loki/rules
alertmanager_url: http://alertmanager:9093
ring:
kvstore:
store: memberlist
# 规则存储
# 支持本地磁盘 / S3 / GCS
storage_config:
s3:
bucket: loki-rules
告警规则示例:
groups:
- name: loki-rules
rules:
- alert: HighErrorRate
expr: |
sum(rate({app="nginx"} | json | status >= 500 [5m])) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "High error rate for nginx"
Ruler 高可用:
多副本 Ruler 通过 Ring 协调
只有 Leader 执行规则评估
Leader 故障后自动切换
14 Loki 如何管理 Chunk 的生命周期?
答案:
Chunk 是 Loki 中日志存储的基本单位,经历从 Ingester 内存到对象存储的完整生命周期。
生命周期阶段:
1. 创建(Ingester 内存)
→ 接收 Distributor 分发的日志行
→ 写入内存 Chunk(包含压缩)
2. 刷盘(Flush)
→ Chunk 达到阈值(大小/时间/空闲)
→ 关闭 Chunk(不可变)
→ 压缩后上传到对象存储
→ 索引信息写入 TSDB
3. 查询
→ Querier 从 Ingester 读热数据(内存 Chunk)
→ Querier 从对象存储读历史数据(已刷盘的 Chunk)
4. 合并(Compactor)
→ 小 Chunk 合并为大 Chunk
→ 覆盖对象的元数据
5. 删除(Retention)
→ Compactor 根据保留策略删除过期 Chunk
Chunk 大小配置:
ingester:
chunk_target_size: 1572864 # 1.5MB(压缩后)
chunk_block_size: 262144 # 256KB
chunk_idle_period: 30m
chunk_retain_period: 5m
max_chunk_age: 2h
性能影响:
- Chunk 太小:对象存储请求多、Compactor 压力大
- Chunk 太大:内存占用高、刷盘延迟、查询需要扫描更多无用数据
15 Loki 的 Distributor 组件如何验证和处理写入请求?
答案:
Distributor 是 Log 写入的第一站,负责数据验证、去重和分发。
distributor:
ring:
kvstore:
store: memberlist
# 写入限制
ingester_heartbeat_timeout: 5s
rate_store:
max_parallel: 10
# 接收限制
receiever:
max_recv_msg_size: 4194304 # 4MB 最大消息
max_connection_age_grace: 0s
处理流程:
Promtail 推送日志流 → Distributor
→ 1. 验证 Label 格式(不能为空、不能超过标签数限制)
→ 2. 验证时间戳(不能太旧或太新)
→ 3. 验证日志行大小(单行不能超过 max_line_size)
→ 4. 将日志流散列到指定 Ingester(一致性哈希)
→ 5. 复制到 replication_factor 个 Ingester
→ 6. 至少半数 Ingester 写入成功即返回成功
限制配置:
distributor:
# 验证
extend_wal: true
max_line_size: 1048576 # 单行最大 1MB
max_label_name_length: 1024 # Label 名最大长度
max_label_value_length: 2048 # Label 值最大长度
max_label_names_per_series: 20 # 单日志流最多标签数
16 Loki 如何与 Prometheus 共享基础设施?
答案:
Loki 与 Prometheus 设计理念一致,可以共用 Grafana、Alertmanager 等组件。
统一 Grafana 数据源:
grafana:
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
- name: Loki
type: loki
url: http://loki:3100
jsonData:
derivedFields: # 日志 → 指标 联动
- datasourceName: Prometheus
matcherRegex: "namespace=(\\w+)"
name: namespace
url: '$${__value.raw}'
统一告警:
# 同一 Alertmanager 同时处理 Prometheus 和 Loki 的告警
# Prometheus 告警: rate(node_cpu_seconds_total[5m])
# Loki 告警: rate({app="nginx"}[5m])
alertmanager:
receivers:
- name: team-alerts
slack_configs:
- channel: '#alerts'
统一变量系统:
# Grafana 变量可以混合 Prometheus 和 Loki 数据源
- name: namespace
type: query
query: label_values(namespace) # Prometheus
# 或
query: label_values(namespace) # Loki
推广实践:
统一告警平台:Prometheus(指标) + Loki(日志) → Alertmanager
统一可视化:Prometheus(趋势图) + Loki(日志面板) → Grafana
统一服务发现:Prometheus 和 Promtail 使用相同的 K8s 元数据
17 Loki 的高可用部署方案是怎样的?
答案:
Loki 通过多副本 + 数据复制 + 微服务隔离实现高可用。
# 微服务模式(推荐生产)
loki:
structuredConfig:
ingester:
replication_factor: 3 # 每个日志流写入 3 个 Ingester
distributor:
ring:
kvstore:
store: memberlist
querier:
max_concurrent: 20
# Helm 多副本
ingester:
replicas: 3
resources:
requests:
memory: 4Gi
cpu: 2
distributor:
replicas: 2
querier:
replicas: 2
queryFrontend:
replicas: 2
compactor:
replicas: 1
单进程模式(测试/小规模):
# Single binary 模式(所有组件同一进程)
loki -config.file=loki.yaml -target=all
# 读写分离模式
loki -config.file=loki.yaml -target=read # Querier + QueryFrontend
loki -config.file=loki.yaml -target=write # Distributor + Ingester
故障场景:
| 故障 | 影响 | 恢复 |
|---|---|---|
| 1 个 Ingester 宕机 | 无影响(replication_factor=3) | 自动分配新 Ingester |
| 多数 Ingester 宕机 | 写入部分失败 | 需手动恢复 |
| Distributor 宕机 | 部分写入失败 | LB 直切健康节点 |
| Querier 宕机 | 部分查询失败 | 自动重试其他节点 |
18 Loki 如何通过 Helm Chart 部署到 K8s?
答案:
Loki 官方提供 Helm Chart,支持单进程和微服务模式。
# 添加仓库
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# 安装单进程模式(测试)
helm upgrade --install loki grafana/loki \
--namespace=loki --create-namespace \
--values single-binary-values.yaml
# 安装微服务模式(生产)
helm upgrade --install loki grafana/loki \
--namespace=loki --create-namespace \
--values microservices-values.yaml
生产 values.yaml 示例:
loki:
auth_enabled: false
commonConfig:
replication_factor: 3
storage:
type: s3
bucketNames:
chunks: loki-chunks
ruler: loki-ruler
admin: loki-admin
s3:
endpoint: minio.minio:9000
region: us-east-1
secretAccessKey: minio123
accessKeyId: minio
s3ForcePathStyle: true
insecure: true
ingester:
replicas: 3
persistence:
enabled: true
size: 10Gi
zoneAwareReplication:
enabled: false
distributor:
replicas: 2
querier:
replicas: 2
queryFrontend:
replicas: 2
compactor:
enabled: true
replicas: 1
monitoring:
dashboards:
enabled: true
alerts:
enabled: true
serviceMonitor:
enabled: true
19 Loki 的 boltdb-shipper 模式的工作原理是什么?
答案:
BoltDB Shipper 是 Loki v2.0 引入的索引模式,将索引也存储在对象存储中,简化架构。
Ingester 写入 Chunk → 上传到对象存储
→ 同时将 BoltDB 索引文件上传到对象存储
→ Querier 从对象存储下载索引文件
→ 基于本地缓存快速查询
不需要独立的 Index Gateway
每个 Querier 直接读取共享索引
工作原理:
# BoltDB Shipper 配置
storage_config:
boltdb_shipper:
active_index_directory: /data/loki/index
shared_store: s3
cache_location: /data/loki/boltdb-cache
cache_ttl: 24h # 索引缓存时间
工作流:
1. Ingester 每 15 分钟将 BoltDB 索引文件上传到 S3
2. Querier 定期从 S3 同步索引文件到本地缓存
3. 查询时优先查本地缓存,缓存未命中再从 S3 获取
4. 索引文件按时间分割(通常每 24h 一个索引)
BoltDB vs TSDB 模式:
| 维度 | BoltDB Shipper | TSDB |
|---|---|---|
| 索引格式 | BoltDB(key-value) | 自研 TSDB |
| 查询性能 | 中等(需加载全部索引) | 快(流式加载) |
| 内存使用 | 高(全量索引加载到内存) | 低(按需流式加载) |
| 支持版本 | v2.0+ | v2.8+ |
| 推荐 | 兼容过渡期 | 新部署首选 |
20 Loki 如何实现日志行多行合并(如 Java 异常栈)?
答案:
Promtail 的 multiline 阶段可以将多行日志合并为单条,解决异常栈等多行问题。
scrape_configs:
- job_name: java-app
pipeline_stages:
# 多行合并
- multiline:
# 第一行匹配:以日期开头(Java 异常栈起始标志)
firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
# 最大等待时间
max_wait_time: 3s
# 最大行数
max_lines: 128
# 解析后添加 Label
- labels:
app: "java-app"
常见多行模式:
# Java 异常栈
multiline:
firstline: '^\d{4}-\d{2}-\d{2}'
# Go panic
multiline:
firstline: '^panic:'
# Python 回溯
multiline:
firstline: '^Traceback'
# SQL 多行
multiline:
firstline: '^(SELECT|INSERT|UPDATE|DELETE)'
max_lines: 50
说明:
- 只有 Promtail 端支持 multiline,Loki 服务端不支持
max_wait_time控制等待后续行的最长时间max_lines控制一次合并的最大行数(防止单条日志过大)
21 Loki 的标签设计最佳实践是什么?
答案:
Loki 的标签设计直接影响查询性能和存储成本,需遵循与 Prometheus 类似的基数规则。
推荐标签:
| 标签 | 基数 | 说明 |
|---|---|---|
namespace | 低(<100) | 必选,K8s 命名空间 |
pod | 中(<10000) | 可选,Pod 级过滤 |
container | 低(<100) | 必选,容器名称 |
app | 低(<500) | 必选,应用名称 |
job | 低(<100) | Promtail scrape job |
level | 极低(<10) | 日志级别 |
禁止的标签:
# ❌ 高基数标签(禁止)
# request_id: "req_abc123" → 基数百万级
# user_id: "usr_12345" → 基数百万级
# session_id: "sess_abc" → 基数百万级
# ip: "10.0.0.1" → 基数万级
# ✅ 正确做法:用 LogQL 解析器提取
{app="nginx"} | json
# 提取 user_id 等字段但不作为索引标签
标签数量控制:
Loki 建议每个日志流 5-10 个标签
单条日志流标签数上限:20(可配置)
标签总数建议:< 100 万活跃 Label 组合
标签选择器优化:
# ❌ 高基数标签过滤→扫描大量数据
{pod="myapp-abc123"} # → 需要扫索引找该 Pod
# ✅ 先缩小范围再过滤
{namespace="prod", app="nginx"} |= "500"
22 Loki 如何通过 LogQL 实现日志采样分析?
答案:
LogQL 提供 rate、count_over_time、topk 等函数实现日志统计和采样。
# 1. 日志速率(每秒行数)
rate({namespace="prod", app="nginx"}[5m])
# 2. 按状态码统计
sum by (status) (
count_over_time({namespace="prod", app="nginx"} | json [5m])
)
# 3. Top 5 最慢请求
topk(5,
avg by (path) (
quantile_over_time(0.95,
{namespace="prod", app="nginx"}
| json
| unwrap response_time [5m]
)
)
)
# 4. 错误比例
sum(rate({namespace="prod", app="nginx"} | json | status >= 500 [5m]))
/ sum(rate({namespace="prod", app="nginx"} | json [5m]))
# 5. 日志采样(取前 100 条)
topk(100, {namespace="prod", app="nginx"} |= "error" | json | status)
# 6. 异常日志模式识别(pattern 解析)
{namespace="prod"} | pattern "<ip> - - <method> <path> <status> <size>" | status >= 500
聚合分析模式:
# 每 5 分钟错误趋势
sum by (namespace) (
count_over_time({namespace="prod", app="nginx"} | json | status >= 500 [5m])
)
# 请求路径分布
topk(10,
sum by (path) (
count_over_time({app="nginx"} | json [1h])
)
)
23 Loki 的 Compactor 组件如何处理索引压缩?
答案:
Compactor 是 Loki 的后台维护组件,负责索引合并和保留策略执行。
compactor:
working_directory: /data/loki/compactor
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 10
# TSDB 模式
tsdb:
# 索引压缩
compact_interval: 10m
# 索引过期
retention:
minimum_interval: 1h
# 对象存储
shared_store: s3
Compactor 工作流程:
1. 定期扫描对象存储中的索引文件
2. 合并小索引文件成大索引文件
3. 删除重复或过期的索引记录
4. 标记已删除的 Chunk(retention)
5. 清理对象存储中已标记删除的文件
6. 更新元数据
Compactor 的读写分离:
Compactor 是写入集中型组件
推荐独立部署,不与其他组件混部
需要足够的 CPU 和内存(索引合并是密集操作)
Compactor 的单副本设计(Leader 选举)
24 Loki 如何通过速率限制保护自身稳定性?
答案:
Loki 提供多层速率限制,防止过载写入或查询导致服务不稳定。
# 全局速率限制
loki:
limits_config:
# 每租户限制
ingestion_rate_mb: 10 # 写入速率限制(MB/s)
ingestion_burst_size_mb: 25 # 突发写入限制
max_streams_per_user: 10000 # 每租户日志流上限
max_global_streams_per_user: 0 # 全局日志流上限
# 查询限制
max_query_length: 721h # 最大查询时间范围(30天)
max_query_series: 500 # 单查询最大系列数
max_query_range: 0 # 最大范围查询跨度
# 行限制
max_line_size: 1048576 # 单行最大长度(1MB)
max_line_size_truncate: true # 超长截断而非拒绝
# 重试限制
max_entries_limit_per_query: 5000 # 单查询最大条数
拒绝行为:
写入超过 ingestion_rate_mb:
→ 返回 429 Too Many Requests
→ Promtail 自动重试(指数退避)
查询超过 max_query_length:
→ 返回 400 Bad Request
→ 提示缩小查询时间范围
25 Loki 的 LogQL 模式解析(Pattern)语法是什么?
答案:
Pattern 解析器将日志行中的变量字段提取出来,适用于结构固定但不完全是 JSON 的日志。
# 模式语法:<name> 表示提取字段
# 示例日志: "10.0.0.1 - GET /api/v1/users 200 1234"
# Pattern 解析
{job="nginx"}
| pattern "<ip> - - <method> <path> <status> <size>"
# 解析后可以用提取的字段过滤
| status >= 500
| ip != "127.0.0.1"
复杂模式示例:
# 原始日志: '[2024-01-01 12:00:00] [INFO] [request_id=abc123] user=admin action=login'
# Pattern 提取
| pattern "[<timestamp>] [<level>] [request_id=<request_id>] user=<user> action=<action>"
# 按用户统计动作
sum by (user, action) (
count_over_time({app="myapp"} | pattern "[<timestamp>] [<level>] [request_id=<request_id>] user=<user> action=<action>" [5m])
)
Pattern vs Regex:
# Pattern(更简单、性能更好)
| pattern "<ip> - - <method> <path> <status> <size>"
# Regex(更灵活、性能稍差)
| regex "(?P<ip>\\S+) - - (?P<method>\\S+) (?P<path>\\S+) (?P<status>\\d+) (?P<size>\\d+)"
Pattern 优势:
- 语法简洁,无需正则专业知识
- 执行性能优于 regex 解析器
- 适合 Nginx、Apache 等格式固定的日志
26 Loki 如何通过 `unwrap` 实现数值提取?
答案:
unwrap 是 LogQL 的数值提取操作,从日志行中提取数值字段用于指标计算。
# 1. 提取响应时间计算 P99
quantile_over_time(0.99,
{app="nginx"} | json | unwrap response_time [5m]
)
# 2. 按路径统计平均响应时间
avg by (path) (
avg_over_time(
{app="nginx"} | json | unwrap response_time [5m]
)
)
# 3. 字节量统计
sum by (method) (
sum_over_time(
{app="nginx"} | json | unwrap body_bytes [5m]
)
)
# 4. 最大值
max_over_time(
{app="nginx"} | json | unwrap upstream_time [5m]
)
# 5. 排序
sort(
avg_over_time(
{app="nginx"} | json | unwrap response_time [5m]
)
)
unwrap 要求:
- 提取的值必须是数值类型(int/float)
- 标签提取后的字段才能
unwrap - 可配合除错处理
__error__标签
27 Loki 如何处理日志采集端(Promtail/Docker/OTel)的差异?
答案:
Loki 支持多种采集端接入,各采集端的定位和功能差异如下。
| 采集端 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| Promtail | K8s 环境 | 原生 K8s 服务发现、Label 注入 | 仅支持 Loki |
| Docker Plugin | Docker 单机 | 部署简单 | 功能有限 |
| Fluent Bit | 轻量采集 | 资源消耗极低 | 需额外配置 |
| Fluentd | 复杂管道 | 插件丰富 | 资源消耗偏高 |
| Logstash | ELK 迁移 | 大量过滤器 | 重 |
| OpenTelemetry Collector | OTel 标准 | 统一采集管道 | 较新 |
Promtail 特有功能:
# K8s 自动服务发现
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
pipeline_stages:
- cri: {} # containerd 日志格式
- docker: {} # Docker 日志格式
- labels:
pod: ""
container: ""
namespace: ""
# 文件日志
- job_name: system-logs
static_configs:
- targets: [localhost]
labels:
job: varlogs
__path__: /var/log/*.log
Fluent Bit 接入:
output:
name: loki
match: *
host: loki
port: 3100
labels: job=fluentbit, app=$TAG
label_keys: $TAG
auto_kubernetes_labels: on
28 Loki 查询时遇到 429 错误是什么原因?如何解决?
答案:
429 错误表示请求速率超过 Loki 配置的速率限制。
原因分析:
| 原因 | 说明 | 表现 |
|---|---|---|
| 写入速率超限 | 超过 ingestion_rate_mb | Distributor 返回 429 |
| 查询并发超限 | 超过 max_concurrent | Query Frontend 返回 429 |
| 日志流超限 | 超过 max_streams_per_user | Distributor 拒绝新流 |
解决方法:
# 1. 增大速率限制
limits_config:
ingestion_rate_mb: 20
ingestion_burst_size_mb: 50
max_streams_per_user: 20000
max_concurrent: 30
# 2. 客户端调整(Promtail)
clients:
# 启用写缓冲
backoff_config:
min_period: 100ms
max_period: 10s
max_retries: 10
# 本地缓冲(网络故障时)
# 默认开启,缓冲目录 /tmp/promtail
监控速率限制:
# 被拒绝的写入请求
rate(loki_discarded_samples_total[5m])
# 速率限制器状态
loki_ingester_rate_limit_logs_per_second
29 Loki 与 ELK 在 K8s 环境下的选型对比?
答案:
| 维度 | Loki | ELK(Elasticsearch + Logstash + Kibana) |
|---|---|---|
| 部署复杂度 | 简单(Helm Chart) | 复杂(需要配置 ES 集群、分片) |
| 资源消耗 | 低-中 | 高(ES 多副本、内存密集型) |
| 存储成本 | 极低(对象存储) | 高(SSD 本地存储) |
| 查询语言 | LogQL(简洁) | Lucene(功能完整但复杂) |
| 全文搜索 | 有限 | 强大 |
| K8s 原生 | 原生(Promtail) | 需额外采集端(Filebeat) |
| 扩容 | 简单(微服务、水平扩展) | 需要管理分片和 rebalance |
| 可视化 | 依赖 Grafana | 内置 Kibana |
| 社区 | CNCF 项目 | Elastic 公司主导 |
| 典型场景 | K8s 日志、DevOps | 企业搜索、安全分析 |
选型建议:
K8s 环境、已有 Grafana、日志量大但查询简单 → Loki
需要全文搜索、复杂分析、企业合规要求 → ELK
混合部署:Loki 做运维日志快速查询,ELK 做审计/全文检索
30 Loki 的典型告警规则有哪些?
答案:
groups:
- name: loki-alerts
rules:
- alert: LokiLogHighErrorRate
expr: |
sum(rate({app="nginx"} | json | status >= 500 [5m])) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High error rate in nginx logs"
description: "Error rate is XQOPEN $value XQCLOSE/s"
- alert: LokiLogPanicDetected
expr: |
count_over_time({namespace="production"} |= "panic" [5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "Panic detected in production"
description: "Application panic found in logs"
- alert: LokiLogNoOutput
expr: |
absent_over_time({app="myapp", namespace="production"}[15m])
for: 5m
labels:
severity: critical
annotations:
summary: "No logs from myapp for 15 minutes"
- alert: LokiLogSlowRequests
expr: |
max_over_time(
{app="nginx"} | json | unwrap request_time [5m]
) > 5
for: 5m
labels:
severity: warning
annotations:
summary: "Slow requests detected"
Loki 自身告警:
- alert: LokiRequestErrors
expr: |
rate(loki_request_duration_seconds_count{status_code=~"5.."}[5m]) > 0.01
for: 5m
labels:
severity: warning
- alert: LokiDiskFull
expr: |
(loki_ingester_disk_usage_bytes / loki_ingester_disk_limit_bytes) > 0.9
for: 5m
labels:
severity: critical