Loki 中文教程

1. 项目概述

Loki 是一个开源的日志聚合系统,由 Grafana Labs 开发,专为云原生环境设计。它采用了与 Prometheus 类似的设计理念,提供了高效、可扩展的日志存储和查询解决方案。

主要功能

  • 基于标签的日志索引:与 Prometheus 类似,使用标签进行日志索引和查询
  • 高效存储:只索引元数据,原始日志数据压缩存储
  • 与 Prometheus 和 Grafana 无缝集成
  • 水平可扩展:支持分布式部署
  • 多租户支持:适合大型组织和多团队环境
  • 丰富的查询语言:支持 LogQL 进行复杂的日志查询和分析
  • 支持多种数据源:通过 Promtail、Docker 驱动等采集日志

技术栈特点

  • 用 Go 语言编写,性能优异
  • 基于 Apache Cassandra 或 Amazon DynamoDB 存储索引
  • 基于对象存储(如 S3、GCS)存储原始日志
  • 与 Kubernetes 深度集成

适用环境

  • 云原生环境日志管理
  • 容器化应用日志聚合
  • 微服务架构日志分析
  • 大型分布式系统日志处理

2. 安装与配置

2.1 完整安装(Loki + Promtail + Grafana)

使用 Docker Compose 安装完整的 Loki 栈:

# docker-compose.yml
version: "3"
services:
  loki:
    image: grafana/loki:2.8.0
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yaml:/etc/loki/local-config.yaml
    command: -config.file=/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:2.8.0
    volumes:
      - ./promtail-config.yaml:/etc/promtail/config.yml
      - /var/log:/var/log
    command: -config.file=/etc/promtail/config.yml

  grafana:
    image: grafana/grafana:9.1.0
    ports:
      - "3000:3000"
    volumes:
      - grafana-storage:/var/lib/grafana

volumes:
  grafana-storage:

2.2 Loki 配置

# loki-config.yaml
auth_enabled: false

server:
  http_listen_port: 3100

ingester:
  lifecycler:
    address: 127.0.0.1
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1

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: /tmp/loki/boltdb-shipper-active
    cache_location: /tmp/loki/boltdb-shipper-cache
    cache_ttl: 24h
    shared_store: filesystem
  filesystem:
    directory: /tmp/loki/chunks

compactor:
  working_directory: /tmp/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

2.3 Promtail 配置

# promtail-config.yaml
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_name: docker
    docker_sd_configs:
    - host: unix:///var/run/docker.sock
      refresh_interval: 5s
    relabel_configs:
    - source_labels: ['__meta_docker_container_name']
      regex: '/(.*)'
      target_label: 'container'
    - source_labels: ['__meta_docker_container_log_stream']
      target_label: 'stream'
    - source_labels: ['__meta_docker_container_image']
      target_label: 'image'

2.4 Kubernetes 安装

使用 Helm 安装 Loki 栈:

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install loki grafana/loki-stack --set grafana.enabled=true

3. 基本使用

3.1 启动 Loki 栈

docker-compose up -d

3.2 访问 Grafana

Loki 栈启动后,可以通过 http://localhost:3000 访问 Grafana Web UI。默认用户名和密码都是 admin

3.3 添加 Loki 数据源

  1. 登录 Grafana Web UI
  2. 点击左侧菜单的 "配置" -> "数据源"
  3. 点击 "添加数据源"
  4. 选择 "Loki"
  5. 配置数据源 URL 为 http://loki:3100
  6. 点击 "保存并测试"

3.4 基本日志查询

在 Grafana 的 "Explore" 页面,选择 Loki 数据源,使用 LogQL 进行日志查询:

# 查询所有日志
{job="varlogs"}

# 按容器名称过滤日志
{container="nginx"}

# 搜索包含特定文本的日志
{job="varlogs"} |= "error"

# 搜索不包含特定文本的日志
{job="varlogs"} != "debug"

# 搜索正则匹配的日志
{job="varlogs"} =~ "[Ee]rror"

# 组合多个条件
{job="varlogs", host="localhost"} |= "error" != "debug"

3.5 日志可视化

在 Grafana 中创建日志仪表盘:

  1. 点击左侧菜单的 "+" -> "创建仪表盘"
  2. 点击 "添加新面板"
  3. 选择 Loki 数据源
  4. 编写 LogQL 查询
  5. 选择 "日志" 可视化类型
  6. 配置面板选项
  7. 点击 "应用"

4. 高级特性

4.1 LogQL 高级查询

4.1.1 范围查询

# 查询过去5分钟的错误日志
{job="varlogs"} |= "error" |~ "[0-9]{3}" [5m]

# 查询过去1小时的日志,按时间聚合
sum(count_over_time({job="varlogs"} |= "error" [5m])) by (level) [1h]

4.1.2 聚合函数

# 计算每分钟的错误数
sum(count_over_time({job="varlogs"} |= "error" [1m])) by (level)

# 计算日志行数的百分位数
topk(5, sum(count_over_time({job="varlogs"} [5m])) by (container))

# 计算日志大小的直方图
histogram_over_time(sum(bytes_over_time({job="varlogs"} [5m])) by (container), 1000, 0, 10000)

4.1.3 提取字段

# 提取 HTTP 状态码并计算每个状态码的出现次数
sum(count_over_time(
  {job="varlogs"} |= "HTTP" 
  | regexp `HTTP/1\.1\" (?P<status>\d{3})` 
  [5m]
)) by (status)

4.2 多租户配置

# loki-config.yaml 中的多租户配置
auth_enabled: true

server:
  http_listen_port: 3100

limits_config:
  enforce_metric_name: false
  reject_old_samples: true
  reject_old_samples_max_age: 168h
  per_stream_rate_limit: 3MB
  per_stream_rate_limit_burst: 10MB
  ingestion_rate_mb: 15
  ingestion_burst_size_mb: 20

chunk_store_config:
  max_look_back_period: 0s

ingester:
  lifecycler:
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1

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: /tmp/loki/boltdb-shipper-active
    cache_location: /tmp/loki/boltdb-shipper-cache
    cache_ttl: 24h
    shared_store: filesystem
  filesystem:
    directory: /tmp/loki/chunks

compactor:
  working_directory: /tmp/loki/boltdb-shipper-compactor
  shared_store: filesystem

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

4.3 告警配置

在 Loki 中配置基于日志的告警:

# /etc/loki/rules/alerts.yaml
groups:
- name: example
  rules:
  - alert: HighErrorRate
    expr: |
      sum(count_over_time({job="varlogs"} |= "error" [5m])) by (job) > 10
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High error rate detected"
description: "Job {{ $labels.job }} has {{ $value }} errors in the last 5 minutes"

4.4 性能优化

4.4.1 存储优化

# 存储优化配置
storage_config:
  boltdb_shipper:
    active_index_directory: /tmp/loki/boltdb-shipper-active
    cache_location: /tmp/loki/boltdb-shipper-cache
    cache_ttl: 24h
    shared_store: s3
  aws:
    s3:
      bucketnames: loki-logs
      endpoint: s3.amazonaws.com
      region: us-east-1
      access_key_id: ${AWS_ACCESS_KEY_ID}
      secret_access_key: ${AWS_SECRET_ACCESS_KEY}

4.4.2 摄入优化

# 摄入优化配置
limits_config:
  ingestion_rate_mb: 30
  ingestion_burst_size_mb: 40
  per_stream_rate_limit: 5MB
  per_stream_rate_limit_burst: 15MB

ingester:
  chunk_idle_period: 3m
  max_chunk_age: 1h
  chunk_target_size: 1536000
  chunk_retain_period: 1m
  max_transfer_retries: 0

5. 最佳实践

5.1 标签设计

  • 使用有意义的标签名称,遵循 namespace_subsystem 格式
  • 避免使用高基数标签(如用户 ID、会话 ID 等)
  • 为所有日志流添加统一的基础标签(如环境、应用名称、版本等)
  • 标签值应保持一致,避免大小写混用

5.2 日志格式

  • 使用结构化日志格式(如 JSON),便于字段提取和分析
  • 包含必要的上下文信息(如请求 ID、用户 ID、时间戳等)
  • 统一日志级别格式(如 debug、info、warn、error)
  • 避免在日志中包含敏感信息(如密码、API 密钥等)

5.3 采集配置

  • 为不同类型的应用配置不同的采集作业
  • 使用 relabel_configs 优化标签
  • 配置适当的批处理大小和刷新间隔
  • 监控 Promtail 的健康状态

5.4 查询优化

  • 使用具体的标签过滤减少查询范围
  • 避免使用 |= 操作符在大量日志中搜索
  • 使用时间范围限制减少查询数据量
  • 对于频繁的查询,考虑使用 Loki 的查询缓存

5.5 存储管理

  • 配置适当的保留策略
  • 定期运行压缩以优化存储
  • 监控存储使用情况
  • 考虑使用对象存储服务降低存储成本

6. 实际应用场景

6.1 Kubernetes 集群日志管理

使用 Loki 管理 Kubernetes 集群日志:

配置 Promtail 采集 Kubernetes 日志:

# promtail-config.yaml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

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

kubernetes_sd_configs:
- role: pod

relabel_configs:
- source_labels: [__meta_kubernetes_pod_namespace]
  action: replace
  target_label: namespace

- source_labels: [__meta_kubernetes_pod_name]
  action: replace
  target_label: pod

- source_labels: [__meta_kubernetes_container_name]
  action: replace
  target_label: container

- source_labels: [__meta_kubernetes_pod_label_app]
  action: replace
  target_label: app

- replacement: /var/log/pods/*$1/*.log
  separator: /
  source_labels: [__meta_kubernetes_pod_uid, __meta_kubernetes_container_name]
  regex: ^([^/]+)/.*$  # 替换为 /var/log/pods/<pod_uid>/<container_name>/*.log
  target_label: __path__

查询 Kubernetes 日志:

# 查询特定命名空间的日志
{namespace="default"}

# 查询特定应用的日志
{app="nginx"}

# 查询特定容器的错误日志
{container="app"} |= "error"

# 统计每个应用的错误数
sum(count_over_time({namespace="default"} |= "error" [5m])) by (app)

6.2 微服务架构日志分析

使用 Loki 分析微服务架构的日志:

结构化日志示例(JSON 格式):

{
  "timestamp": "2023-06-01T12:00:00Z",
  "level": "error",
  "service": "user-service",
  "version": "1.0.0",
  "trace_id": "abc123",
  "span_id": "def456",
  "message": "Failed to fetch user data",
  "error": "User not found",
  "user_id": "12345",
  "method": "GET",
  "path": "/api/users/12345",
  "status_code": 404,
  "response_time": 150
}

查询结构化日志:

# 解析 JSON 日志并提取字段
{service="user-service"} |= "error" | json

# 按错误类型统计
{service="user-service"} |= "error" | json | sum(count_over_time({service="user-service"} [5m])) by (error)

# 查找响应时间超过 500ms 的请求
{service="user-service"} | json | response_time > 500

# 跟踪特定请求的完整调用链
{trace_id="abc123"} | json | sort by timestamp

6.3 安全事件监控

使用 Loki 监控安全事件:

查询安全相关日志:

# 查找认证失败事件
{service="auth-service"} |= "authentication failed"

# 查找越权访问事件
{service="api-gateway"} |= "unauthorized access"

# 查找异常登录位置
{service="auth-service"} |= "login" | json | country != "CN"

# 统计认证失败次数
sum(count_over_time({service="auth-service"} |= "authentication failed" [1h])) by (ip_address)

7. 总结

Loki 是一个现代化、高效的日志聚合系统,特别适合云原生环境和容器化应用。它采用了与 Prometheus 类似的设计理念,通过标签索引和压缩存储,提供了高性能、低成本的日志管理解决方案。

关键优势

  • 高效存储:只索引元数据,原始日志压缩存储
  • 与 Grafana 无缝集成:提供丰富的可视化和分析能力
  • 强大的查询语言:LogQL 支持复杂的日志查询和分析
  • 水平可扩展:支持分布式部署,适合大型系统
  • 多租户支持:适合大型组织和多团队环境

应用场景

  • Kubernetes 集群日志管理
  • 容器化应用日志聚合
  • 微服务架构日志分析
  • 安全事件监控
  • 业务指标分析

Loki 与 Prometheus 和 Grafana 一起,构成了完整的可观测性解决方案,帮助您更好地理解系统状态,及时发现和解决问题。

« 上一篇 Grafana 中文教程 下一篇 » Jaeger 中文教程