phosae / phosae.github.io

share interesting things
0 stars 0 forks source link

搞懂 K8s apiserver aggregation #22

Open phosae opened 1 year ago

phosae commented 1 year ago

https://www.zeng.dev/post/2023-k8s-apiserver-aggregation-internals/

Wangmin362 commented 1 year ago

大佬,您也太牛逼了吧,写的真好,醍醐灌顶

xuzhenglun commented 11 months ago

有个比较困惑的点,我的理解不知道是否正确:

通过 RecommandOptions 初始化的自定义 API 会启动 APF 流控,同时会尝试 Patch FlowSchema 和 PriorityLevelConfigurations 对象的 Status。 那么:

  1. filterChain 既然是在聚合API前执行的,感觉后面被代理的 API 并不需要开启流控。我下了断点的确流控的中间件并不会被执行两次,也符合猜想。
  2. 在一些情况下,比如自定义 API 和 kube-apiserver 版本存在差异的情况下,可能会因为 FlowControl 对象的版本不兼容导致自定义 API 访问失败,或者对象状态在不同版本 API 之间 Patch 产生抖动。

所以感觉既然没有必要,后面自定义 API 的流控逻辑是不是可以关闭。不过目前的代码,似乎关闭之后flag上的限流开关会生效,这部分是否存在啥最佳实践呀,比如把值配置的比较大防止被二次限流。

phosae commented 11 months ago

@xuzhenglun

没太理解这个表述,能举个具体例子吗?

在一些情况下,比如自定义 API 和 kube-apiserver 版本存在差异的情况下,可能会因为 FlowControl 对象的版本不兼容导致自定义 API 访问失败,或者对象状态在不同版本 API 之间 Patch 产生抖动。

xuzhenglun commented 11 months ago

@phosae

多谢回复,其实这句话其实是来自社区文档:https://kubernetes.io/docs/concepts/cluster-administration/flow-control/#mandatory-configuration-objects

Each kube-apiserver independently maintains the mandatory and suggested configuration objects, using initial and periodic behavior. Thus, in a situation with a mixture of servers of different versions there may be thrashing as long as different servers have different opinions of the proper content of these objects.

这两个问题我前两天仔细看了下代码基本上搞明白了,我自己尝试回答下,有错误的话麻烦指正:

  1. filterChain 只包在了 APIServerHandlerFullHandlerChain 上,Director 上并没有被包含 filterChain。 实际委托的时候是通过 Director -> NonGoRestfulMux 委托出去的,所以后续请求不会走到另外一个 filterChain 里去了。
  2. APF 会定时 Patch FlowSchemaPriorityLevelConfigurationstatus,这个行为包括自定义 API 也会。 K8S API 和自定义 API 内 apimachinary 库版本不一样的时候,可能出现读写对象行为不一致的现象。 具体的例子的话,我这边自定义 API 是 1.28 的库写的,跑在 1.24 的 K8S API 上会因为还没有 v1beta3 的对象而报错。workaround 是把 APF 关掉换成旧的限流器, 因为从上面 1 的分析得出,此时 K8S 的 API 内的 APF 还是会生效,为自定义 API 提供限流功能。

另外在分析代码的时候,发现似乎博客文章在 API 委托链的图那边有一点微小的不准确。虽然广义上都是委托,但是实际行为实现并不相同:

  1. 当请求通过 FilterChain 后,AggratorServer 会负责处理 APIService 对象的 CURD,其他对象统一由 NonGoRestfulMux 处理。AggratorServer 中存在一个叫 APIServiceRegisterController 的控制器,会负责将为所有 APIService 对象生成 ProxyHandler 对象并注册进 NonGoRestfulMux 中。此时,无论是原生对象还是 CRD 对象,都会被通过 local 代理委托交给 KubeAPIService 中 Director 继续处理;
  2. 当 APIService 中无论是 GoRestfulContainer 还是 NonGoRestfulMux 都没有匹配到路由的时候,请求会进入 NotFoundHandler,而注册在其中的就是 APIExtensionServer 的Director

apf drawio

phosae commented 11 months ago

@xuzhenglun

  1. APF 会定时 Patch FlowSchemaPriorityLevelConfigurationstatus,这个行为包括自定义 API 也会。 K8S API 和自定义 API 内 apimachinary 库版本不一样的时候,可能出现读写对象行为不一致的现象。 具体的例子的话,我这边自定义 API 是 1.28 的库写的,跑在 1.24 的 K8S API 上会因为还没有 v1beta3 的对象而报错。workaround 是把 APF 关掉换成旧的限流器, 因为从上面 1 的分析得出,此时 K8S 的 API 内的 APF 还是会生效,为自定义 API 提供限流功能。

社区文档说「可能存在不同版本的 kube-apiserver 共同管理存储在 etcd 中的 (FlowSchema| PriorityLevelConfiguration).flowcontrol.apiserver.k8s.io」,这种情况通常发生在集群升级期间。如文档所言,kube-apiserver 在迭代时会尽最大努力保证跨版本兼容(alpha API 和 beta API 保证性一般,正式 API v1 v2 保证很强。

如果升级时,发生 beta 到 v1 之类 API 迁移,kubeadm 之类的集群管理工具会在升级之际一并迁移资源对象。

以上说的是 K8s 自身升级过程中的兼容。

自定义 API 会定时 Patch FlowSchema 和 PriorityLevelConfiguration 的 status,这点不是很明白。FlowSchema 和 PriorityLevelConfiguration CRUD 应该是在 kube-apiserver 完成。其他进程并不直接管理它们,但可以通过 K8s API 操作对应资源。

如果三方程序使用 client-go 操作 FlowSchema 或 PriorityLevelConfiguration,那么应该根据 kube-apiserver 版本调整 client-go 为对应版本。

委托图谢纠正 🙏,我后续会做修复