首页
/ etcd 版本维护指南:CHANGELOG 规范、生产版本推荐与 v3.5 数据损坏事件的技术复盘

etcd 版本维护指南:CHANGELOG 规范、生产版本推荐与 v3.5 数据损坏事件的技术复盘

2026-09-05 23:48:04作者:魏献源Searcher

etcd 仓库中的 CHANGELOG/README.md 看似简短,却集中定义了三件与生产运维直接相关的事:哪些 etcd 版本可以安全用于生产、v3.5 早期版本数据损坏问题的官方处置口径、以及项目撰写变更历史(changelog)的书写规则。本文以该文档为主线,结合仓库内的 changelog 文件、版本定义源码与损坏检测实现,帮你建立一套“选对版本、读懂变更、规避已知风险”的完整方法。

CHANGELOG 目录的组织方式:按 minor 版本归档

etcd 将每个 minor 版本族的变更历史单独归档在一个 Markdown 文件中。从 CHANGELOG/ 目录可以看到,仓库维护了从 v2.3 一直到 v4.0 的完整序列:

这种“一个 minor 版本一个文件”的约定与下面的书写规则直接呼应:因为 patch 版本的 changelog 只记录相对上一个 patch 的增量,所以单个文件会随时间不断追加新条目。以 CHANGELOG/CHANGELOG-3.5.md 为例,文件开头就声明了“上一份变更历史请查看 CHANGELOG-3.4”,随后按 ## v3.5.34 (TBC)## v3.5.33 (2026-07-23) 这样的版本小节倒序排列;每个版本小节内部再按 etcd serveretcdctlPackage clientv3Dependencies 等组件分类,逐条列出修复项及其来源 PR。这种结构让“某个修复从哪个版本开始可用”这类问题可以直接在对应文件中线性检索到答案。

生产环境版本推荐:v3.4.22+ 与 v3.5.6+

CHANGELOG/README.md 给出的第一条硬性建议是:

生产环境推荐的最低 etcd 版本为 v3.4.22+v3.5.6+,更多细节参考官方的版本策略(versioning policy)。

这条推荐并非随意划定,它对应的是“每个 minor 版本维护到一定成熟度后才对外背书”的维护策略:v3.4 系列需要至少到 .22、v3.5 系列至少到 .6,之前的 patch 版本可能存在尚未回填的稳定性问题。对于运维人员,实际操作就是两条:

  1. 部署 v3.4 系时,镜像/二进制版本不得低于 v3.4.22;
  2. 部署 v3.5 系时,版本不得低于 v3.5.6。

仓库源码可以佐证“版本”在 etcd 中是一组被严格管理的数据。api/version/version.go 集中定义了版本常量,例如 MinClusterVersion(当前分支为 3.0.0,表示本二进制可兼容的最低集群版本)、当前开发版本号(本仓库为 3.8.0-alpha.0),以及按升序排列的 AllVersions(覆盖 3.0 至 4.0 的 semver 常量表)。集群侧则由 server/etcdserver/version/monitor.go 中的 Monitor 负责根据各成员上报的 server 版本、集群版本与降级状态来计算并推进 cluster version / storage version。也就是说,README 中的版本推荐对应到代码层面,就是一套“成员版本 → 集群版本 → 存储版本”的联动机制,选版不仅决定你跑的是哪个二进制,也决定了集群兼容性的边界。

v3.5 数据损坏问题:官方警告与升级要求

README 中专门用一个小节标注了 v3.5 的数据损坏问题,原文要点如下:

  • 在高负载下运行 v3.5.2、v3.5.1、v3.5.0,可能出现数据损坏;
  • 具体表现为:如果 etcd 进程被 kill,部分已提交的事务可能没有在所有成员上体现(committed transactions are not reflected on all the members);
  • 官方建议升级到 v3.5.4+
  • 如果已经发生数据损坏,官方文档提供了专门的数据损坏修复指南(data corruption 文档)供排查与恢复。

根因:consistent index 丢失原子性

仓库内的 Documentation/postmortems/v3.5-data-inconsistency.md 对这一问题有完整的公开复盘,可以作为 README 警告的技术底注:

  • 状态的双份存储:etcd v3 的状态在磁盘上以 WAL(写前日志)和数据库快照两种形式保存。数据库通过 consistent index(CI,一致索引)元数据记录“自己重放到 WAL 的哪一条”。
  • 原子性要求:数据库状态与 CI 的更新必须是原子提交。若只更新 CI 而未落盘对应变更,重启后该批 WAL 条目会被跳过;若只落了变更而未更新 CI,则会被执行两次。
  • 缺陷引入:v3.5.0 为简化 CI 管理引入了 backend hooks 机制(对应 PR #12855),在提交事务时自动把内存中的 CI 值写入数据库。问题在于内存中的 CI 是共享的:周期性提交(periodic commit)可能在 apply 流程“设置了新 CI、但尚未落盘变更”的窗口内先把 CI 存盘,从而形成一个“CI 前进了、变更却没应用”的空洞。
  • 触发条件:恰好在这个窗口内进程崩溃。复盘记录了触发场景——etcd 在高请求压力、高内存压力(频繁 OOM)下被 SIGKILL 直接杀死,恢复时 etcd 会认为失败 apply 的变更“已经执行过”而跳过,导致该成员数据库缺数据。

该 postmortem 的时间线还记录了几个关键节点:问题代码于 2021-05 合入,随 v3.5.0(2021-06-16)发布,2021-12 起陆续有损坏报告,2022-03-25 由 maintainer 确认,修复版本(postmortem 记为 v3.5.3,发布时间 2022-04-24)随后发布——这也解释了 README 中“推荐升级到 v3.5.4+”的保守口径:它要求你跳过整个有问题的早期窗口,并且留出至少一个 patch 的余量。

损坏如何被检测:HashKV 与 corrupt check 实现

postmortem 指出:单成员集群中该问题完全不可检测(当时没有校验数据库与 WAL 是否一致的工具);多成员集群中,受损成员会对同一个 revision 算出不同的数据库哈希,可通过 HashKV gRPC 调用暴露差异。etcd 为此提供了两套检测入口:

  • --experimental-initial-corrupt-check:启动时对初始哈希与对端比对;
  • --experimental-corrupt-check-time:周期性重复该检查。

其实现位于 server/etcdserver/corrupt.gocorruptionChecker.InitialCheck() 在对外提供 peer/client 流量前执行,先取本地 HashByRev(0) 的哈希与 revision,再通过 PeerHashByRev(rev) 收集对端在同一 revision 上的哈希逐一比较;PeriodicCheck() 则周期性执行同样的比对逻辑。检查周期由 server/embed/config.go 中的 CorruptCheckTime 字段(对应命令行标志 corrupt-check-time)控制。

postmortem 同时诚实地列出了检测机制的缺陷:HashKV 依赖所有成员都能提供目标 revision 的哈希,在成员很慢、或损坏已导致 revision 分叉时会失效,因此该检查只在“崩溃重启后刚启动”阶段最可靠。针对这类教训,CHANGELOG/CHANGELOG-4.0.md 记录了 v4.0 的规划方向:--experimental-initial-corrupt-check--experimental-corrupt-check-time 被去实验化,改为 --initial-corrupt-check(默认 true)与 --corrupt-check-time(默认 12h),把损坏检测从“可选实验特性”变成默认开启的稳定性基线。如果你在规划 v4.0 升级,这类默认行为变化属于必读项。

Changelog 书写规则:两条增量原则

README 的第二部分是维护者撰写变更历史时必须遵守的规范,共两条,核心思想是“每条 changelog 只描述相对基线的新增内容,不做全量复述”:

  1. patch 规则:每个 patch 发布只包含相对上一个 patch 发布的变更。例如 v3.5.5 的 changelog 只应包含 v3.4.x 序列中 v3.5.4 之后新增的条目。
  2. 首个 minor/major 版本规则:每个 minor 或 major 版本的首个发布(如 3.4.0、3.5.0、3.6.0、4.0.0)只包含相对上一个 minor/major 首个发布的新增内容。例如 v3.5.0 只记录相对 v3.4.0 的新内容,v3.6.0 只记录相对 v3.5.0 的新内容。

这两条规则配合“一个 minor 一个 changelog 文件”的目录约定,形成了可追溯的增量链:读 v3.5.33 的小节只看到该 patch 的增量,而想了解 v3.5 相对 v3.4 的全部变化,就去看 v3.5.0 小节——它按规则只包含相对 v3.4.0 的增量,天然避免了跨版本条目重复。

从仓库实际内容看,这一规则执行得相当严格:CHANGELOG/CHANGELOG-3.5.md 中每个 patch 小节都以 ## vX.Y.Z (日期) 开头(未定日期则标注 TBC),条目按组件分类;CHANGELOG/CHANGELOG-4.0.md 作为 major 版本首发的 changelog,则集中列出 Breaking Changes(如移除 v2 proxy、--proxy* 标志弃用、v2 存储后端弃用、损坏检查标志转正等),符合“首个 major 版本 changelog 描述相对上一个 major 的全部新内容”的约定。

实操建议:把 README 的版本建议落到运维流程

结合上述内容,可以归纳出一条可执行的检查清单:

  1. 选型:对照 CHANGELOG/README.md 的最低推荐线(v3.4.22+ / v3.5.6+)选择二进制版本,不要停留在推荐线之下的早期 patch;
  2. 升级确认:若集群仍运行 v3.5.0–v3.5.2,参照 v3.5 数据损坏警告立即升级到 v3.5.4+,并参考 Documentation/postmortems/v3.5-data-inconsistency.md 了解该问题的触发条件与检测手段;
  3. 开启检测:确认部署配置中启用了损坏检查(--corrupt-check-time / initial corrupt check,见 server/embed/config.go),v4.0 环境下该检查将成为默认行为;
  4. 跟踪变更:升级前通读目标版本在对应 CHANGELOG/ 文件中的增量小节,重点关注 etcd server 分类下的行为变化与 Dependencies 分类下的依赖/工具链变更。

需要说明的是,以上版本推荐与损坏警告均以当前仓库 CHANGELOG/README.md 的原文为准;随着仓库演进(当前开发线已推进到 v3.8 alpha,v4.0 changelog 已在编写中),具体推荐版本与标志名称可能发生变化,部署前应以对应维护分支的最新 CHANGELOG 内容为准。

登录后查看全文
热门项目推荐
相关项目推荐