etcd RangeStream 在 Kubernetes v1.37 中进入 Beta 阶段。配合 etcd v3.7,它能够降低 API 服务器与 etcd 读取大型集合所需的内存,让峰值占用更容易预测。
大规模读取的成本
API 服务器从内存中的 watch 缓存处理大多数 list 和 watch 请求。在启动以及每次重新初始化时,填充该缓存都需要从 etcd 读取某类资源的完整状态。如果资源对象数量很多,或者对象很大,例如 Pod,这种读取的成本就很高。
API 服务器已经对这些读取进行分页:每次向 etcd 请求固定数量的键,而不是一次读取整个集合。但按键数量限制的一页并不了解对象大小,因此一页大型对象仍可能占用大量空间。内存占用由此难以预测,当对象大小和并发读取的不利因素叠加时,就足以触发内存不足(OOM)。etcd 的一元 Range 调用会先完整组装一页,再发送给客户端;API 服务器在解码时也持有这页数据,因此同一份载荷同时存在于两端的内存中。大部分成本落在 etcd 上,流式读取对它的帮助也最大。
通过 RangeStream 流式读取
etcd v3.7 为这种读取新增了流式版本,即 RangeStream RPC。它接受与 Range 相同的 RangeRequest,返回相同的结果集。区别是 etcd 不再预先构建整个响应,而是将其拆成分块,逐块流式发送。分块大小根据返回的值自适应调整,因此大型对象集合受到的是字节数限制,而不是键数量限制。随着流向前推进,内存逐步释放,无需等整页组装完毕。
启用该功能后,API 服务器凡是从 etcd 读取整个集合,都使用 RangeStream。这包括 watch 缓存初始化,以及 list 请求无法由缓存满足、需要直接读取 etcd 的回退路径。两种情况下,API 服务器都是收到一个分块就解码,并在拉取下一块之前释放当前分块,因此任何一端都不会同时持有整个集合。
使用要求
- Kubernetes v1.37 或更高版本
- etcd v3.7 或更高版本
当 kube-apiserver 启用了 EtcdRangeStream 特性门控,且 etcd 版本至少为 v3.7 时,才会使用 RangeStream。该特性在 v1.37 中处于 Beta 阶段,默认启用。API 服务器启动时会判断 etcd 是否支持它;如果运行期间某次调用返回 Unimplemented,也会自动回退。因此,配合旧版 etcd 的 API 服务器仍会自行使用分页的 Range 路径。要关闭该功能,禁用门控即可:
--feature-gates=EtcdRangeStream=false
确认 RangeStream 正在使用
API 服务器在 etcd 指标中使用独立的操作标签记录流式读取。下面的计数非零,表示 RangeStream 正在被使用:
etcd_request_duration_seconds_count{operation="listStream"}
如果计数始终为零,API 服务器仍然使用分页 Range 路径,最可能的原因是 etcd 版本低于 v3.7。
进一步了解
如有问题或反馈,可以加入 Kubernetes Slack 的 #sig-etcd 频道。











暂无评论内容