ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Grafana Tempo Live-store 架构深度解析:从内存追查到本地 WAL 的近期数据读取路径

Grafana Tempo Live-store 架构深度解析:从内存追查到本地 WAL 的近期数据读取路径 Grafana Tempo Live-store 架构深度解析从内存追查到本地 WAL 的近期数据读取路径【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempoGrafana Tempo 的 live-store 是读取路径read path上的核心组件专门负责服务“刚刚写入、尚未落入对象存储 block”的近期 trace 数据。本文以仓库文档 live-store.md 为主线结合modules/livestore目录下的源码实现系统讲解 live-store 的定位、trace 生命周期、分区所有权partition ownership、多可用区高可用、本地 WAL 与关键监控指标。读完本文你将能够理解近期数据查询的完整链路掌握live_store配置块的每个参数及其默认值并学会通过指标与 HTTP 接口完成 live-store 的缩容与排障。Live-store 是什么读取路径上的“内存热点”Live-store 是 Tempo 读取路径中负责服务近期 trace 数据的组件。它把 trace 保存在内存中从而在“数据被写入 Kafka”与“block-builder 把数据刷入对象存储、block 可被查询”之间的窗口期内依然能够响应查询请求。Live-store 如何接收数据取决于部署模式微服务模式Microserviceslive-store独立于 block-builder 消费 Kafka中的 trace 数据。此时 live-store 既是查询入口也是 Kafka 的消费者。单体模式Monolithiclive-store 直接在进程内接收来自 distributor 的数据通过LiveStore.PushBytes方法不涉及 Kafka 消费。两种模式在代码层面对应Config.ConsumeFromKafka字段见 modules/livestore/config.go微服务模式下为true单体模式下由应用装配层app wiring显式关闭。这一开关还决定了 complete-block 生命周期策略——Kafka 模式下完成块只保留在本地kafkaCompleteBlockLifecycle为 no-op单体模式下则会通过localCompleteBlockLifecycle在后台把完成的 block 刷入对象存储见 modules/livestore/complete_block_lifecycle.go。为什么需要 live-store在微服务模式下数据链路存在一个天然的空档distributor ── Kafka ── block-builder ── 对象存储 block可查询 │ └── live-store内存中可查询当 trace 数据写入 Kafka、但 block-builder 尚未把它刷新到对象存储时唯一能查询这批数据的途径就是 live-store。在单体模式下live-store 扮演同样的角色——为最近摄入的数据提供即时查询能力——只是数据来源从 Kafka 变成了进程内的 distributor。无论哪种模式live-store 都会在内存中按 trace ID 组织trace响应 querier 对近期数据的查询周期性把 traceflush 到本地 WALParquet 格式使数据可用于 TraceQL 搜索和指标metrics查询。Trace 生命周期active → idle → WAL → complete block当 live-store 收到 spans 后会在内存中把它们组装成 trace。每条 trace 会经历三个阶段的流转对应源码instance中的liveTraces、walBlocks与completeBlocks三类数据容器见 modules/livestore/instance.goActive活跃期trace 正在接收 spans驻留内存可以被查询trace ID 查询直接命中内存中的LiveTraces见 instance_search.go 中“先查 live traces”的逻辑。Idle空闲期当超过配置的max_trace_idle时间内没有新的 span 到达trace 被判定为 idle并被flush 到本地 WALcutIdleTraces见 instance.go。写入 WAL 的数据是Parquet 格式此后数据便可用于 TraceQL 搜索而不仅是 trace ID 查询。Complete block完整块WAL 数据最终被“切块”cut并完成complete成本地完整块。切块的触发条件在shouldCutHead中定义见 instance.go包括immediate立即/停机、达到max_block_duration默认 30s或达到max_block_bytes默认 50MB。WAL block 通过instance.completeBlock见 instance.go转换成查询性能更优的 complete block并保留在本地磁盘上继续提供查询服务。整个流水线由后台循环驱动每个租户实例有独立的perTenantCutToWalLoop按flush_check_period周期切 WAL与perTenantCleanupLoop清理过期 block全局则有globalCompleteLoop消费一个按租户/block 排重的完成队列见 live_store_background.go。背压backpressure机制为了避免内存无界增长instance实现了两级背压见 instance.go当活跃 trace 占用的内存字节数达到max_live_traces_bytes默认 250MB时等待内存被 flush 腾出空间当未完成的 WAL block 数量超过walBackpressureLimit4 个时等待 block 完成、减少积压。背压时长通过tempo_live_store_back_pressure_seconds_total与tempo_live_store_back_pressure_duration_seconds指标暴露。Trace 空闲期max_trace_idlemax_trace_idle控制 live-store 在最后一个 span 到达后、判定 trace 空闲并 flush 到 WAL 之前需要等待多久live_store: max_trace_idle: 10s调大该值会让 trace 在内存中驻留更久提高同一 trace 的全部 span 在 flush 时被聚合到一起的概率——这对长运行 tracelong-running traces尤其有益。但代价是内存占用上升。需要特别注意两点源码默认值为5s见 modules/livestore/config.go上述示例中的10s是文档给出的调优值校验规则max_trace_idle不能大于max_trace_live默认 30s否则配置校验直接报错见 config.go因为 idle 判定本质上不能晚于 trace 的最长驻留上限。分区所有权Partition ownership在微服务模式下live-store 是 Tempo 中分区生命周期的所有者每个 live-store 实例消费一个或多个 Tempo 分区而每个分区在每个可用区availability zone中恰好被一个 live-store 拥有。分区环Partition ringlive-store 维护一个分区环用来追踪Tempo 中存在哪些分区每个分区由哪些 live-store 拥有每个分区的状态pending待定、active活跃或inactive不活跃。该环通过memberlist gossip传播PartitionRingConfig默认将 KVStore 设为memberlist见 partition_ring.go存储键为livestore-partitions见 live_store.go。分区状态的完整定义与转移规则可参考分区环文档。启动流程live-store 启动时LiveStore.starting见 live_store.go会执行以下步骤检查 shutdown marker若存在说明上次是受控缩容则预先进入 prepare-shutdown 模式。初始化 WAL 并重放本地 blockreloadBlocks清理 tombstone 遗留。启动分区 lifecycler查询分区环中自己负责的分区——分区已存在以 owner 身份加入分区不存在以pending状态创建等待足够的 owner 注册后由min_partition_owners_count与min_partition_owners_duration控制默认分别为 1 和 10s自动提升为active。启动读取路径微服务模式下创建 Kafka reader从上次提交的 Kafka offset 开始回放PartitionReader.fetchLastCommittedOffset见 partition_reader.go以重建内存状态。若本地没有数据则强制从 lookback 周期2 × complete_block_timeout处开始消费若分区已处于inactive上一个 Pod 已排空则跳过 lookback 回放见 live_store.go。就绪判定等待 Kafka 追赶到readiness_target_lag阈值默认0即关闭该等待保持向后兼容后将tempo_live_store_ready置为 1CheckReady返回 nil开始服务查询。关闭与缩容缩容 live-store 的正确姿势是先给分区打上 inactive 标记让分区进入只读模式read-only。实现上Tempo 提供了两个 HTTP 管理接口见 downscale.goPreparePartitionDownscaleHandler作用于分区本身POST把分区切换为inactive若分区处于pending状态则返回 409因为无法确认回退目标状态DELETE取消缩容准备把分区从inactive恢复为activeGET查询分区当前状态与切换时间戳。PrepareDownscaleHandler作用于 live-store 实例基于 shutdown marker 文件POST设置 shutdown marker并配置“关闭时从分区环移除 owner”且“启动时不创建分区”DELETE取消准备GET查询是否已设置。等待足够时间让数据全部 flush 到对象存储之后就可以安全地移除分区和 live-store 实例。反之直接粗暴地杀掉 live-store不先标记 inactive会使其分区的近期数据暂时不可查询直到另一个 live-store 接管该分区在多可用区部署下其他可用区的 live-store 会继续服务这些分区。此外remove_owner_on_shutdown默认true控制正常关闭时是否从分区环清理 owner 注册避免残留陈旧条目见 config.go。多可用区高可用读法定人数为 1生产环境中live-stores 通常跨多个可用区AZ部署。每个 Tempo 分区在每个可用区各由一个 live-store 拥有当某个可用区的 live-store 不可用时另一可用区的 live-store 继续为相同分区服务查询querier 对每个分区只需要来自一个 live-store 的响应读法定人数 read quorum 1因此只要至少一个可用区健康查询就能成功这提供了高可用性同时无需在读取路径上做数据去重。这一设计的收益在于读取路径可以容忍单个可用区故障而不会引入额外的合并/去重开销。分区环通过 memberlist 在各可用区之间传播min_partition_owners_count可配置为期望的 owner 数量例如每个分区在 2 个可用区各有一个 ownermin_partition_owners_duration则防止 pending 分区在 owner 尚未齐备时过早提升。本地 WAL搜索可用性与重启恢复当 trace 从内存中 flush 出来时会被写入本地 WAL格式为 Parquet。WAL 承担两个目的搜索可用性数据进入 WAL 后即可被TraceQL 搜索命中而不再局限于 trace ID 查询。源码中instance.iterateBlocks会并发遍历三类 block——head block、WAL blocks、complete blocks受query_block_concurrency限制默认 10——并对其统一执行 Search / SearchTags / SearchTagValues / QueryRange见 instance_search.go。重启恢复微服务模式下live-store 重启后会从 Kafka 回放而 WAL 提供在回放期间继续服务查询的能力。reloadBlocks见 live_store_background.go会重扫 WAL block 与 complete block把未完成的 WAL block 重新入队完成从而做到“重启不丢查询窗口”。WAL 最终会被切成本地完整块complete blocks这些块同样存储在本地并且一直保持可查询直到数据超出 live-store 的保留窗口。保留由complete_block_timeout默认 20 分钟驱动deleteOldBlocks会删除结束时间早于now - complete_block_timeout的 block见 instance.go。另外block_reclaim_grace默认 2 分钟会延迟删除已从快照移除 block 的磁盘文件避免在途 reader 遇到ENOENT——配置约束是不得小于 querier 的search.query_timeout默认 30s。摄入路径上的数据质量保障Kafka 消费回调LiveStore.consume见 live_store.go会丢弃时间戳早于now - complete_block_timeout的过期记录避免回放过老数据reasontoo_old丢弃解码失败的记录reasondecoding_failed丢弃租户实例无法创建的记录reasoninstance_not_found成功处理的记录按租户累加tempo_live_store_records_processed_total并在每批消费后推进待提交 offset提交周期由commit_interval默认 5s控制设为0时改为同步提交。关键指标live-store 暴露的指标以tempo_live_store_为前缀核心监控项如下指标说明tempo_live_store_traces_created_totallive-store 中创建的 trace 总数按租户tempo_live_store_lagged_requests_total因 Kafka 延迟而无法保证结果完整性的请求数按route标签区分tempo_live_store_query_inspected_bytes_totallive-store 查询检查inspected的总字节数按tenant和op标签区分tempo_warnings_totaltrace 处理期间的告警按reason标签区分tempo_ingest_group_partition_lag{grouplive-store}每个分区的消费延迟consumer lag其中tempo_live_store_query_inspected_bytes_total的op标签标识 live-store 的查询操作类型可能取值包括search、search_tags、search_tag_values、trace_by_id、query_range对应源码中的常量定义见 instance.go。该指标用于在 query frontend 聚合整个查询路径的 inspected bytes之前先行评估近期数据查询的成本。tempo_live_store_lagged_requests_total与fail_on_high_lag默认true配合使用当 Kafka 延迟导致无法保证结果完整性时搜索与指标查询会直接失败返回cannot guarantee complete results避免返回不完整结果误导用户见 live_store.go。其他可用于运维监控的补充指标同样来自源码tempo_live_store_ready1 表示就绪0 表示启动中/停止中tempo_live_store_live_traces/tempo_live_store_live_trace_bytes当前活跃 trace 数量与占用内存tempo_live_store_blocks_cut_total按reasonimmediate/max_block_duration/max_block_bytes、tempo_live_store_blocks_completed_total、tempo_live_store_complete_queue_lengthtempo_live_store_completion_duration_seconds、tempo_live_store_completion_size_bytestempo_live_store_partition_owned分区归属、tempo_ingest_storage_reader_receive_delay_secondsKafka 接收延迟。配置参考完整 live_store 配置块结合文档与 modules/livestore/config.go 中的默认值live_store的完整配置项如下live_store: # 定时类参数 flush_check_period: 5s # 周期性切 WAL 的检查间隔 flush_op_timeout: 5m # 清理周期清理过期 block max_trace_live: 30s # trace 在内存中的最长驻留时间 max_trace_idle: 5s # 判定 trace 空闲的等待时间不得大于 max_trace_live max_live_traces_bytes: 250000000 # 活跃 trace 占用内存上限250MB触发背压 max_block_duration: 30s # head block 按时间切块阈值 max_block_bytes: 52428800 # head block 按大小切块阈值50MB commit_interval: 5s # Kafka offset 提交周期0 表示同步提交 # 块完成与查询 complete_block_timeout: 20m # 完成块在 live-store 中的保留时长默认 20 分钟 complete_block_concurrency: 2 # 并行完成 block 的协程数 query_block_concurrency: 10 # 查询时并发扫描的 block 数 block_reclaim_grace: 2m # block 文件删除延迟须 querier search.query_timeout # 就绪与延迟控制 readiness_target_lag: 0 # 就绪前的目标 Kafka 延迟0 表示禁用等待默认 readiness_max_wait: 30m # 追赶超时上限仅 readiness_target_lag 0 时生效 fail_on_high_lag: true # 高延迟时是否让搜索/指标请求失败 remove_owner_on_shutdown: true # 正常关闭时是否从分区环移除 owner # 存储路径 shutdown_marker_dir: /var/tempo/live-store/shutdown-marker wal: path: /var/tempo/live-store/traces # 本地 WAL 存储路径 # 分区环 partition_ring: kvstore: store: memberlist # 分区环通过 memberlist gossip 传播 min_partition_owners_count: 1 # pending 提升为 active 所需最少 owner 数 min_partition_owners_duration: 10s # 最少 owner 数的持续验证时长 delete_inactive_partition_after: 13h # inactive 分区可被删除的等待时长0 禁用删除 metrics: time_overlap_cutoff: 0.2 # 指标查询是否加载 trace 级时间戳列的重叠比例阈值0.0~1.0配置校验规则见 config.go要求上述定时类参数均大于 0且max_trace_idle max_trace_live。commit_interval为0时 partition reader 采用同步提交见 partition_reader.go。相关资源完整配置项与部署参数以 modules/livestore/config.go 为准结合 config.go 中存储相关设置如 block 版本、dedicated columns分区状态机与转移规则partition-ring.md部署模式差异deployment-modes.md查询 I/O 与 span 时间戳距离的监控参见仓库docs/sources/tempo/operations/monitor/目录下的监控文档以及上述tempo_live_store_query_inspected_bytes_total指标的使用方式测试参考modules/livestore/下的live_store_test.go、instance_test.go、instance_search_test.go、config_test.go等测试文件覆盖了分区接管、trace 切块、查询与配置校验等关键路径是理解 live-store 行为的绝佳补充材料。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表