ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Pyroscope v2 架构深度解析:无本地磁盘的持续分析平台设计

Pyroscope v2 架构深度解析:无本地磁盘的持续分析平台设计 Pyroscope v2 架构深度解析无本地磁盘的持续分析平台设计【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscope本篇文章系统讲解 Pyroscope v2 的完整架构从 v1 的局限性出发剖析 v2 如何通过「写入即落对象存储 Raft 元数据索引 无状态读写路径」三大设计支柱实现写入/查询路径独立扩展、消除写复制与本地磁盘依赖。读完本文你将掌握 v2 六个核心组件distributor、segment-writer、metastore、compaction-worker、query-frontend、query-backend的职责与协作流程、三层数据分布算法、块与元数据索引格式、两种部署模式以及基于 Helm 从 v1 平滑迁移到 v2 的三阶段实操方案。设计动机v1 的哪些局限促成了 v2 重构Pyroscope v2 是一次彻底的架构重设计其核心目标是提升可扩展性scalability、性能performance与成本效率cost-efficiency具体包括四项目标高写入吞吐、低存储成本、可扩展的查询性能、降低运维开销。v2 之所以选择推倒重来是因为 v1 存在一系列「无法通过增量修补解决」的根本性局限。写入路径的 v1 局限无写前日志WALv1 的 ingester 在内存中累积 profile周期性地刷盘但写入到达时没有 WAL 做持久化记录。如果 ingester 在两次刷盘之间崩溃内存中的 profile 就会丢失。复制可以缓解但无法在多个 ingester 同时故障时完全避免数据丢失。去重开销v1 中每个 profile series 复制到 N 个 ingester每个 ingester 各自写一个 block查询时需要把这些副本合并、去重随着 ingester 数量增长该开销急剧上升。读写隔离薄弱写入延迟尖峰可能引发 distributor 内存溢出OOM昂贵查询因宽锁broad locks提升写入延迟其自身也可能导致 ingester OOM。数据分布欠佳基于 label-hash 的分片会把同一服务的 profile 分散到所有 ingester造成符号symbol信息过度复制降低查询选择性。滚动升级慢大规模部署中ingester 滚动升级需要先刷盘再停机可能耗时数小时。读取路径与压缩的 v1 局限store-gateway 不稳定重查询可能引发 store-gateway OOMblock 索引开销随 block 数量增长给 store-gateway 带来内存压力。弹性有限querier 与 store-gateway 难以动态伸缩因为 store-gateway 在提供服务前必须先加载 block 索引其滚动升级也因此缓慢。压缩器可扩展性差v1 compactor 在大租户下难以跟上节奏写入时数据已复制压缩延迟会给读路径带来压力因为查询要处理并去重更多未压缩的 block。扩展性差在 v1 中新增一种数据访问方式例如为 heatmap 新增 API 端点需要改动大量组件ingester、store-gateway、querier 之间的紧耦合使代码库难以维护和扩展。v1 与 v2 对比总览维度v1v2写入路径Distributor → Ingester → Object StorageDistributor → Segment writer → Object storage Metastore元数据对象存储中的按租户 bucket 索引Metastore基于 Raft 的内存索引读取路径Query frontend → Query scheduler → Querier → Ingester / Store-gatewayQuery frontend → Metastore Query backend压缩Compactorhash-ring 分片、按租户Compaction worker由 metastore 编排复制写入复制到 N 个 ingester无写复制靠对象存储保证持久性v2 通过「用对象存储持久性取代写复制」「将元数据集中到 metastore 以加速查询规划」「让无状态的 query backend 直连对象存储」「把压缩解耦为 metastore 编排的、基于任务的弹性系统」四点系统性化解了上述问题。架构总览写入路径与读取路径如何解耦v2 最大的结构性变化是存储方式数据被直接写入对象存储ingester 不再需要本地磁盘。单节点部署时仍可用本地文件系统充当对象存储但微服务模式不支持本地文件系统。v2 同时将写入路径与查询路径解耦两条路径可独立伸缩——即便最重的查询也不会干扰写入性能读取路径可以瞬时扩展到数百实例。架构的高层组件关系如下Ingest Path → distributor ──写入──▶ segment-writer ──更新──▶ metastore │ └──创建段──▶ object storagesegments / blocks metastore ──协调──▶ compaction-worker ──压缩──▶ object storage Query Path → query-frontend ──调用──▶ query-backend ──读取──▶ object storage │ └──查询──▶ metastorev2 中绝大多数组件是无状态的进程重启之间无需持久化任何数据metastore 是唯一的存状态组件通过 Raft 共识实现复制与容错。各组件详细说明见 Components 索引。写入路径从 Push RPC 到对象存储的同步落盘Profile 通过 Push RPC API 与 HTTP/ingestAPI 进入 distributor写入路径由 distributor 与 segment-writer 两个服务组成二者均无状态、无磁盘、可高效水平扩展。写入请求在 distributor 之间负载均衡distributor 再把 profile 路由到 segment-writer以将同一应用的 profile 共置co-locate——很可能被一起查询的 profile 会被存放在一起。segment-writer 在内存中累积 profile形成小数据块segments后写入对象存储同时把新增对象的元数据更新进 block 索引。每个 writer 为每个 shard 只产出一个对象内含该 shard 上所有租户服务的数据从而把对对象存储的写操作次数降到最低优化整体成本。写入客户端会阻塞等待直到数据已持久化到对象存储、且元数据索引中已创建对应条目。默认情况下写入是同步的使用默认配置时中位延迟预期低于 500ms。关键代码佐证distributor 的入口与路由逻辑位于 pkg/distributor/distributor.go其中通过IngestionTenantShardSize第 142 行与ShuffleShard第 851 行实现基于租户的分片选取。segment-writer 侧的分片放置实现位于 pkg/segmentwriter/client/distributor/distributor.go第 83–108 行展示了「先确定租户在环空间中的 shard 数再在租户子环中选取 dataset shard」的完整过程。distributor写入路径的入口与校验distributor 是无状态组件接收来自 agent 的 profile 数据并路由到 segment-writer。与 v1 基于 hash ring token 分布的路由不同v2 依据 profile 的service_name标签路由这一共置策略带来三个收益查询性能可能一起查询的 profile 落在同一 block 中压缩效率相关数据可被更有效地压缩存储优化减少满足典型查询所需读取的对象数量。发送到 segment-writer 之前distributor 还会做数据清洗与校验确保 profile 带时间戳缺失时默认取接收时间、剔除零值样本、对共享同一 stacktrace 的样本求和。若请求包含非法数据distributor 返回 400 状态码并在响应体中给出详情。在负载均衡上写入请求在 distributor 实例间随机分配若运行在 Kubernetes 中可为 distributor 定义 Kubernetes Service 作为入口。distributor 通过基于 memberlist 的 ring 发现机制维护可用的 segment-writer 实例列表。由于完全无状态无磁盘distributor 可以任意增删实例而无需数据迁移也支持部署在临时容器中。segment-writer内存累积 同步落盘segment-writer 是 v2 新增组件替代了 v1 ingester 在写入路径中的角色。工作流程分四步Profile 累积从 distributor 接收 profile 并在内存中累积Segment 创建profile 被批量打包成名为 segment 的小 block对象存储写入segment 直接写入对象存储元数据更新segment-writer 向 metastore 登记新 segment 的元数据。内存累积使用内存数据库结构包含 profile 索引对已累积 profile 的高效索引与倒排索引segment 创建时基于标签的查找。单 shard 单对象每个 segment-writer 每个 shard 只产出一个对象包含该 shard 上所有租户服务的数据显著减少对象存储写操作次数、降低成本。失败处理与死信队列segment-writer 依赖 at-least-once 投递语义。若 segment 已上传到对象存储但 metastore 尚未确认元数据时写入失败客户端会重试请求可能导致同一 profile 出现在多个 segment 中——这会在压缩阶段解决。若 segment-writer 无法向 metastore 登记元数据例如 metastore 不可用元数据会被写入对象存储的死信队列DLQ目录metastore 在后台恢复这些条目确保数据最终对查询可见。部署形态segment-writer 以无持久化卷的 StatefulSet 运行参与 distributor 路由所用的 hash ring。拓扑变化期间如扩缩容、滚动升级多个 writer 可能短暂写入同一 shard这是预期行为由压缩处理。flush 配置权衡segment-writer 的 flush 行为可在三个目标间权衡配置——延迟数据多久可被查询、成本对象存储写操作次数、内存用量flush 前在内存中持有的数据量。读取路径查询规划 高并行执行Profile 数据通过 query-frontend 提供的 Query API 查询。用户在 UI 中看到的一张常规火焰图查询可能需要从存储中拉取数 GB 数据且原始 profile 数据需要昂贵的后处理才能以火焰图形式展示。Pyroscope 通过「自适应数据放置adaptive data placement最小化一次查询需要读取的对象数」和「查询执行的高度并行」两个手段应对。query-frontend查询规划query-frontend 是无状态组件负责查询规划并把请求路由到 query-backend 执行具体流程校验查询请求查询 metastore找出匹配查询条件时间范围、租户、可选 service name的全部 block构建物理查询计划树叶子节点是针对特定 block 与 dataset 的读操作中间节点是合并子节点结果的 merge 操作把计划树根发送给某个 query-backend 实例由其把子树分发到其他 query-backend 并行执行并合并。由于 metastore 从内存中以 linearizable 读的方式提供 block 元数据查询规划非常快query-frontend 无需维护任何关于 block 的本地状态。原生 profile 的符号化symbolization来自原生代码如 eBPF 采集的 profile其栈帧可能只带 build ID 和地址、没有解析出的函数名。Pyroscope 通过 debuginfod 解析这些帧按 build ID 拉取调试信息、提取函数名并把结果缓存在对象存储中复用。符号化默认关闭相关配置为-symbolizer.enabledtrue开启符号化为所有租户设置默认值可按租户覆盖-symbolizer.debuginfod-url选择拉取调试信息的 debuginfod 服务器默认https://debuginfod.elfutils.org按租户标志symbolizer.symbol-ref-trees-enabled默认false开启后 query backend 会在树查询结果中携带未解析的原生帧由 query-frontend 在合并所有 query-backend 的结果后统一解析一次symbolizer.resolve-timeout默认20s单个二进制地址解析的全局上限。query-backend树状执行与并行合并query-backend 是无状态组件按 query-frontend 下发的查询计划以高并行度直接从对象存储读取数据并处理。查询计划是树结构读节点叶子从对象存储中特定 block 取数处理合并节点中间层组合子节点结果。query-frontend 把计划树根发给一个 query-backend 实例该实例把子树分发给其他 query-backend 并行执行、收集结果并逐层合并最终结果返回 query-frontend 再转发给客户端。这种树状执行让查询能扇形扩散到大量 query-backend 实例上并行执行合并在树的每一层进行而非集中于单一聚合点。与 v1 查询近期数据可能需要访问 ingester 不同v2 的 query-backend 直接读对象存储无需与写入路径组件协调、查询执行更简单、读写路径隔离更好、水平扩展更容易。它不需要任何缓存层实例间不共享状态支持基于查询负载自动伸缩。性能特征高并行多 block 并发处理、内存高效树状执行最小化内存需求、网络优化结果在靠近数据源处合并。压缩控制对象数量、保证查询性能若不压缩存储中的对象数量每小时可达数百万导致查询读放大严重、对象存储调用过多、metastore 索引无界膨胀读写路径双双性能劣化。因此数据对象会在后台被压缩compaction-worker 负责把小 segment 合并成更大的 block 再写回对象存储由 metastore 协调调度。压缩会在数据写入对象存储后尽快进行首次压缩的中位时间不超过 15 秒worker 无状态、无需本地存储。六个核心组件速查组件状态职责备注distributor无状态写入入口接收、校验、路由 profile依据service_name共置路由segment-writer无状态内存累积 profile写 segment 到对象存储并登记元数据单 shard 单对象、同步写入、DLQ 兜底metastore有状态维护元数据索引、编排压缩、查询规划、数据放置、留存策略唯一状态组件Raft 复制BoltDB 存储索引compaction-worker无状态拉取压缩任务合并 segment 为 blockSmall Job First 调度lease fencing token 防冲突query-frontend无状态查询入口校验、规划、路由可扩至数百实例query-backend无状态按计划树高并行执行查询直连对象存储无需缓存层数据分布三层放置算法与自适应负载均衡v2 用一套精细的数据分布算法把 profile 放置到 segment-writer 上既要保证同一应用的 profile 共置又要维持集群负载均衡。详细设计见 Data distribution完整算法规范与 shard 映射流程见 pkg/segmentwriter/client/distributor/README.md如仓库中存在。设计目标数据共置同一租户服务的 profile 存在一起、查询性能共置减少查询所需对象数、压缩效率、负载均衡、最小化再平衡集群变化时数据移动尽量少。三步放置Tenant shards用tenant_id从总 N 个 shard 中找出 m 个候选位置Dataset shards用service_name标签从 m 个候选中缩小到 n 个Final placement从 n 个候选中选出确切 shard s。其中 N 是部署总 shard 数mtenant shard limit显式配置ndataset shard limit根据观测到的写入速率动态选取。示例某租户的 shard 范围从偏移 3 开始、大小为 8其 dataset 的 shard 范围是该租户范围内的子集从偏移 1 开始、含 4 个 shard。一致性哈希与热点缓解Pyroscope 使用 Jump consistent hash 在子环内选位保证均衡性对象均匀分布与单调性增加 bucket 时对象只从旧桶迁往新桶集群规模变化时数据再平衡最小。为防止大量 dataset 落在同一节点形成热点shard 通过一张独立的映射表映射到实例保证跨节点均衡、节点增删时更新、尽可能保留既有映射。从源码看这一「shard 洗牌映射以避免热点」的实现位于 pkg/segmentwriter/client/distributor/distributor.go。自适应负载均衡与放置管理由于持续 profiling 的数据天然不均衡系统默认用fingerprint mod n作为分布键检测到偏斜skew时切换为random(n)分布在可能时保持局部性。Placement Manager 运行在 metastore leader 上跟踪 segment-writer 元数据中的 dataset 统计、按固定间隔构建放置规则、决定每个 dataset 的 shard 数与负载均衡策略fingerprint mod 或 round-robin。放置规则存于对象存储、由 distributor 拉取由于不执行实际数据再平衡放置规则无需实时同步。失败处理segment-writer 故障时distributor 从可用候选中选择下一个合适的 writer并在请求中显式指定 shard 标识从而在瞬时故障期间维持数据局部性。两个相同分布键的请求偶尔落入不同 shard 属预期内的罕见情况。对象存储与块格式v2 的设计目标就是无本地磁盘运行、完全依赖对象存储以此最小化运维开销与成本。支持的对象存储后端包括Amazon S3、Google Cloud Storage、Microsoft Azure Storage、OpenStack Swift以及本地文件系统仅单节点。对象存储目录布局Segmentlevel 0尚未压缩与压缩后的 block 存放在两个顶层目录segment 尚未按租户拆分、使用匿名租户目录压缩后 block 按租户组织segments/ {shard}/ anonymous/ {block_id}/ block.bin blocks/ {shard}/ {tenant}/ {block_id}/ block.bin dlq/ {shard}/ {tenant}/ {block_id}/ block.binblock.bin 结构每个block.bin对象包含一串 dataset后跟元数据页脚metadata footerOffset | Content ----------|------------------------------------------- 0 | Dataset 0 data | Dataset 1 data | ... | Dataset N data | Protobuf-encoded block metadata end-8 | uint32 (big-endian): raw metadata size end-4 | uint32 (big-endian): CRC32 of metadata sizeDataset 与块元数据Dataset 是 block 内自包含的区域存放某特定服务的 profile 数据包含TSDB 索引series 标签 → profile 映射、符号数据symbols.symdb用于栈追踪与函数名、Parquet profile 样本表。Dataset 带有service_name、profile_type等标签注解查询路径可以不读整个 block 只选相关 dataset另有租户级 dataset 索引供不针对特定服务的查询定位相关 dataset。块元数据是 protobuf 编码结构block IDULID、租户、shard、压缩级别、时间范围dataset 列表字节偏移/目录表、标签、大小以及用于去重的字符串表。元数据同时存于 metastore 索引和 block 对象内部。元数据索引metastore 的存储核心元数据索引记录对象存储中所有数据对象block 与 segment的信息由 metastore 维护为查询规划提供快速查找。详见 Metadata index。职责与实现metastore 的职责包括维护全部 block/segment 索引、调度协调压缩任务、为 query-frontend 提供查询规划元数据、管理数据放置规则、执行基于时间的留存策略并生成过期数据的 tombstone。实现上采用BoltDB键值存储单写者多读者场景简洁高效Raft复制与一致性。索引按时间分区每个分区覆盖6 小时窗口分区内按租户、shard 组织shard 内包含块条目block ID → 元数据、字符串表与用于高效过滤的时间范围索引。源码层面metastore 的 BoltDB 与 Raft 集成可见于 pkg/metastore/metastore.go其 leader/follower 读取路径基于bbolt.Tx的 StateReader第 98-99 行。索引写入Raft 提交与 tombstone 防护segment-writer 创建新 segment 时发起写入调用AddBlock(metadata)→ metastore 向 Raft 提交ADD_BLOCK→ 提交到日志 → 插入索引 → 返回成功。写入前索引会检查 tombstone防止重复添加已被压缩的 block覆盖「writer 的响应丢失但 block 已添加」「block 在重试前已被压缩」两种场景。索引查询linearizable 读查询走 linearizable 读模式请求当前 commit index → 校验当前 leader → 等待 commit index 在本地应用 → 读本地状态机。leader 与 follower 副本都可服务查询且保证读到最新已提交状态。支持两类查询模式元数据查询按条件找 block例如时间范围[start, end]、租户[tenant-1]、标签{service_namefrontend}标签查询不读数据直接列出可用标签值例如按{service_namefrontend}过滤返回profile_type的去重取值。留存策略与清理留存按分区粒度执行分区超过配置的留存周期即整体删除而非逐 block 评估删除时对底层数据对象生成 tombstone最终由 compaction-worker 清理。留存策略按租户配置对应limits中的retention_period参数详见 参考配置参数对应文档docs/sources/configure-server/configure-server/下的配置参考。清理进程运行在 Raft leader 上列出分区 → 应用留存策略 → 识别待删分区 → 向 Raft 提议删除 → 为受影响 block 创建 tombstone → 压缩时处理 tombstone。性能特征即使在大规模下索引存储也仅需数 GB 磁盘shard cache 与 block cache 两级缓存让查询达到亚毫秒级写入吞吐受限于 Raft 共识对常规写入速率通常足够。压缩机制Small Job First 与租约模型压缩流程压缩由 metastore 协调、compaction-worker 执行循环过程为worker 轮询任务 → metastore 分配含源 block 的任务 → worker 下载源 segment → 合并为 block → 上传压缩后的 block → 上报完成 → metastore 更新索引。压缩服务的状态变更严格依赖 Raft 保证一致性leader 准备任务状态变更只读→ 变更提交到 Raft 日志 → 所有副本原子应用变更保证各副本对压缩状态视图一致。任务规划与调度Job planner 维护一个按租户、shard、level 分段的 FIFO 队列积攒足够 block 时创建任务压缩绝不跨越租户、shard 或 level 边界。数据布局上每个服务的 profile 数据在 block 内是独立 dataset压缩时合并不同 block 的匹配 dataset、合并 TSDB 索引、合并重写符号与 profile 表输出包含优化后的非重叠 dataset 的 block。Job scheduler 采用Small Job First策略低 level block 优先更小、对读放大影响更大同 level 内先处理未分配任务失败次数更少的任务优先租约更早过期的任务优先。worker 轮询任务时上报自身可用容量调度器据此创建任务自动适配可用资源。任务所有权与容错任务采用租约lease模型worker 获得有限时长的所有权Raft 日志索引充当 fencing tokenworker 须在租约到期前续租租约过期即允许任务重分配。worker 故障时租约过期 → metastore 检测 → 任务重分配给其他 worker → 源 block 保留至压缩成功。反复失败的任务会被降优先级避免阻塞压缩队列。任务状态机Unassigned创建→ InProgress分配→ Success完成移出调度或 LeaseExpired放弃→ Reassigned 回 InProgress或超过失败阈值进入 Excluded故障任务。两阶段块删除压缩成功后源 block 不会立即删除先在 metastore 创建 tombstone经过可配置的延迟后才真正删除给在途查询留出发现新压缩块、停止访问源块的时间最终 tombstone 进入压缩任务由 worker 从对象存储移除源对象防止压缩期间查询失败。部署模式与资源规划微服务模式生产推荐每个组件独立进程运行可独立伸缩、故障隔离、按需分配资源、独立滚动更新。推荐实例数组件实例数有状态说明Distributor2否按写入速率伸缩Segment-writer2否按写入速率伸缩Metastore3 或 5是Raft 需奇数节点Compaction-worker2否按压缩积压伸缩Query-frontend2否按查询负载伸缩Query-backend2否按查询负载伸缩微服务模式必须使用对象存储S3 / GCS / Azure Blob / OpenStack Swift不支持本地文件系统。单节点模式评估、开发或小规模部署可单进程运行全部组件部署简单单二进制、资源需求低、可用本地文件系统。局限无高可用、无法单独伸缩组件、不建议生产使用。Kubernetes 部署使用 Helm chart 并开启 v2 存储。单二进制模式helm install pyroscope grafana/pyroscope --version 1.20.3 \ --set architecture.storage.v1false \ --set architecture.storage.v2true微服务模式helm install pyroscope grafana/pyroscope --version 1.20.3 \ --set architecture.microservices.enabledtrue \ --set architecture.storage.v1false \ --set architecture.storage.v2trueKubernetes 部署注意为 metastore 节点配置持久卷、配置对象存储凭据、为各组件配置资源请求与限制、为 distributor 与 query-frontend 配置 ingress。资源规划要点Metastore唯一需要持久存储的组件——磁盘数 GB 级即使大规模、内存获益于常驻索引、CPU 用于 Raft 共识操作无状态组件主要需要 CPU数据处理、内存在途数据与查询执行、网络对象存储访问对象存储成本按写操作segment flush 与压缩上传、读操作查询执行、存储留存数据三部分规划。从 v1 迁移到 v2三阶段 Helm 实操迁移采用分阶段方式可让两套存储后端并行运行后再完全切到 v2。完整指南见 Migrate from v1 to v2 storage using Helm。前置条件Helm chart1.19.2 或更高helm list -n pyroscope -f pyroscope检查 CHART 列当前运行在 v1 存储上helm get values -n pyroscope pyroscope -o yaml --all | grep -A8 storage: | grep -E v1:|v2:应显示v1: true与v2: false已配置对象存储——v2 直接写对象存储不使用本地磁盘存 block。S3 示例pyroscope: structuredConfig: storage: backend: s3 s3: endpoint: s3.us-east-1.amazonaws.com bucket_name: pyroscope-data access_key_id: ${AWS_ACCESS_KEY_ID} secret_access_key: ${AWS_SECRET_ACCESS_KEY}其他后端GCS、Azure、Swift可参考对象存储后端配置文档filesystem后端也可用但在 Kubernetes 中需要所有 Pod 共享的ReadWriteMany卷。以下命令假定 Pyroscope 安装于pyroscope命名空间、release 名为pyroscope。三阶段总览阶段发生什么可回滚1. 双写入Dual ingestv2 组件与 v1 并行部署写入同时进入两套存储是2. 验证Validate双后端并行运行至少 24 小时验证 v2 数据与压缩是3. 移除 v1Remove v1移除 v1 组件读写全部由 v2 服务部分单二进制模式迁移Phase 1 开启双写入单二进制进程同时启用 v1/v2 存储模块写入进入两套后端读路径同时服务 v1 与 v2 数据helm upgrade -n pyroscope pyroscope grafana/pyroscope --version 2.0.0 \ --reset-then-reuse-values \ --set architecture.storage.v1true \ --set architecture.storage.v2true验证Pod 已重启运行kubectl get pods -n pyroscope -l app.kubernetes.io/instancepyroscopeHelm release notes 显示迁移激活helm get notes -n pyroscope pyroscope期望输出Write traffic will be written to: 100% v1: ingester / 100% v2: segment-writermetastore Raft 已初始化日志中出现entering leader statesegment-writer ring 健康curl -s http://localhost:4040/ring-segment-writer | grep -o ACTIVE | wc -l单二进制模式应为 1。Phase 2 验证 v2 工作正常并行运行至少 24 小时。用profilecli query series --url http://localhost:4040 --from now-1h --to now确认 v2 数据可查询检查压缩任务完成日志中出现msgcompaction finished successfully input_blocks20 output_blocks1之类行压缩通常写入后几分钟内开始用 PromQLsum(rate(pyroscope_request_duration_seconds_count{status_code~5..}[5m]))确认错误率未上升。Phase 3 切换到 v2确认无需再查询 Phase 1 之前的数据后helm upgrade -n pyroscope pyroscope grafana/pyroscope --version 2.0.0 \ --reset-then-reuse-values \ --set architecture.storage.v1false \ --set architecture.storage.v2true警告此后 Phase 1 之前摄入的数据将无法再通过 Pyroscope 查询数据仍在对象存储但服务它的 v1 存储模块已禁用。迁移开始前请确认无需查询历史数据。微服务模式迁移Phase 1在既有 v1 安装旁部署 v2 组件segment-writer、metastore、compaction-worker、query-backenddistributor 同时向两套后端写入helm upgrade -n pyroscope pyroscope grafana/pyroscope \ --reuse-values \ --set architecture.microservices.enabledtrue \ --set architecture.storage.v1true \ --set architecture.storage.v2true验证点与单二进制模式对应差异metastore Raft leader 选举日志用-l app.kubernetes.io/componentmetastore过滤segment-writer ring 通过svc/pyroscope-distributor端口转发检查ACTIVE 数量应等于 segment-writer 实例数查询验证端口转发到svc/pyroscope-query-frontend压缩日志用-l app.kubernetes.io/componentcompaction-worker过滤。Phase 3--set architecture.storage.v1false --set architecture.storage.v2trueHelm chart 会自动移除 v1 专属组件ingester、compactor、store-gateway、querier、query-scheduler即使 values 文件中仍保留这些组件的覆盖配置。回滚Phase 1 或 2 期间直接--set architecture.storage.v1true --set architecture.storage.v2false回到纯 v1双写入期间写入 v2 的数据成为孤儿数据但不影响 v1 运行。Phase 3 之后需重新部署 v1 组件architecture.storage.v1true且v2true回到双写入模式注意 Phase 3 与回滚之间摄入的数据只写入了 v2v1 读路径不可见。Helm values 速查存储层开关Value类型默认说明architecture.storage.v1booltrue启用 v1 存储及其组件ingester、store-gateway、querier、compactorarchitecture.storage.v2boolfalse启用 v2 存储及其组件segment-writer、metastore、compaction-worker、query-backend迁移调优仅双写入模式生效均在architecture.storage.migration下Value类型默认说明prefix.ingesterWeightfloat1.0发送到 v1 ingester 的写入流量比例[0, 1]prefix.segmentWriterWeightfloat1.0发送到 v2 segment-writer 的写入流量比例[0, 1]prefix.queryBackendbooltrue读取启用 v2 query backendprefix.queryBackendFromstringautov2 读路径开始服务流量的 RFC 3339 时间戳如2025-01-01T00:00:00Zauto时 query-frontend 按租户向 metastore 查询 v2 数据首次出现时间租户无 v2 数据时查询回退 v1结语Pyroscope v2 通过「对象存储即持久层 Raft 元数据索引 全无状态读写路径 任务化压缩」的组合把持续分析平台的读写路径彻底解耦写入端以单 shard 单对象与同步落盘换来了低成本与强持久性读取端以内存元数据规划与树状并行执行换来了瞬时扩展能力压缩端以 lease tombstone 机制保证了数据一致性与查询可用性。无论是全新部署 v2还是通过双写入三阶段从 v1 平滑迁移本文涉及的组件职责、放置算法与 Helm 参数都可作为落地实践的参考基线。【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscope创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表