kube-apiserver代码分析 – API多版本再探

在上一篇API多版本初探中已经描述了API资源是如何注册到路由当中,并大概介绍了api请求如何根据优先版本存储到etcd中。本篇文章主要介绍apiserver如何将etcd中的数据按照按照用户指定的版本返回。在探究同一个api group下不同版本资源兼容问题之外,还会探究不同api group下的不同资源是如何做到兼容的。本文基于kubernetes-1.19.11版本。 本文关注的重点内容有: apiserver如何读取解析etcd中的数据 apiserver怎么返回用户指定的版本资源 不同api group怎么做到兼容,如netwoking.k8s.io/v1beta1下的Ingress资源和extensions/v1beta1下的Ingress资源 在阅读源码过程中在protobuf处理关键位置加入了打印堆栈日志,先找出整个函数调用链,再根据链路分析整个执行流程会更容易的多,具体修改和打印的栈信息可在后面的附录中查看。 首先看pkg/master/import_known_versions.go这个文件,该文件中有许多xxx/install的import。 import ( // These imports are the API groups the API server will support. _ "k8s.io/kubernetes/pkg/apis/admission/install" _ "k8s.io/kubernetes/pkg/apis/admissionregistration/install" _ "k8s.io/kubernetes/pkg/apis/apps/install" ... 来看下这个文件的作用。从文件的名称来看就知道是导入所有的所有已知的api版本,在install包的init函数中会将所有已定义的资源加载到runtime schema中,主要包含下面两类: 在pkg/apis/${group}/types.go中定义的类型 在k8s.io/api/${group}/${version}/types.go中定义的 第1类资源代表了内部版本的表示方法,是在etcd中读取的数据(或api请求数据)反序列化之后的表示类型。第2类资源为不同的group下特定version的类型,在写入etcd的(或api响应的)数据是根据特定group/version下的类型进行序列化的。 在install中有一个SetVersionPriority()方法,用来设置写入etcd是优先使用的版本。当然第2类资源在注册的时候还会加上一些设置默认值和转换到内部版本或从内部版本转换的方法来实现内部版本和外部版本之间的互相转换。这些方法的定义在pkg/apis/${group}/${version}下的default.go,conversion.go,zz_generated.xxx.go中定义的。 接下来以Ingress资源来说明转换的读取,转换,返回响应的流程,首先通过加日志查看当前runtime schema中可以看到有三个Ingress类型的定义。分别对应的是extensions/v1beta1,networking.k8s.io/v1beta1和内部版本,可以看到内部版本有两个,分别是extensions/__internal和networking.k8s.io/__internal,但是其他俩对应的实际类型都是pkg/apis/networking/types.go中定义的Ingress类型。 再来回顾一下加载api资源注册路由部分,有一个InstallAPIs()方法,该参数中的networkingrest.RESTStorageProvider{}来自pkg/registry/networking/rest/storage_settings.go中定义的storage接口,这个storage接口设置了ingresses资源对应ingressStorage。 func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { storage := map[string]rest.Storage{} // ingresses ingressStorage, ingressStatusStorage, err := ingressstore.NewREST(restOptionsGetter) if err != nil { return storage, err } storage["ingresses"]…

kube-apiserver代码分析 – API多版本初探

kube-apiserver是k8s中最为核心的组件,对外暴露restful接口,实现对集群中各种资源的增删改查操作。kubelet,kube-*组件都通过apiserver获取自己感兴趣的资源做处理,系统中所有的组件都只负责自己的部分,最终会促使各种资源到达期望的状态。 apiserver中的api资源是由组构成的,叫做ApiGroup,如apps,extensions,每个资源组下面又有不同类型的资源,称为Kind,如Deployment。每个分组下还会有不同的版本,在相同分组的不同版本下面相同的Kind的资源可能会有字段的增删等变更。因此apiserver需要能够正确处理不同版本资源之间的兼容性处理。从废弃策略的规则#2中可以了解到,不同版本之间的资源可以互相转换,并且不丢失任何信息,接下来分析apiserver是具体怎么实现的。本文基于k8s的1.8.0版本版本的代码。 首先看下apiserver的启动流程,下面是关键的函数调用链,去掉了不相关代码和条件判断等。 main() cmd/kube-apiserver/apiserver.go:31 NewAPIServerCommand() cmd/kube-apiserver/app/server.go:99 Run() cmd/kube-apiserver/app/server.go:151 CreateServerChain() cmd/kube-apiserver/app/server.go:169 # 返回的config.ExtraConfig中保存了 CreateKubeAPIServerConfig() cmd/kube-apiserver/app/server.go:273 buildGenericConfig() cmd/kube-apiserver/app/server.go:417 # 加载当前版本默认api资源配置 genericConfig.MergedResourceConfig = master.DefaultAPIResourceConfigSource() cmd/kube-apiserver/app/server.go:431 # 将参数配置中的--runtime-config应用到默认api资源配置 s.APIEnablement.ApplyTo(genericConfig, master.DefaultAPIResourceConfigSource(), legacyscheme.Scheme) cmd/kube-apiserver/app/server.go:449 # 设置存储端后端对应的api操作接口 storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig # 将apiserver和storage的配置返回了,就是说apiserver和storage都是使用的用户自定义的配置对默认配置进行覆盖后的配置 # 返回的 genericConfig.MergedResourceConfig保存了api启用配置,类型为*storage.ResourceConfig # 返回的storageFactory.APIResourceConfig也保存了api启用配置,类型也为*storage.ResourceConfig CreateKubeAPIServer() cmd/kube-apiserver/app/server.go:191 kubeAPIServerConfig.Complete().New() cmd/kube-apiserver/app/server.go:219 # 启用legacy api(/api/v1) m.InstallLegacyAPI() pkg/master/master.go:405 # 启用api(/apis/{groupn}) m.InstallAPIs() m.GenericAPIServer.InstallAPIGroups() pkg/master/master.go:553 `` 其中m.InstallLegacyAPI()是安装旧版接口,及/api/v1接口路径下的接口。我们主要看m.InstallAPIS这部分,这部分安装的接口的都在/apis/{group}路径下。 现在深入到InstallAPIGroups()这个函数中以及看后面的详细调用链。 // Exposes given api groups in the API. func (s *GenericAPIServer) InstallAPIGroups(apiGroupInfos…