Loki 部署使用

Loki 是 Grafana 体系里的日志聚合系统,设计思路和 Prometheus 很像,但处理对象是日志。它和传统全文检索型日志系统的最大区别是: 默认只索引标签(labels),不索引整条日志正文 ,所以存储成本通常更低,扩展性也更好,特别适合 Kubernetes、Docker、主机系统日志、应用日志统一汇聚场景。

截至 2026-04-16,Promtail 已经 EOL,新的采集端优先建议使用 Grafana Alloy ;另外,Loki 本身不内置认证层,生产环境应放在 Nginx 等反向代理或网关之后。

它的典型链路是:

日志源 → 采集器(Alloy)→ Loki → Grafana 查询与展示

其中:

  • Loki 负责接收、压缩、存储、查询日志。
  • Alloy 负责在主机或 K8s 节点上收集日志、打标签、做预处理,再推送到 Loki。
  • Grafana 负责可视化、检索、告警。

Loki 核心架构逻辑上常见有这些组件:

  • Distributor :接收写入请求
  • Ingester :缓存并写入日志块
  • Querier :执行查询
  • Query Frontend :查询拆分与缓存
  • Compactor :压缩、整理、保留策略
  • Index / Storage :索引和日志对象存储

标签设计建议

Loki 的性能很大程度取决于标签设计,因此标签设计非常关键。

建议保留低基数标签

  • job
  • host
  • env
  • app
  • namespace
  • pod
  • container

不要把高基数内容做成标签

  • user_id
  • request_id
  • trace_id
  • URL 全路径
  • 错误详情
  • SQL 文本

官方文档强调 labels 是 Loki 的核心组织方式,但标签过多、基数过高会带来性能和成本问题。

Docker Compose 部署示例

  • Loki version 3.7.1
  • alloy, version v1.15.1
docker-compose.yaml
services:
loki:
image: grafana/loki:latest
command:
- '-config.file=/etc/loki/config.yaml'
ports:
- "3100:3100"
volumes:
- "./config/loki/:/etc/loki/:ro"
- "./data/loki/:/data/loki"

grafana:
image: grafana/grafana
ports:
- "3000:3000"
volumes:
- "./config/grafana/:/etc/grafana/"
- "./data/grafana/:/var/lib/grafana/"


alloy:
image: grafana/alloy
container_name: alloy
restart: unless-stopped
ports:
- "12345:12345"
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- --storage.path=/var/lib/alloy/data
- /etc/alloy/config.alloy
volumes:
- ./config/alloy/:/etc/alloy/:ro
- ./data/alloy/:/var/lib/alloy/data
- /var/run/docker.sock:/var/run/docker.sock

主配置文件 /etc/loki/config.yaml 内容参考:

/etc/loki/config.yaml
auth_enabled: false

server:
http_listen_port: 3100

common:
path_prefix: /loki
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

storage_config:
filesystem:
directory: /loki/chunks

limits_config:
allow_structured_metadata: true
retention_period: 168h

compactor:
working_directory: /loki/compactor
retention_enabled: true
delete_request_store: filesystem

Alloy 配置文件参考:

config/alloy/config.alloy
logging {
level = "info"
format = "logfmt"
}

discovery.docker "containers" {
host = "unix:///var/run/docker.sock"
}

discovery.relabel "docker_logs" {
targets = discovery.docker.containers.targets

rule {
source_labels = ["__meta_docker_container_name"]
target_label = "container"
regex = "/(.*)"
replacement = "$1"
}

rule {
source_labels = ["__meta_docker_container_log_stream"]
target_label = "stream"
}

rule {
source_labels = ["__meta_docker_container_label_com_docker_compose_service"]
target_label = "service"
}
}

loki.source.docker "containers" {
host = "unix:///var/run/docker.sock"
targets = discovery.relabel.docker_logs.output
forward_to = [loki.write.local.receiver]
}

loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}

external_labels = {
host = "prometheus-jumpserver",
env = "prod",
}
}

启动成功后,在 Grafana 中添加新的 Data Source。即可在 Grafana 中查看到日志。

配置文件详解

Loki 配置文件

  • Loki version 3.7.1

以下配置适用于单机、单副本、本地文件系统、保留 7 天数据的 Liki

/etc/loki/config.yaml
auth_enabled: false

server:
http_listen_port: 3100 # Loki 的 HTTP 服务监听端口。Grafana、Alloy 或自己用 curl 查询 Loki API 时,默认都会连这个端口。官方配置文档把 server 作为 Loki 进程网络与服务行为的基础配置入口。

common: # Loki 多组件共用的基础参数(通用运行参数)。
path_prefix: /loki # Loki 的本地工作目录前缀。很多组件的临时文件、索引缓存、压缩过程中的文件、规则文件等,都可能基于这个路径展开。官方示例中常见把它设成 /loki。在 Docker 部署中需要持久化
replication_factor: 1 # 只保留 1 份副本。这适合单机或非高可用测试环境。官方示例中单机 / 简化部署也常用 1。想要高可用,通常需要 >1,并配套 ring/多节点/对象存储
ring: # ring 是分布式 Loki 用来协调实例状态、分片、接收写入等的关键机制。
kvstore:
store: inmemory # 表示 Loki 的 ring 元数据存储在内存里。好处是简单,坏处是进程重启后状态不持久,也不适合多实例生产。

schema_config: # 存储 schema 定义。这部分是 Loki 最关键的配置之一。schema 是按时间段生效的,Loki 会根据日志时间决定使用哪套 schema 来存储和查询数据。
configs:
- from: "2024-01-01" # 表示从 2024-01-01 开始写入的数据,使用下面这一整套 schema。from 是时间分界点;如果后面再新增一条新 schema,比如 from: "2026-05-01",那么之后的数据就会按新 schema 存。
store: tsdb # 使用 TSDB 索引。从 Loki 2.8 起,TSDB 是推荐的索引方式,更高效、更快、可扩展性更好。
object_store: filesystem # 日志块和相关对象数据存到本地文件系统,而不是 S3、GCS、Azure Blob 之类的对象存储。正式环境应该优先使用 S3 / MinIO / GCS 等对象存储
schema: v13 # 这是 schema 版本。官方升级文档明确说,Structured Metadata 要求 active schema 同时使用 tsdb 和 v13;迁移文档也把 v13 作为推荐版本。
index:
prefix: index_ # 索引前缀名。 Loki 会按自己的规则生成索引对象名或目录名,这个前缀就是命名的开头。
period: 24h # 这是非常关键的参数,表示索引按 24 小时切分。官方 retention 文档明确说明,retention 仅支持 24h index period。

storage_config: # 底层存储配置
filesystem: # schema 配置了 object_store: filesystem,就需要给 filesystem 指定目录。
directory: /loki/chunks # 这个目录用于保存 Loki 的日志块数据。简单说,日志正文压缩后的 chunk 文件会在这里。你容器删除或这个目录丢失,就会失去本地日志数据。

limits_config: # 限制与功能开关。控制 Loki 的一些行为限制和特性。官方升级文档、structured metadata 文档以及 retention 相关文档都涉及这里的配置项。
allow_structured_metadata: true # 允许 Loki 使用 Structured Metadata(某些字段可以作为结构化元数据随日志返回)。官方说明,Structured Metadata 在 Loki 3.0 默认已启用,但前提是你的 active schema 必须是 TSDB + v13;如果不是,就需要关闭或迁移。
retention_period: 168h # 全局日志保留 168 小时,也就是 7 天。这个只是“希望保留多久”,真正执行删除还依赖 compactor 配置正确,并且索引周期满足要求

compactor: # 压缩与保留删除执行者,Compactor 不只是做压缩,也负责 retention 相关处理。
working_directory: /loki/compactor # compactor 的工作目录,用来存放压缩、保留、删除流程中的临时文件或处理中间状态。
retention_enabled: true # 表示打开 retention 机制。没有这个,即使你写了 retention_period,compactor 也只会做压缩,不会执行基于保留期的删除。
delete_request_store: filesystem # 若启用 retention,必须配置 delete_request_store;它决定删除请求存在哪种后端里。

Alloy 配置文件详解

  • alloy, version v1.15.1

Alloy 配置文件本质上是在描述一条 数据流水线

  • 发现目标 → 读取数据 → 处理/加标签 → 写到 Loki / Prometheus / OTLP 等后端

Alloy 的配置语法由两类基础元素组成:attributes(属性) 和 blocks(块) 。属性用 key = value 赋值,块用来定义组件实例和嵌套配置。

Alloy 官方把一些顶层块称为 configuration blocks ,它们用于配置 Alloy 进程本身,不是在采集业务日志或者输出日志。比如:

  • logging
  • tracing
  • http
  • livedebugging
  • import.*

Linux 下 Alloy 默认配置文件路径是 /etc/alloy/config.alloy

logging {                      # 配置 Alloy 自己的日志
level = "info" # Alloy 自己输出 info 级别日志
format = "logfmt" # 输出为 logfmt 格式
}

discovery.docker "containers" { # 发现 Docker 容器,discovery.docker 会把每个容器映射成 target。
host = "unix:///var/run/docker.sock" # 连接 Docker daemon、从 Docker 中发现容器目标、暴露出一组可供下游使用的 targets。要生效,你的容器通常要挂载 /var/run/docker.sock
}

discovery.relabel "docker_logs" { # 重写或补充标签。把 Docker 自动发现到的元标签,转成在 Loki/Grafana 里更好用的业务标签。labels 是 Loki 的核心组织方式,必须谨慎设计,避免高基数标签。
targets = discovery.docker.containers.targets #

rule {
source_labels = ["__meta_docker_container_name"]
target_label = "container"
regex = "/(.*)"
replacement = "$1"
}

rule {
source_labels = ["__meta_docker_container_label_com_docker_compose_service"]
target_label = "service"
}
}

loki.source.docker "containers" { # 读取 Docker 日志,这是日志采集核心。
host = "unix:///var/run/docker.sock" # 连接哪个 Docker daemon
targets = discovery.docker.containers.targets # 从哪里拿目标列表,从 discovery.docker "containers" 获取目标容器列表
forward_to = [loki.write.local.receiver] # 日志下一步发给谁。把当前读到的日志,发给 loki.write "local" 这个组件的 receiver。
}

loki.write "local" { # 接收来自其他 Loki 组件的日志,把日志发到 Loki,这是输出端。
endpoint { # 定义目标 Loki 地址
url = "http://loki:3100/loki/api/v1/push"
}
external_labels = { # 给所有发出去的日志统一追加静态标签。
host = "prometheus-jumpserver", # 所有日志自动打上 host=prometheus-jumpserver,env=prod 标签
env = "prod",
}
}

可以把 Alloy 理解成 组件图 。比如上面的配置链路:

  • discovery.docker (用于 Docker 目标发现)去发现 Docker 容器
  • loki.source.docker 读取这些容器的日志,并转发给其他 loki.* 组件
  • loki.write 把日志发给 Loki,用于接收日志并通过 Loki logproto 发往远端 Loki

如果不是采集 Docker,而是采集文件日志,这时通常会用:

  • loki.source.file
  • local.file_match
loki.source.file "files" {
file_match {
path_targets = [
{"__path__" = "/var/log/*.log", "job" = "system"},
{"__path__" = "/var/log/nginx/*.log", "job" = "nginx"},
]
}

forward_to = [loki.write.local.receiver]
}