• k8s-List机制


    1、kube-apiserver 核心职责

    提供Restful API;代理集群组件,如dashboard、流式日志、kubectl exec 会话;缓存全量的etcd 数据且无状态服务可水平扩展。

    2、kube-List操作

    k8s在两级 List/ListWatch(但数据是同一份):

    (1)apiserver List/ListWatch etcd;

    (2)基础服务 List/ListWatch apiserver;

    因此,从最简形式上来说,apiserver 就是挡在 etcd 前面的一个代理(proxy)。

    绝大部分情况下,kube-apiserver 都会直接从本地缓存提供服务(因为它缓存了etcd全量数据),某些特殊情况,apiserver 就只能将请求转发给 etcd,例如

    (1)客户端明确要求从 etcd 读数据,追求最高的数据准确性,客户端 LIST 参数设置不当也可能会走到这个逻辑;

    (2)apiserver 本地缓存还没建好

    3、请求举例

    1、LIST apis/cilium.io/v2/ciliumendpoints?limit=500&resourceVersion = 0

    resourceVersion=0 表示会从apiserver缓存中获取数据,resourceVersion=""表示从etcd获取数据,etcd是 KV 存储,能支持limit/continue和namespace过滤,其余的 label/field 过滤功能都需要kube-apiserver处理,所以 resourceVersion=0 会导致 limit=500被忽略,导致客户端拿到的是全量 ciliumendpoints 数据。

    2、LIST api/v1/pods?filedSelector=spec.nodeName%3Dnode1

    %3D 是 = 的转义,这个请求是根据 nodename 做过滤,获取 node1 上的所有 pods,感觉数据量不太大,但其实背后要比看上去复杂:

    (1)没有指定 resourceVersion=0会导致 apiserver 跳过缓存,直接去 etcd 读数据;

    (2)etcd 只是 KV 存储,没有按 label/field 过滤功能(只处理 limit/continue),因此apiserver 是从 etcd 拉全量数据,然后在内存做过滤,再返回给客户端,开销是很大的。

    3、LIST api/v1/pods?filedSelector=spec.nodeName%3Dnode1&resourceVersion = 0

    resourceVersion=0表示 apiserver 会从缓存读数据,性能会有量级的提升,但是apiserver需要在内存做过滤再返回给客户端, apiserver 需要处理的数据量可能会非常大。

    4、LIST 全量 pod

    podList, err := Client().CoreV1().Pods("").List(ctx(), ListOptions{FieldSelector: "spec.nodeName=node1"})

    我们来实际看一下它背后的数据量,以一个 4000 node,10w pod 的集群为例,全量 pod 数据量在etcd 中紧凑的非结构化 KV 存储,在 1GB 量级;在apiserver 缓存中已经是结构化的 golang objects,在 2GB 量级;kube-apiserver 返回,client 一般选择默认的 json 格式接收, 也已经是结构化数据,在 2GB 量级。

    可以看到,某些请求看起来很简单,只是客户端一行代码的事情,但背后的数据量是惊人的。指定按 nodeName 过滤 pod 可能只返回了 500KB 数据,但 apiserver 却需要过滤 2GB 数据,最坏的情况,etcd 也要跟着处理 1GB 数据。在集群规模比较小的时候,这个问题可能看不出来(etcd 在 LIST 响应延迟超过某个阈值 后才开始打印 warning 日志);规模大了之后,如果这样的请求比较多,apiserver/etcd 肯定是扛不住的。

    4、如何判断是否必须从 etcd 读数据

    shouldDelegateList()

    1. //https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go#L591
    2. func shouldDelegateList(opts storage.ListOptions) bool {
    3.   resourceVersion := opts.ResourceVersion
    4.   pred           := opts.Predicate
    5.   pagingEnabled   := DefaultFeatureGate.Enabled(features.APIListChunking)     // 默认是启用的
    6.   hasContinuation := pagingEnabled && len(pred.Continue) > 0                   // Continue 是个 token
    7.   hasLimit       := pagingEnabled && pred.Limit > 0 && resourceVersion != "0" // 只有在 resourceVersion != "0" 的情况下,hasLimit 才有可能为 true
    8.   // 1. 如果未指定 resourceVersion,从底层存储(etcd)拉去数据;
    9.   // 2. 如果有 continuation,也从底层存储拉数据;
    10.   // 3. 只有 resourceVersion != "0" 时,才会将 limit 传给底层存储(etcd),因为 watch cache 不支持 continuation
    11.   return resourceVersion == "" || hasContinuation || hasLimit || opts.ResourceVersionMatch == metav1.ResourceVersionMatchExact
    12. }

    客户端未设置 ListOption{} 中的 ResourceVersion 字段,会对应到这里的 resourceVersion == ""从而导致从 etcd 拉全量数据;

    客户端设置了 limit=500&resourceVersion=0 不会导致下次 hasContinuation==true,因为resourceVersion=0 将导致 limit 被忽略仍会返回全量数据。

    因此,未指定resourceVersion、resourceVersionMatch=exact(即同时resourceVersion=非零值)、有limit/continue都会直接从etcd读数据。

    5、resourceVersion的语义

    1、对于Get resourceVersion的语义

    任何版本指的是最新可用资源版本优先,但不要求强一致性。

    2、对于List resourceVersion的语义

    从 v1.19 版本开始,apiserver支持list在resourceVersion=<非零值>的同时携带resourceVersionMatch参数,来确定如何解析 resourceVersion。

    resourceVersionMatch=Exact,表示精确匹配resourceVersion,如果resourceVersion找不到则返回 HTTP 410 (Gone)的响应。

    resourceVersionMatch=NotOlderThan,不表示老于指定版本的resourceVersion,最新可用资源版本优先。

    3、对于Watch resourceVersion的语义

    6、部署和调优建议

    1、Get/List 请求设置 ResourceVersion=0,client-go 的 ListWatch/Informer 接口默认已经设置了 ResourceVersion=0

    2、优先使用 namespaced API;

    3、Restart backoff,对于kubelet、cilium-agent、daemonsets等需要通过有效的 restart backoff 降低大面积重启时对kube-apiserver的压力;

    4、频繁的list操作尤其是带筛选条件的list建议使用informer的 ListWatch 机制,将数据拉到本地,业务逻辑根据需要自己从 local cache 过滤。如果只是一次性的 list操作,并且有筛选条件, 那显然应该通过设置 label 或字段过滤器,让 apiserver 帮我们把数据过滤出来。同时不要忘记在请求中同时带上 resourceVersion=0。

    7、源码分析

    可以参考 K8s 集群稳定性:LIST 请求源码分析、性能评估与大规模基础服务部署调优

  • 相关阅读:
    关于Blob链接的一些开发应用
    Oracle高级
    C++之前置声明
    基于二维小波变换的散斑相位奇异构造算法matlab仿真
    (函数)颠倒字符串顺序(C语言)
    Jmeter之性能测试
    AS400-RPG-01
    STM32控制max30102读取血氧心率数据(keil5工程)
    spring 注入 当有两个参数的时候 接上面
    现代企业管理笔记——领导
  • 原文地址:https://blog.csdn.net/qq_34562093/article/details/127955775