ARTICLE DETAIL

资讯详情

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

Neon Storage Controller 节点 Drain/Fill 机制解析:无感知集群重启的实现原理与操作指南

Neon Storage Controller 节点 Drain/Fill 机制解析:无感知集群重启的实现原理与操作指南 Neon Storage Controller 节点 Drain/Fill 机制解析无感知集群重启的实现原理与操作指南【免费下载链接】neonNeon: Serverless Postgres. We separated storage and compute to offer autoscaling, code-like database branching, and scale to zero.项目地址: https://gitcode.com/GitHub_Trending/ne/neon导读本文基于 Neon 开源仓库中的设计文档 docs/rfcs/033-storage-controller-drain-and-fill.md深入讲解 Storage Controller 如何通过「节点排空Drain与回填Fill」两类后台操作配合节点调度策略状态机让 pageserver 的滚动重启与重新部署对租户tenant几乎无感知。读完本文你将掌握Drain/Fill HTTP API 的完整调用方式与返回码语义、NodeSchedulingPolicy状态机的流转规则、各类故障场景的处理策略以及对应实现在仓库中的关键源码位置。背景为什么需要无感知重启问题来源在 Neon 的分层架构中pageserver 承担存储职责租户的读写路径依赖 pageserver 提供页面服务。pageserver 重启会直接造成租户的读可用性中断RFC 中记录了 2024-04-03 一起真实案例某租户因 pageserver-3 重启按需激活时随机等待了约 30 秒。更隐蔽的问题是负载较高的 pageserver 上大量关闭流程无法在 systemd 强制 10 秒超时 内完成导致未刷盘的 ephemeral layer 被迫丢弃重启后必须重新摄取数据才能服务请求进一步恶化首请求延迟。尽管当时 Storage Controller 托管的 pageserver 租户密度较低、问题尚不尖锐但 Neon 计划最终将所有 pageserver 迁移到 Storage Controller 管理之下因此提前解决该问题具有明确价值。解决思路核心思路是在重启前把节点上的租户分片挪走重启后再挪回来。Storage Controller 新增两类后台操作Draining排空将某个 pageserver 上所有可迁移的租户分片分发给集群中的其他节点Filling回填对称过程将租户分片迁回该 pageserver直到集群恢复均衡、静默的分布状态。配合两类辅助概念节点调度策略Node Scheduling Policies充当调度器的约束例如节点处于Paused策略时不再为其调度新分片部署编排器Deployment Orchestrator指代驱动部署的流程当前是 Ansible playbook。设计需求与非目标RFC 明确列出的需求清单也是判断实现是否到位的验收标准pageserver 重新部署期间租户停机时间最小化Storage Controller 暴露用于排空/回填的 HTTP API供编排进程或人工操作员使用提供取消排空/回填后台操作的 HTTP API排空/回填失败不应致命此时集群重启照常进行接受停机排空/回填进度可通过 metrics 观测。明确排除的范围不与 control plane 集成不为大型非 HA 租户做无感知重启。整体流程编排器视角的操作协议RFC 假设 pageserver 已经按顺序逐个重启编排器为每个 pageserver 的重启实现「序言Prologue 尾声Epilogue」两步协议。Prologue重启前排空编排器先从 control plane 或 pageserver 本身获取节点 ID向 Storage Controller 发出PUT /control/v1/node/:node_id/drain开始排空所有错误响应以短退避重试收到202 (Accepted)即排空已开始轮询节点状态接口GET /control/v1/node/:node_id直到响应中policy字段变为PauseForRestart表明排空完成可以重启 pageserver。序言受整体超时约束量级在数分钟且随托管 pageserver 负载升高应相应增大。Epilogue重启后回填重启完成后编排器发出PUT /control/v1/node/:node_id/fill启动回填所有错误码均可短退避重试该调用本身是同步原语若 pageserver 尚未重新附着到 Storage Controller回填会被拒绝收到202 (Accepted)即回填已开始轮询节点状态接口直到policy变为Active回填完成可继续处理下一个 pageserver。尾声同样有整体超时初始可与序言一致也可依赖 Storage Controller 的后台优化并采用更短超时。若编排器超时则尝试取消回填短退避重试若最终取消失败需要人工介入将调度策略设置为Active——不立即处理虽无大碍但会持续约束调度器。节点调度策略状态机无故障、无超时时的理想流转为Active - Draining - PauseForRestart - Active - Filling - ActiveRFC 给出了完整状态机图docs/rfcs/033-storage-controller-drain-and-fill.md 中 Node Scheduling Policy State Machine 一节要点包括Draining状态下排空完成后进入PauseForRestart排空失败取消 / pageserver 重新附着 / Storcon 重启则回到ActiveFilling状态下回填完成后回到Activepageserver 重启后重新附着时从PauseForRestart回到Active。源码中的策略枚举调度策略枚举定义于 storage_controller/src/node.rs完整取值包括Active、Deleting、Draining、Filling、Pause、PauseForRestart。每个策略对调度器的约束在may_schedule()中体现storage_controller/src/node.rsActive可调度受利用率影响Filling可调度受利用率影响Deleting/Draining/Pause/PauseForRestart不可调度。Drain/Fill HTTP API触发接口排空/回填的触发接口在 storage_controller/src/http.rs 的路由表中注册storage_controller/src/http.rs方法路径语义PUT/control/v1/node/:node_id/drain触发排空DELETE/control/v1/node/:node_id/drain取消排空PUT/control/v1/node/:node_id/fill触发回填DELETE/control/v1/node/:node_id/fill取消回填节点状态轮询接口为GET /control/v1/node/:node_id路由注册见 storage_controller/src/http.rs处理器handle_node_status位于 storage_controller/src/http.rs。说明RFC 草案中的路径写作PUT /v1/control/node/:node_id/{drain,fill}仓库实际实现的路由为/control/v1/node/:node_id/{drain,fill}两者语义一致请以当前仓库代码为准。返回码语义触发接口的 HTTP 非成功返回码全部可从 Storage Controller 视角安全重试404节点未知503节点已知但不可用412排空前置条件失败——没有其他节点可排空或节点调度策略禁止排空409该节点已有排空/回填在进行中每节点同时只允许一个后台操作。排空/回填被接受并开始时返回202取消成功返回200错误可重试。触发前置校验源码依据start_node_drain的实现位于 storage_controller/src/service.rs依次执行节点必须已注册否则 404无其他正在进行的后台操作否则 409 Conflict见 storage_controller/src/service.rs节点必须可用否则 503见 storage_controller/src/service.rs集群中必须存在至少一个可调度节点作为排空目标否则 412见 storage_controller/src/service.rs当前调度策略必须是Active或Pause若是Draining则返回 409其他策略一律 412见 storage_controller/src/service.rs。校验通过后将节点调度策略置为Draining并持久化到内存与数据库node_configure落库实现见 storage_controller/src/persistence.rs随后把操作注册进ongoing_operation并tokio::task::spawn一个独立任务执行排空storage_controller/src/service.rs。回填的校验类似但更严格起始策略只接受Activestart_node_fill位于 storage_controller/src/service.rs。当节点重新附着时若原策略为PauseForRestart或Draining排空可能的终态会自动重置为Active——这一重附着重置逻辑在数据库启动加载节点时同样生效见 storage_controller/src/persistence.rs。Drain 与 Fill 的实现细节Drain 过程RFC 描述与 storage_controller/src/service.rs 的drain_node实现一致对排空节点上每个已附着的租户分片将其降级为 secondary 并尝试重新调度到其他节点调度可能因约束无法满足而失败但这可以接受——排空是**尽力而为best effort**的过程不一定能切换所有分片后台任务限流并发 reconcile 数避免淹没目标 pageserver、并让其他重要 reconcile 得以推进上限为MAX_RECONCILES_PER_OPERATION 64见 storage_controller/src/background_node_operations.rs触发的 reconcile 全部结束或超时后将节点策略置为PauseForRestart以宣告排空结束storage_controller/src/service.rs。若此步骤失败并非致命轮询方会因整体超时兜底策略会在节点重附着或回填时被修正。关于非 HA 租户的特别说明非 HA 租户没有 secondary按上述描述不会被迁移。RFC 认为跳过它们尤其是大型租户是合理的——迁移后目标 pageserver 需要按需下载整个工作集可能比重启本身的扰动更大未来可考虑将小型非 HA 租户纳入。Fill 过程fill_nodestorage_controller/src/service.rs与fill_node_planstorage_controller/src/service.rs实现如下生成回填计划优先把该节点上有 secondary、且该节点在其 home AZ的分片迁回若迁移后仍未达到该节点的公平份额再从附着分片最多的节点提升分片但跳过 home AZ 不匹配的分片对每个回填节点担任 secondary 的租户分片执行secondary 提升promote直到分片耗尽或集群附着分片数重新均衡与排空一样并发 reconcile 受限。后台操作的类型抽象Operation::{Drain, Fill, Delete}、取消令牌CancellationToken与错误枚举OperationError定义在 storage_controller/src/background_node_operations.rs。故障模式与处理策略RFC 的核心设计哲学是任何失败都尽量把节点状态归位到中性的Active以简化实现与推理代价是状态机增加了若干转移边。故障场景处理方式Storage Controller 崩溃重启启动时将Draining/Filling/PauseForRestart状态的节点全部重置为Active——重启后已丢失编排器意图上下文排空中的 pageserver 崩溃pageserver 重启时会重新附着附着时策略自动重置为Active调度器恢复可用排空目标节点崩溃两个合理选项取消排空专注故障切换或两者并行但优先故障切换由于 drain/fill 产生的并发 reconcile 受限后者免费获得RFC 建议采用回填中的 pageserver 崩溃同排空崩溃重附着时重置为Active排空/回填期间节点不可用drain/fill 任务提前停止当心跳检测到节点恢复在线时将其策略重置为Active若发生的是重启走崩溃分支编排器排空超时仍继续重启pageserver 重附着时策略回到Active编排器回填超时尝试取消回填若失败回填继续直至静默节点停留在Filling策略约束调度器但无害由人工重置为Active或未来在 Storage Controller 内置回填超时指标观测排空/回填进度通过 metrics 暴露。仓库中与之直接相关的指标包括storage_controller_reconcile_spawn、storage_controller_reconcile_complete、storage_controller_pending_reconciles因并发限制被阻塞的 reconcile 数等storage_controller/src/metrics.rs后续演化中还增加了storage_controller_drain_and_fill_long_waits用于统计 drain/fill 期间等待节点状态变化的长时间等待次数storage_controller/src/metrics.rs。优化方向Location Warmth切换 secondary 时Storage Controller 会等待其变热下载足够多的租户数据导致部分 reconcile 显著偏慢、占用宝贵的 reconcile 配额。RFC 提出两项优化Drain 阶段只切换已经热的租户Fill 阶段优先回填最热的租户。鉴于近期托管租户数量仍较少首版实现可直接查询租户的 secondary 状态随着租户规模增长未来需要 pageserver 新增 API 端点直接报告热/冷节点集合。备选方案纯调度约束实现RFC 还对比了一种替代设计——将排空/回填完全表达为调度约束而非独立后台任务。作者认为该方案理论上优雅但实现、运维和推理都更困难取消操作需要重建调度意图状态并重新应用难以甄别哪些 reconcile 属于某个 drain/fill为 reconcile 打标签会变得混乱且 reconcile 需要产生持久化副作用排空完成时写库作者在概念上并不认同。因此最终选择了显式后台任务 状态机的方案。附相关实现入口速查关注点仓库位置RFC 原文docs/rfcs/033-storage-controller-drain-and-fill.mdHTTP 路由注册storage_controller/src/http.rs触发/取消排空与回填storage_controller/src/service.rs、storage_controller/src/service.rs排空/回填主流程storage_controller/src/service.rs、storage_controller/src/service.rs后台操作抽象与并发上限storage_controller/src/background_node_operations.rs调度策略枚举与约束storage_controller/src/node.rs策略数据库持久化与启动重置storage_controller/src/persistence.rs、storage_controller/src/persistence.rs相关指标storage_controller/src/metrics.rs该 RFC 还附带一个实现了除优化与部分故障处理外几乎所有内容的 POC对应 PR #7682可作为阅读代码前的高层参照。生产使用前请以当前仓库 storage_controller 目录下的最新实现为准。【免费下载链接】neonNeon: Serverless Postgres. We separated storage and compute to offer autoscaling, code-like database branching, and scale to zero.项目地址: https://gitcode.com/GitHub_Trending/ne/neon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表