Loki 2.8 版本深度解读:TSDB 索引转正、查询拦截器(Query Blocker)与 backend 目标
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
本篇技术指南围绕 Grafana Loki 2.8 版本发布说明展开,聚焦该版本三大核心变化:TSDB 索引正式脱离实验状态成为推荐索引存储、新增 Query Blocker 查询拦截能力(按租户运行时配置拦截高风险查询)以及新增backend目标(配合read/write形成三目标可扩展部署,令 read 目标彻底无状态化)。同时完整梳理 2.8 系列的升级注意事项与各补丁版本的安全修复。读完本文,你将能独立完成 TSDB 索引的迁移配置、通过blocked_queries保护集群免受昂贵查询冲击,并理解简单可扩展部署(Simple Scalable Deployment)中 read/write/backend 三目标的分工与组件归属。
一、版本概览
Loki 2.8 由 Grafana Labs 发布,是 2.x 生命周期中承上启下的一个重要版本。它既承载了此前数个版本积累的 TSDB 索引成熟度(该索引已在 Grafana Cloud Logs 产品中经受大规模生产验证),又为运维侧引入了两项直接影响日常使用体验的能力:按租户粒度的查询拦截,以及更合理的水平扩展目标划分。
除功能增强外,2.8 还包含一批针对依赖链的修复与安全加固,后续发布的 2.8.1 至 2.8.6 六个补丁版本均以升级 Go 版本、更新基础镜像、修复特定缺陷为主,详情可查阅仓库根目录的 CHANGELOG。
二、核心特性一:TSDB 索引不再实验
发布说明的第一条即为 TSDB 索引 "no longer experimental"。在经历 Grafana Cloud Logs 中大量生产环境的验证后,Loki 团队确认新 TSDB 索引已足够稳定,鼓励所有 Loki 部署迁移使用。
2.1 什么是单存储 TSDB 索引
Loki 的存储层由"对象存储中的块数据 + 索引"两部分组成。TSDB 索引(Single Store TSDB)将原本 Prometheus 生态的 TSDB 索引格式用于 Loki 的日志索引,索引文件随块数据一同存放在对象存储中,由 ingester 生成、compactor 压缩合并、querier/index gateway 读取。仓库文档 operations/storage/_index.md 明确说明:Single Store TSDB 是Loki 2.8 及更新版本的推荐索引存储,用于替代已进入淘汰流程的 BoltDB Shipper(logs-deletion.md 亦指出 BoltDB Shipper 将在 Loki 4.0 移除,新部署应直接使用 TSDB)。
与旧的 BoltDB Shipper 相比,TSDB 索引带来几个显著优势:
- 文件系统也可作为对象存储使用:由于索引文件随块数据"托运"到对象存储,querier 在独立进程中也能读取同一份数据,只要所有进程能访问同一目录(见 filesystem.md)。旧 BoltDB 索引一次仅允许一个进程持有数据库文件锁,无法做到这一点。
- 支持动态分片(dynamic sharding):TSDB 可以按查询负载动态计算分片数量,这也是 2.8 将
metrics.go日志中subqueries字段拆分为splits与shards的原因之一(详见第五节)。 - 日志删除(logs deletion):基于 TSDB 的索引结构可实现精确的日志条目删除。
2.2 TSDB 索引配置示例
仓库 examples/getting-started/loki-config.yaml 给出了一个完整的最小 TSDB 配置:
schema_config: configs: - from: 2023-01-01 store: tsdb object_store: s3 schema: v13 index: prefix: index_ period: 24h关键字段说明:
store: tsdb:指定索引存储类型为 TSDB;object_store: s3:块数据与索引均存放在 S3(也支持 GCS、Azure Blob 等对象存储,以及filesystem);schema: v13:2.8 时代的 schema 版本;index.period: 24h:索引按天切分,配合 compactor 将同一天、同一租户的多个索引文件合并为单个文件,提升查询时的索引查找效率(见 components.md 中 Compactor 一节)。
2.3 TSDB 生态组件
TSDB 索引启用后,集群中与之紧密协作的组件包括:
- Index Gateway:专门处理元数据查询(如查询日志量、定位 chunk 引用),仅在单存储 TSDB 模式下启用;支持
simple与ring两种运行模式。 - Compactor:将 ingester 上传的多个索引文件按"天 + 租户"合并为单一索引文件,同时承担日志保留(retention)与日志删除(deletion)职责,通常以单实例运行。
- Querier / Query Frontend:查询路径上通过 Index Gateway 获取 chunk 引用后再拉取数据。
三、核心特性二:Query Blocker 查询拦截器
在无法控制客户端查询行为的环境中,某些查询可能无意或有意地代价高昂(例如无过滤的大范围聚合),进而影响整个集群的稳定性与成本。2.8 引入的Query Blocker允许在 Querier / Ruler 中通过每租户的运行时配置(runtime configuration file)直接拦截这类查询,且配置变更无需重启进程。
3.1 配置方式与完整示例
拦截规则定义在 runtime 配置文件的overrides.<tenant-id>.blocked_queries中。仓库中的权威操作文档 operations/blocking-queries.md 给出了完整示例:
overrides: "tenant-id": blocked_queries: # 精确拦截这一条查询 - pattern: 'sum(rate({env="prod"}[1m]))' # 拦截任何匹配该正则的查询 - pattern: '.*prod.*' regex: true # 拦截所有 metric 类型查询(pattern/regex 省略时默认匹配全部) - types: metric # 仅拦截 filter 与 limited 类型中匹配正则的查询 - pattern: '.*prod.*' regex: true types: filter,limited # 按查询哈希精确拦截(32 位 FNV-1 哈希) - hash: 2943214005 # {stream="stdout",pod="loki-canary-9w49x"} 的哈希 types: filter,limited # 通过 X-Query-Tags 请求头按来源拦截(键值均不区分大小写) - pattern: '.*' # 省略 pattern/regex 时默认分别为 '.*' 与 true regex: true query_tags: source: grafana feature: beta3.2 规则字段与匹配语义
每条blocked_queries规则支持以下字段:
| 字段 | 含义 | 说明 |
|---|---|---|
pattern | 查询文本匹配串 | 省略时默认为.*(匹配所有查询) |
regex | 是否将 pattern 视为正则 | 省略时默认为true |
types | 仅拦截指定查询类型 | 可多值,见下 |
hash | 查询文本的 32 位 FNV-1 哈希 | 避免长查询字符串的转义麻烦 |
query_tags | 仅拦截携带指定X-Query-Tags键值对的请求 | 所有指定键值都必须匹配 |
三种查询类型(types)定义如下:
metric:带聚合函数的查询,如sum(rate({env="prod"}[1m]));filter:带日志过滤器的查询,如{env="prod"} |= "error";limited:既无过滤器也无聚合的查询。
hash字段使用查询字符串的 32 位 FNV-1 哈希(32 位无符号整数)。实际使用中无需自行计算——query-frontend 与 querier 会在每次请求的日志中输出query_hash字段,例如:
level=info ts=2023-03-30T09:08:15.2614555Z caller=metrics.go:152 component=frontend org_id=29 latency=fast query="{stream=\"stdout\",pod=\"loki-canary-9w49x\"}" query_hash=2943214005 query_type=limited range_type=range ...3.3 源码实现剖析
Query Blocker 的核心逻辑位于 pkg/logql/blocker.go。从源码结构看,其实现要点如下:
- 规则读取:
isBlocked方法通过qb.q.limits.BlockedQueries(ctx, tenant)获取当前租户的拦截规则(该接口由 pkg/validation/limits.go 中的Overrides.BlockedQueries实现,从每租户覆盖配置中读取blocked_queries字段,配置声明见同文件 L190)。 - 匹配优先级:先比较
hash(b.Hash > 0时精确比对util.HashedQuery(query));否则依次处理 pattern:空 pattern 视为匹配全部、regex: true时编译正则匹配、否则做去除空白的精确字符串比较。 - 类型与标签双重校验:
block方法先校验查询类型是否命中types列表,命中后再由tagsMatch校验请求上下文中的X-Query-Tags(通过httpreq.ExtractQueryTagsFromContext提取),要求规则中声明的所有键值对全部匹配才真正拦截。 - 首个命中即停:注释明确写道 "The order of patterns is preserved, so the first matching pattern will be used"——规则的顺序被保留,Loki 在第一条 pattern 或 hash 命中的规则处停止。若该规则的
types或query_tags约束不满足,则查询不会被拦截,且不再继续检查后续规则(即使后续规则本可拦截)。因此文档建议:多条规则共享同一 pattern 时,应合并约束到单条规则,而非依赖后面的规则兜底。 - 并发安全:对共享配置对象采用局部副本处理,使
isBlocked可被并发安全调用。
3.4 观测拦截结果
被拦截的查询会记录到日志,并累计到按租户统计的loki_blocked_queries指标。当 pattern/hash/regex 命中时,日志会输出类型与标签是否匹配:
level=warn msg="query blocker matched with regex policy" user=29 type=metric pattern=".*rate\\(.*\\).*" query="sum(rate({app=\"foo\"}[5m]))" typesMatched=true tagsMatched=false blocked=false标签约束未命中时输出 debug 级日志,指明缺失的键与收到的原始头:
level=debug msg="query blocker tags mismatch: missing or mismatched key" key=feature tagsRaw="Source=grafana,Feature=alpha"3.5 拦截范围与限制
Query Blocker 由 LogQL 查询引擎在求值阶段强制执行,覆盖:
/loki/api/v1/query(即时查询)与/loki/api/v1/query_range(范围查询)API;- 告警与记录规则(Alerting / Recording Rules),本地与远程评估模式均覆盖——远程评估模式下规则查询会经 query-frontend 与 querier 回传,与普通 API 查询同等拦截。
不适用的场景包括:
- 日志 tail(
/loki/api/v1/tail端点):tail 直接读取 ingester 与存储,不经过查询引擎; - 元数据端点:如
labels、series、index/stats、index/volume、detected_fields等,不执行 LogQL 表达式,无对象可供匹配。
此外仓库文档特别警告:实验性的下一代查询引擎(query_engine.enable: true,面向 dataobj/columnar 存储的查询)完全不检查blocked_queries策略,这是一项已知限制而非延迟检查,拦截策略对其静默失效。
3.6 基于 X-Query-Tags 的定向拦截
除按查询文本拦截外,还可将规则限定到携带特定X-Query-Tags请求头的请求:
- 头格式:逗号分隔的
key=value对,例如Source=grafana,Feature=beta; - 允许字符为字母数字加空格、逗号、等号、
@、.、-,其他字符替换为_;仅保留规范的key=value标记,畸形标记被忽略; - 匹配规则:键与值均不区分大小写(服务端将键转为小写),规则中声明的所有
query_tags键值对必须全部出现在请求中才生效。
一个典型场景是仅拦截来自测试特性开关的查询:
overrides: tenant-a: blocked_queries: - types: metric query_tags: feature: beta - pattern: '.*rate\\(.*\\).*' regex: true query_tags: source: grafanaRuler 查询标签:当 ruler 以远程评估模式(-ruler.evaluation.mode=remote)运行时,会自动在查询上附带source=ruler,rule_name=<rule name>,rule_type=<rule type>标签,因此可以针对规则评估定向配置策略,例如拦截所有来自规则评估的查询:
overrides: "tenant-id": blocked_queries: - query_tags: source: ruler注意:本地评估模式下 ruler 不设置X-Query-Tags,无法按此匹配;且规则名若含逗号或等号会被标签解析截断或丢弃,匹配rule_name时需谨慎。
四、核心特性三:新增backend目标
2.8 为 Loki 的scalable 配置(即 Loki helm chart 的默认部署方式,Simple Scalable Deployment / SSD)新增了第三个目标backend。至此 Loki 可以按read、write、backend三个目标分组运行,read目标因此变为无状态,可作为 Kubernetes Deployment 运行并自动水平扩缩容。
4.1 三目标模式下的组件归属
仓库文档 get-started/components.md 以表格形式明确了各组件在三目标模式下的归属(x表示该目标包含此组件):
| 组件 | all | read | write | backend |
|---|---|---|---|---|
| Distributor | x | x | ||
| Ingester | x | x | ||
| Query Frontend | x | x | ||
| Query Scheduler | x | x | ||
| Querier | x | x | ||
| Index Gateway | x | x | ||
| Compactor | x | x | ||
| Ruler | x | x | ||
| Pattern Ingester | x | x | ||
| Bloom Planner / Builder / Gateway(实验性) | x | x |
可以看到:
read:由 Query Frontend 与 Querier 组成,两者均为无状态组件,天然适合 Kubernetes Deployment + HPA 自动扩缩;write:由 Distributor、Ingester、Pattern Ingester 组成,ingester 依赖 WAL 与对象存储保持有状态;backend:收纳 Query Scheduler、Index Gateway、Compactor、Ruler 等"既不参与写入、也不直接处理用户查询"的组件,通常以 StatefulSet 或固定副本数部署。
4.2 目标机制与配置示例
Loki 的单二进制内聚了所有微服务组件,通过-target命令行参数(或target配置项)指定启动哪些模块。从 pkg/loki/loki.go 的源码可见,target默认值为all(单二进制模式),并支持-list-targets标志打印全部可用目标。
仓库 examples/getting-started/loki-config.yaml 展示了一个三目标部署共享的配置骨架:
memberlist: join_members: ["read", "write", "backend"] dead_node_reclaim_time: 30s gossip_to_dead_nodes_time: 15s left_ingesters_timeout: 30s bind_addr: ['0.0.0.0'] bind_port: 7946 gossip_interval: 2s common: path_prefix: /loki replication_factor: 1 compactor_address: http://backend:3100 storage: s3: endpoint: minio:9000 insecure: true bucketnames: loki-data access_key_id: loki secret_access_key: supersecret s3forcepathstyle: true ring: kvstore: store: memberlist要点解读:
memberlist.join_members列出 read/write/backend 三个目标的服务名,使所有实例通过 memberlist 协议共享集群状态;common.compactor_address指向 backend 目标的 compactor 地址,供需要对接 compactor 的组件(如查询删除相关流程)使用;common.ring.kvstore.store: memberlist让所有内部 ring(ingester、distributor 等)共用 memberlist 集群。
运维上,三个目标各部署一个 Kubernetes Service(分别暴露端口供对应的客户端/组件访问),read与write可按需横向扩容,backend保持较小规模即可。
4.3 与部署模式的衔接
从 get-started/deployment-modes.md 看,当前仓库的部署模式体系已演化为三种:Monolithic(单二进制,-target=all)、HA Monolithic(高可用单二进制)与Microservices(微服务)。其中 HA Monolithic 模式已取代旧 SSD(Simple Scalable Deployment)成为推荐的折中方案,但 2.8 引入的backend目标仍是理解 SSD / helm chart 默认部署形态的关键:它将原本挤在read中的无状态查询组件与有状态/后台组件分离,为 read 路径的弹性伸缩扫清了障碍。
五、升级注意事项(2.8.0)
原发布说明强调升级前务必阅读 upgrade guide。结合仓库升级文档,2.8.0 的关键变更整理如下:
5.1retention_period默认值改为 0s
这是最具破坏性的一项变更。此前compactor.retention_enabled: true且未在limits_config中定义retention_period时,默认保留 744h(31 天);2.8 起默认改为0s,语义为"永久保留 / 禁用保留"。
注意:旧版本中
0或0s会触发立即删除全部日志;仅在 2.8 及之后的版本,零值才表示禁用保留。若希望维持旧行为,需显式配置:limits_config: retention_period: 744h
5.2 LogQL 重复标签行为变化
当日志行中出现重复标签时,Loki 2.8 改为只保留第一个值,此前保留的是最后一个值。使用重复标签的查询结果可能因此变化。
5.3metrics.go日志字段:subqueries拆分为splits与shards
旧版metrics.go每行日志中的subqueries仅反映按时间拆分产生的子查询数,未包含分片数。2.8 起不再输出subqueries(统计 API 中为向后兼容仍返回该字段,但恒为 0),改为:
splits:按时间间隔拆分产生的查询段数量;shards:为查询创建的总分片数(值为 0 通常表示该查询无法分片)。
这尤其适用于 TSDB 的动态分片能力,便于区分"时间拆分"与"分片"两种并行化来源。同时 2.8 在metrics.go中新增了 Store 与 Cache 统计:记录从存储下载 chunk、以及从缓存下载 chunk / 索引查询结果 / 结果缓存所耗费的时间(*_download_time字段),这些统计也可通过 LogCLI 的--stats标志查看。
5.4 Promtail:promtail_journal_enabled构建标签
Promtail 引入 Go 构建标签promtail_journal_enabled,只有传入该标签才启用 Journal 支持:
go build --tags=promtail_journal_enabled ./clients/cmd/promtail此举旨在让无需 Journal 支持的 Linux/CentOS 用户(CGO 启用时)免于安装libsystemd-dev/systemd-devel依赖。
5.5 Ruler:ruler.wal-cleaer.period废弃
CLI 标志ruler.wal-cleaer.period拼写有误,2.8 废弃并以修正后的ruler.wal-cleaner.period取代;YAML 配置不变:
ruler: wal_cleaner: period: 5s5.6 Querier:query-frontend Kubernetes 服务类型调整
此项仅影响使用 jsonnet 部署 Loki 到 Kubernetes 的用户。原query-frontendheadless 服务被拆分为两个服务:query-frontend改为负载均衡服务(公平分发查询请求),新增query-frontend-headless供 querier 发现前端 Pod IP 建立 worker 连接。使用 Query Scheduler(query_scheduler_enabled: true)的部署无需处理;未使用 Scheduler 时建议按"先建 headless 服务 → 滚动更新 querier → 再应用其余变更"的顺序灰度。
六、补丁版本与安全修复
2.8 系列共发布 2.8.1 至 2.8.6 六个补丁,主要内容为安全加固与缺陷修复(完整清单见仓库根目录 CHANGELOG):
| 版本 | 日期 | 要点 |
|---|---|---|
| 2.8.6 | 2023-10-17 | 升级 Go 至 v1.20.10、golang.org/x/net 至 v0.17.0、grpc-go 至 v1.56.3,修复 CVE-2023-39325 / CVE-2023-44487 |
| 2.8.5 | 2023-09-14 | 更新 Docker 基础镜像,缓解安全漏洞 CVE-2022-48174 |
| 2.8.2 | 2023-05-03 | 升级 Go 至 1.20.4 修复安全漏洞;Promtail 新增decompression配置以自定义解压器行为 |
| 2.8.1 | 2023-04-21 | 修复索引周期为零值时索引被丢弃的问题;修复 redis 客户端在本地地址下错误选择集群模式的问题;升级 Go 至 1.20.3;alpine 镜像更新至 3.16.5;修复 Promtail 在 amd64 二进制构建中的 journald 支持 |
这些补丁提醒生产用户:若运行 2.8 早期版本,应至少升级至 2.8.6,以规避 Go 依赖链与基础镜像层的历史 CVE。
七、总结
Loki 2.8 是 2.x 系列中"转正与治理"并重的一个版本:TSDB 索引经生产验证后成为官方推荐索引方案(配合 schema v13 与 compactor/index gateway 组件使用);Query Blocker 借助每租户运行时配置,为集群提供了按文本、哈希、类型、请求来源多维度的查询治理手段;backend目标的加入则完善了三目标可扩展部署的组件划分,让 read 路径可以无状态弹性伸缩。升级侧最需要留意的是retention_period默认值变更与 LogQL 重复标签语义调整。对于 2.7 及更早版本的用户,建议结合升级指南与本文第六节,制定"配置核对 → 灰度升级 → 安全补丁到位"的完整升级路径。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考