Kubernetes v1.37 中,etcd RangeStream 随 etcd v3.7 升级为 Beta。它针对 API Server 初始化或重建 watch cache 时的大规模读取,降低 API Server 与 etcd 的内存占用,并让峰值内存更容易预测。
大型 List 请求为什么容易吃满内存
API Server 通常通过内存中的 watch cache 响应 List 和 Watch 请求。但在启动或缓存重新初始化时,它仍然需要从 etcd 读取某类资源的完整状态。Pod 数量多、单个对象体积大时,这个过程会产生明显的内存压力。
API Server 过去已经使用分页读取:每次向 etcd 请求固定数量的 key,而不是一次取完整集合。不过,按 key 数量分页并不了解对象大小。一页里如果恰好包含许多大型对象,响应仍然可能非常大。
更麻烦的是,传统的 unary Range 会先在 etcd 中组装完整页面,再发送给 API Server。API Server 解码期间也需要持有这份数据,因此同一批 payload 会同时存在于两端。并发初始化、对象体积和页面大小叠加后,可能触发 OOM。
RangeStream 把“按 key 分页”变成流式读取
etcd v3.7 提供了 RangeStream RPC。它使用与 Range 相同的 RangeRequest,并返回相同的结果集,但不会先构造完整响应,而是将结果拆成多个 chunk 后持续发送。
RangeStream 会根据返回值自适应调整 chunk 大小。对于大型对象集合,内存边界更多由字节数决定,而不只是由 key 数量决定。API Server 收到一个 chunk 后立即解码并释放,再请求下一个 chunk;etcd 也不必一直保留完整页面。
这并不改变 List 的语义,也不是把所有读取改成无限制的逐对象请求。它改变的是传输和缓冲方式:完整集合不再需要同时驻留在 etcd 和 API Server 的内存中。
启用后,API Server 会在从 etcd 读取完整集合的场景使用 RangeStream,包括:
- watch cache 初始化;
- watch cache 重建;
- 无法从缓存直接服务时,List 请求回退到 etcd 的路径。
版本、Feature Gate 与兼容行为
使用 RangeStream 需要满足以下条件:
- Kubernetes v1.37 或更高版本;
- etcd v3.7 或更高版本;
- API Server 的
EtcdRangeStreamFeature Gate 已启用。
在 Kubernetes v1.37 中,该 Feature Gate 处于 Beta 阶段并默认开启。可以这样显式关闭:
kube-apiserver \
--feature-gates=EtcdRangeStream=false
API Server 会在启动时探测 etcd 是否支持 RangeStream。如果运行时调用返回 Unimplemented,它也会回退到原有的分页 Range 路径。因此,API Server 与旧版本 etcd 配对时仍能工作,但不会获得流式读取的内存收益。
如何确认 RangeStream 正在工作
API Server 会在 etcd 指标中使用独立的 operation 标签记录流式读取。可以在 API Server 的指标端点查询:
curl -s http://127.0.0.1:8080/metrics \
| grep 'etcd_request_duration_seconds_count.*operation="listStream"'
也可以使用 Prometheus 查询:
etcd_request_duration_seconds_count{operation="listStream"}
该指标的 count 大于零,说明至少有流式读取已经发生。如果长期为零,常见原因是 etcd 版本低于 v3.7,API Server 正在使用分页 Range 路径,或者 Feature Gate 被关闭。
实际排查时,建议同时确认版本和启动参数:
kubectl version
kubectl get pods -n kube-system -l component=etcd -o wide
ps -ef | grep '[k]ube-apiserver' | grep -o -- '--feature-gates=[^ ]*'
上面的命令需要根据集群部署方式调整。静态 Pod 集群通常可以在 /etc/kubernetes/manifests/ 中查看 API Server 参数;托管集群则需要通过云厂商提供的控制面配置确认。
采用时要关注什么
RangeStream 主要缓解大规模集合读取时的瞬时内存压力,不会消除对象数量增长、对象过大或 watch cache 本身的成本。升级后仍应关注 API Server 和 etcd 的内存、重启时间、缓存重建时间以及 OOM 事件。
可以按下面的清单推进:
- 确认 Kubernetes 已升级到 v1.37 或更高版本;
- 确认 etcd 已升级到 v3.7 或更高版本;
- 检查
operation="listStream"指标是否出现; - 在大规模资源和 API Server 重启场景下观察内存峰值;
- 如果需要回滚或进行对比测试,使用
EtcdRangeStream=false关闭 Feature Gate。
RangeStream 的价值不在于让每一次 List 都更快,而在于让大型读取不再因为单页对象大小不可预测而制造尖锐的内存峰值。对于资源规模较大的集群,这是 Kubernetes v1.37 中一项值得重点验证的控制面改进。