当 Kubernetes 集群增长到数万个节点时,监听 Pod 等高基数资源的控制器会遇到扩展瓶颈。水平扩展后的每个控制器副本都会接收 API 服务器的完整事件流,为反序列化所有对象付出 CPU、内存和网络开销,随后又丢弃不由自己负责的对象。增加控制器副本并不会降低每个副本的成本,反而会让总成本成倍增加。
Kubernetes v1.36 引入了处于 Alpha 阶段的服务端分片 List 与 Watch 功能,见 KEP-5866。启用后,API 服务器在源头过滤事件,使每个控制器副本只接收由自己负责的那一部分资源集合。
客户端分片的问题
一些控制器,例如 kube-state-metrics,已经支持水平分片。每个副本被分配一部分键空间,并丢弃不属于它的对象。这在功能上可行,却不能减少从 API 服务器流出的数据量:
- N 个副本各自接收完整事件流:每个副本都会反序列化和处理每个事件,然后丢弃自己不需要的部分。
- 网络带宽需求随副本数量增长,而不是随每个分片的大小变化。
- 用于反序列化被丢弃对象的 CPU 时间被浪费了。
服务端分片 List 与 Watch 将过滤操作上移到 API 服务器。每个副本告诉服务器自己负责的哈希范围,服务器只发送匹配的事件。
工作原理
该功能在 ListOptions 中增加了 shardSelector 字段。客户端使用 shardRange() 函数指定哈希范围:
shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')
API 服务器对指定字段计算确定性的 64 位 FNV-1a 哈希,只返回哈希落入 [start, end) 范围的对象,即包含起点、不包含终点。此规则同时适用于 List 响应和 Watch 事件流。所有 API 服务器实例会得到相同的哈希结果,因此可用于拥有多个 API 服务器副本的集群。
当前支持的字段路径为 object.metadata.uid 和 object.metadata.namespace。
在控制器中使用分片 Watch
控制器通常使用 informer 列举和监听资源。要对工作负载分片,每个副本通过 WithTweakListOptions,将 shardSelector 注入 informer 使用的 ListOptions:
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
)
shardSelector := "shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')"
factory := informers.NewSharedInformerFactoryWithOptions(client, resyncPeriod,
informers.WithTweakListOptions(func(opts *metav1.ListOptions) {
opts.ShardSelector = shardSelector
}),
)
对于包含两个副本的部署,选择器将哈希空间等分为两半:
// Replica 0: lower half of the hash space
"shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')"
// Replica 1: upper half of the hash space
"shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')"
其中,副本0负责哈希空间的下半部分,副本1负责上半部分。单个副本也可以使用 || 覆盖多个不连续范围:
"shardRange(object.metadata.uid, '0x0000000000000000', '0x4000000000000000') || " +
"shardRange(object.metadata.uid, '0x8000000000000000', '0xc000000000000000')"
确认服务器是否支持
API 服务器实际采用分片选择器时,List 响应的元数据中会包含 shardInfo 字段,回显已应用的选择器:
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "10245",
"shardInfo": {
"selector": "shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')"
}
},
"items": [...]
}
如果缺少 shardInfo,就说明服务器没有采用分片选择器,客户端收到的是完整、未过滤的集合。此时客户端应能够处理完整结果集,例如在客户端执行过滤,丢弃不属于自身分片范围的对象。
参与反馈
该功能处于 Alpha 阶段,需要在 API 服务器上启用 ShardedListAndWatch 特性门控。项目希望收到控制器作者和大规模集群运维人员的反馈。
如有问题或反馈,可加入 Kubernetes Slack 的 #sig-api-machinery 频道。
原文:Kubernetes v1.36: Server-Side Sharded List and Watch。作者:Jeffrey Ying(Google);发表于2026年5月6日。本文为中文翻译,保留原文1.36 Alpha版本范围。原文网站仓库采用 CC BY 4.0。代码与命令仅依据原文核对,未在集群中运行。










暂无评论内容