Grafana Loki是云原生环境下日志聚合的主流方案,相比传统ELK Stack的全文索引方式,Loki仅对日志标签建立索引,大幅降低了存储成本和运维复杂度。在Kubernetes日志采集、微服务日志分析和故障排查场景中,Loki与Promtail、Grafana的集成方案已成为DevOps标准技术栈。本文介绍Loki的部署配置、日志采集、查询和告警的完整流程。
Loki架构设计与核心组件解析
Loki的架构分为读写分离两部分:
Distributor(分发器):接收日志写入请求,对日志流做哈希分片,分发到多个Ingester节点。负责校验标签合法性和数据冗余。
Ingester(采集器):缓存最近写入的日志数据,定期压缩并刷入后端存储。是写入路径的核心组件,内存中维护未压缩和压缩中的日志块。
Querier(查询器):处理日志查询请求,先从Ingester获取实时数据,再从后端存储加载历史数据。查询器内部通过查询前端做查询拆分和负载均衡。
Compactor(压缩器):对历史日志块做合并压缩,减少小文件数量,提升查询效率。
Loki支持多种后端存储:文件系统(开发测试)、S3兼容对象存储(生产推荐)、GCS、Azure Blob。生产环境建议使用S3兼容存储(如MinIO),成本远低于本地磁盘且支持水平扩展。
Loki分布式部署配置实战
生产环境推荐使用分布式模式部署。以下是基于Helm的部署配置:
# 添加Grafana Helm仓库
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# 创建loki-values.yaml
cat << 'EOF' > loki-values.yaml
loki:
commonConfig:
replication_factor: 3
storage:
type: s3
s3:
endpoint: minio.observability.svc:9000
bucketnames: loki-chunks
access_key_id: minio
secret_access_key: minio123
s3forcepathstyle: true
schemaConfig:
configs:
- from: 2024-01-01
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
singleBinary:
replicas: 0
# 分布式组件
distributor:
replicas: 2
ingester:
replicas: 3
persistence:
enabled: true
size: 50Gi
querier:
replicas: 2
max_concurrent: 8
query_frontend:
replicas: 2
compactor:
replicas: 1
persistence:
enabled: true
size: 10Gi
# 资源限制
ingester resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2
memory: 4Gi
EOF
helm install loki grafana/loki-distributed -n observability -f loki-values.yaml
schema配置使用v13版本,采用TSDB索引,相比旧的v11 boltdb-shipper索引,查询性能更好且支持更高效的标签过滤。replication_factor设为3保证数据高可用。
Promtail日志采集Agent配置
Promtail是Loki的日志采集Agent,部署在每个需要采集日志的节点上。以下配置展示了Kubernetes环境下的日志采集方案:
# promtail-config.yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki-gateway.observability.svc:80/loki/api/v1/push
tenant_id: default
scrape_configs:
# 采集Kubernetes Pod日志
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
# 添加namespace标签
- source_labels: ["__meta_kubernetes_namespace"]
target_label: namespace
# 添加pod名称标签
- source_labels: ["__meta_kubernetes_pod_name"]
target_label: pod
# 添加容器名称标签
- source_labels: ["__meta_kubernetes_pod_container_name"]
target_label: container
# 通过annotation控制是否采集
- action: keep
source_labels: ["__meta_kubernetes_pod_annotation_loki_collect"]
regex: "true"
# 采集应用结构化JSON日志
- job_name: app-json-logs
static_configs:
- targets: [localhost]
labels:
job: app-server
env: production
__path__: /var/log/app/*.json
pipeline_stages:
# 解析JSON格式日志
- json:
expressions:
level: level
trace_id: trace_id
service: service
message: message
duration_ms: duration_ms
# 提取level作为标签
- labels:
level:
service:
# 解析时间戳
- timestamp:
source: timestamp
format: RFC3339
pipeline_stages是Promtail的核心配置,支持json、regex、template等多种处理阶段。将高频过滤字段(如level、service)提取为标签,可以大幅提高查询效率。但标签基数要控制——user_id、request_id这类高基数字段不适合做标签,应保留在日志内容中通过全文搜索过滤。
LogQL查询语言语法与实战
LogQL(Log Query Language)是Loki的查询语言,语法分为日志流选择器、过滤器和聚合函数三部分:
# 基本日志流选择器
{namespace="production", app="api-server"}
# 添加正则过滤
{namespace="production", app="api-server"} |= "ERROR"
# 多条件过滤:包含ERROR且不包含DEBUG
{namespace="production", app="api-server"} |= "ERROR" != "DEBUG"
# 正则匹配
{namespace="production"} |~ "panic|fatal|timeout"
# JSON字段提取与过滤
{app="api-server"} | json | level="ERROR" | duration_ms > 1000
# 日志行格式化提取特定字段
{app="api-server"} | json | line_format "{{.timestamp}} [{{.level}}] {{.service}}: {{.message}}"
# 统计每分钟ERROR日志数量
sum(count_over_time({app="api-server"} |= "ERROR" [1m]))
# 按服务分组统计错误率
sum by (service) (count_over_time({level="ERROR"} [5m]))
/ sum by (service) (count_over_time({app="api-server"} [5m])) * 100
# 计算请求延迟P99分位数
quantile_over_time(0.99,
{app="api-server"} | json | unwrap duration_ms [5m]
) by (service)
unwrap关键字用于从日志中提取数值字段做聚合计算,适合在日志中记录了延迟、状态码等指标的场景,可以替代部分Prometheus指标的作用。
Grafana日志可视化与告警规则配置
Grafana内置Loki数据源支持,配置好数据源后可以直接在Dashboard中展示日志:
添加Loki数据源时,URL填入Loki Gateway地址,如 http://loki-gateway.observability.svc:80。在Dashboard中添加Log Panel,输入LogQL查询即可实时展示日志。
Grafana Alerting支持基于LogQL的告警规则:
# alerting-rules.yaml
groups:
- name: loki-alerts
rules:
# ERROR日志激增告警
- alert: HighErrorRate
expr: |
sum by (service) (
count_over_time({level="ERROR"}[5m])
) > 100
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.service }} ERROR日志激增"
description: "过去5分钟内{{ $labels.service }}产生超过100条ERROR日志"
# 服务无日志输出(可能宕机)
- alert: NoLogsReceived
expr: |
sum by (service) (
count_over_time({app="api-server"}[10m])
) == 0
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.service }} 10分钟无日志输出"
description: "服务可能已停止运行"
# 高延迟请求告警
- alert: HighLatencyRequests
expr: |
quantile_over_time(0.99,
{app="api-server"} | json | unwrap duration_ms [5m]
) > 2000
for: 5m
labels:
severity: warning
annotations:
summary: "API请求P99延迟超过2秒"
description: "过去5分钟内P99延迟持续超过2000ms"
告警规则可以路由到Alertmanager,通过邮件、Slack、钉钉等渠道通知。Loki告警相比Prometheus告警的优势在于可以直接基于日志内容做触发条件,不需要应用额外暴露指标。例如排查线上问题时在日志中打印特定标记,通过LogQL告警即可快速建立临时监控。
Loki的标签设计是系统成败的关键。标签数量过多会膨胀索引,高基数字段做标签会导致OOM。建议标签控制在10个以内,仅保留namespace、app、env、level这类低基数维度。查询时尽量用标签缩小范围,再做日志内容过滤,减少扫描的数据量。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/grafanaloki-ri-zhi-ju-he-xi-tong-bu-shu-shi-zhan-yu-jie-gou/