You need to enable JavaScript to run this app.
文档中心
容器服务

容器服务

复制全文
下载 pdf
集群
Kubernetes 开发者最佳实践
复制全文
下载 pdf
Kubernetes 开发者最佳实践
本文主要描述使用容器服务过程中关于访问控制面组件、优化客户端访问模式相关建议和最佳实践。​
使用 list-watch 模式替代全量 list​
背景信息​
作为 Kubernetes 开发者,您的组件可能:​
  • 需要定期查询一些 Kubernetes 对象的列表。​
  • 需要在多个节点上运行(尤其 DaemonSet 方式部署的组件)。​
通常情况下通过定期调用 list API 来查询对象状态,但该方法每次必须加载所有对象到 kube-apiserver 内存中进行序列化并传输。这类请求会占用控制面大量 CPU 和内存,使控制面(Control Plane)过载或请求超时,即使定期查询的 Kubernetes 对象状态未发生变化,也会在 kube-apiserver 上造成高负载。​
推荐方案​
强烈建议避免全量 list,替换为 list-watch 模式,即先通过调用 list API 加载所有对象后,后续使用 watch API 获取对象状态的增量变更。list-watch 模式与全量 list 相比,可有效缩短 kube-apiserver 处理请求的时间、减少请求流量,如果对象状态未发生更改,则不会产生额外的负载。​
如果您使用 Golang 语言,请查看 SharedInformer 和 SharedInformerFactory,了解实现 list-watch 模式的 Golang 包。​
补充说明​
无论是全量 list 还是 list-watch 模式,建议在 list 请求中设置 ResourceVersion 参数(ResourceVersion=0),从 kube-apiserver cache 中读取数据,减少 kube-apiserver 与 etcd 数据库的交互次数。​
说明​
  • VKE 的 kube-apiserver 内置了限流规则,避免大量 list 请求导致 kube-apiserver 内存溢出(OOM),但 client-go 在收到限流响应后会自动重试,因此一般情况下不会导致您的 list 请求失败。​
​
ns := "kube-system"​
selector := "app.kubernetes.io/name=xxx"​
labelSelector, err := labels.Parse(selector)​
​
//client-go clienset 客户端​
pods1, kerr := kubeCli.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{​
LabelSelector: selector,​
ResourceVersion: "0",​
})​
​
//controller-runtime 客户端​
pods2 := &corev1.PodList{}​
rerr := runtimeCli.List(ctx, pods2, &client.ListOptions{​
LabelSelector: labelSelector,​
Namespace: ns,​
Raw: &metav1.ListOptions{​
ResourceVersion: "0",​
},​
})​
​
减少 watch 和 list 产生的不必要流量​
Kubernetes 在内部使用 watch API 发送关于对象更新的通知。即便 watch 调用需要的资源比定期 list 调用要少得多,但在大规模集群中的 watch 调用也会占用大量集群资源,影响集群性能。最大的影响来自于创建从多个位置观察频繁更改对象的 watch 调用,例如对运行在多个节点上的组件对应的 Pod 数据进行 watch 调用。集群上安装的第三方代码或扩展程序,可能会在后台创建此类 watch 调用。​
我们推荐以下最佳实践:​
  • 减少 watch 和 list API 调用产生的不必要处理操作和流量。​
  • 避免创建从多个节点观察频繁更改的对象的 watch(例如 DaemonSet)。​
  • 建议在单个节点上创建一个中央控制器,实现对象数据的监听及处理。​
  • 仅对必要的对象进行 watch 操作,避免对集群全量对象进行 watch 操作,例如每个节点上的 kubelet 只 watch 调度到同一节点上的 Pod。​
  • 如果您使用 controller-runtime 库,可以参考 Kubernetes 官网文档 设置 watch 过滤器。代码片段如下:​
  • ​
    ctrl.Options.NewCache = cache.BuilderWithOptions(cache.Options{​
    SelectorsByObject: cache.SelectorsByObject{​
    &corev1.Pod{}: {​
    Field: fields.SelectorFromSet(fields.Set{"spec.nodeName": "node01"}),​
    },​
    },​
    },​
    )​
    ​
  • 如果您直接使用 client-go 库,可以在构造 Informer 时设置 watch 过滤器。代码片段如下:​
  • ​
    lw := cache.NewListWatchFromClient(c.CoreV1().RESTClient(), "pods", metav1.NamespaceAll, fields.OneTermEqualSelector("spec.nodeName", string(nodeName)))​
    informer := cache.NewSharedInformer(lw, &apiv1.Pod{}, resyncPeriod)​
    ​
  • 避免部署需要执行大量 watch 或 list 调用的第三方组件或扩展程序,从而影响集群性能。​
停用 API 访问凭证的自动挂载​
在 Kubernetes 1.22 之前版本,如果您在创建 Pod 时未指定 ServiceAccount(服务账号),Kubernetes 会自动执行以下操作:​
  • 将默认的 ServiceAccount 分配给 Pod。​
  • 挂载 ServiceAccount 的 API 访问凭证作为 Pod 的 Secret。​
  • 对于每个已挂载的 Secret,kubelet 会创建一个 watch,以观察每个节点上该 Secret 的更改。​
在大规模集群中,上述操作意味着数千个不必要的 watch 调用,可能会在 kube-apiserver 上产生大量负载。​
因此如果 Pod 中运行的逻辑不需要访问 Kubernetes API,建议您停用 ServiceAccount 的 API 访问凭证的自动挂载,以避免创建相关的 Secret 和 watch 调用。详细说明,请参见 Opt out of API credential automounting。​
API 序列化协议建议使用 Protobuf​
Kubernetes API 序列化协议建议使用 Protobuf(Protocol Buffers),相比于 JSON 更节省内存和传输流量。更多信息,请参见 Alternate representations of resources。​
无论您使用 controller-runtime 库还是直接使用 client-go 库,都可以在 KubeConfig 中设置编码格式,代码样例如下:​
​
kubeConfig.ContentType = "application/vnd.kubernetes.protobuf"​
kubeConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"​
//controller-runtime​
mgr, err := ctrl.NewManager(kubeConfig, ctrl.Options{})​
//client-go​
kubeClient, err := clientset.NewForConfig(kubeConfig)​
​
​
最近更新时间:2026.10.23 10:37:21
这个页面对您有帮助吗?
有用
有用
无用
无用