写点什么

apiserver 源码分析——启动流程,java 工程师技术栈

用户头像
极客good
关注
发布于: 刚刚

//代码位于 /vendor/k8s.io/apiextensions-apiserver/apiserver.go


func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {


//创建 Generic


genericServer, err := c.GenericConfig.New("apiextensions-apiserver", delegationTarget)


//实例化 CustomResourceDefinitions


s := &CustomResourceDefinitions{


GenericAPIServer: genericServer,


}


//实例化 APIGroupInfo


apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Scheme, metav1.ParameterCodec, Codecs)


if apiResourceConfig.VersionEnabled(v1beta1.SchemeGroupVersion) {


storage := map[string]rest.Storage{}


// customresourcedefinitions


customResourceDefinitionStorage, err := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)


if err != nil {


return nil, err


}


storage["customresourcedefinitions"] = customResourceDefinitionStorage


storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefinitionStorage)


apiGroupInfo.VersionedResourcesStorageMap[v1beta1.SchemeGroupVersion.Version] = storage


}


//另一个版本的类似,不作展示


//InstallAPIGroup 注册


if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {


return nil, err


}


}


KubeAPIServer


KubeAPIServer 处理 k8s 内置资源请求,它的创建流程与 APIExtensionServer 类似,包含下面几个步骤


  • 创建 GeneriAPIServer

  • 实例化 Instance

  • installLegacyAPI

  • installAPI


其中 Instance 是 KubeAPIServer 的 Server 对象。KubeAPIServer 创建和 Install 的 APIGroup 需要调用两个方法,一个是 installLegacyAPI,另一个是 installAPI,原因在于 k8s 的 apiGroup 分了/api 和/apis 两种。初期的资源其实没有 apiGroup 这个概念,而后期引入了 groupVersion 为了兼容原有的才把旧的资源类型的 URI 地址都归属于/api 这个路径下的,新的全部在/apis 这个路径下,因此在创建注册 APIGroup 时都分了两类。代码如下


func CreateKubeAPIServer(kubeAPIServerConfig *controlplane.Config, delegateAPIServer genericapiserver.DelegationTarget) (*controlplane.Instance, error) {


kubeAPIServer, err := kubeAPIServerConfig.Complete().New(delegateAPIServer)


if err != nil {


return nil, err


}


return kubeAPIServer, nil


}


//代码位于 /vendor/k8s.io/kube-aggregrator/pkg/apiserver/apiserver.go


func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Instance, error) {


//创建 Generic


s, err := c.GenericConfig.New("kube-apiserver", delegationTarget)


//1.14 版本的是 Master,当前版本是 Instance


m := &Instance{


GenericAPIServer: s,


ClusterAuthenticationInfo: c.ExtraConfig.ClusterAuthenticationInfo,


}


//实例化核心 API


if c.ExtraConfig.APIResourceConfigSource.VersionEnabled(apiv1.SchemeGroupVersion) {


legacyRESTStorageProvider := corerest.LegacyRESTStorageProvider{


StorageFactory: c.ExtraConfig.StorageFactory,


ProxyTransport: c.ExtraConfig.ProxyTransport,


KubeletClientConfig: c.ExtraConfig.KubeletClientConfig,


EventTTL: c.ExtraConfig.EventTTL,


ServiceIPRange: c.ExtraConfig.ServiceIPRange,


SecondaryServiceIPRange: c.ExtraConfig.SecondaryServiceIPRange,


ServiceNodePortRange: c.ExtraConfig.ServiceNodePortRange,


LoopbackClientConfig: c.GenericConfig.LoopbackClientConfig,


ServiceAccountIssuer: c.ExtraConfig.ServiceAccountIssuer,


ExtendExpiration: c.ExtraConfig.ExtendExpiration,


ServiceAccountMaxExpiration: c.ExtraConfig.ServiceAccountMaxExpiration,


APIAudiences: c.GenericConfig.Authentication.APIAudiences,


}


if err := m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter, legacyRESTStorageProvider); err != nil {


return nil, err


}


}


restStorageProviders := []RESTStorageProvider{...}


//InstallAPIs,内部包含 InstallAPIGroup


if err := m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...); err != nil {


return nil, err


}


}


注册核心 apiGroups


进入 m.InstallLegacyAPI,这个方法包含了实例化 ApiGroupInfo 和 InstalAPIGroup 两个操作,这部分资源是 k8s 的核心资源


func (m *Instance) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) error {


//实例化 ApiGroupInfo


legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)


if err


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


!= nil {


return fmt.Errorf("error building core storage: %v", err)


}


controllerName := "bootstrap-controller"


coreClient := corev1client.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)


bootstrapController := c.NewBootstrapController(legacyRESTStorage, coreClient, coreClient, coreClient, coreClient.RESTClient())


m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)


m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook)


//相当于调用 InstallAPIGroup


if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {


return fmt.Errorf("error in registering group versions: %v", err)


}


return nil


}


实例化 APIGroupInfo 的代码局部如下,代码篇幅较长,只摘取 pod 一部分的源码展示,代码位于/pkg/registry/core/rest/storage_core.go


func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generic.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) {


apiGroupInfo := genericapiserver.APIGroupInfo{


PrioritizedVersions: legacyscheme.Scheme.PrioritizedVersionsForGroup(""),


VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},


Scheme: legacyscheme.Scheme,


ParameterCodec: legacyscheme.ParameterCodec,


NegotiatedSerializer: legacyscheme.Codecs,


}


podStorage, err := podstore.NewStorage(


restOptionsGetter,


nodeStorage.KubeletConnectionInfo,


c.ProxyTransport,


podDisruptionClient,


)


restStorageMap := map[string]rest.Storage{


"pods": podStorage.Pod,


"pods/attach": podStorage.Attach,


"pods/status": podStorage.Status,


"pods/log": podStorage.Log,


"pods/exec": podStorage.Exec,


"pods/portforward": podStorage.PortForward,


"pods/proxy": podStorage.Proxy,


"pods/binding": podStorage.Binding,


"bindings": podStorage.LegacyBinding,


.....


}


}


m.GenericAPIServer.InstallLegacyAPIGroup 的第一个参数是 apiPrefix,值是/api;第二个参数是上面创建好的,包含资源存储方式的 apiGroupInfo


与 InstallAPIGroup 类似地,InstallLegacyAPIGroup 需要经过两层调用才会到达 InstallREST,调用链如下


m.GenericAPIServer.InstallLegacyAPIGroup


|--s.installAPIResources


|--apiGroupVersion.InstallREST


InstallREST 的入参是 restful.Container,他是 golang http 框架 go-restful 里面的一个重要对象,在 InstallREST 里面构造出 installer,installer 包含资源的存储方法和资源对应 api 的前缀,利用 installer.Install()来创建出 go-restful 的 webservice,webservice 加入到传入得 container,即完成 api 的注册。


代码位于/vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go


func (g *APIGroupVersion) InstallREST(container *restful.Container) error {


prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)


installer := &APIInstaller{


group: g,


prefix: prefix,


minRequestTimeout: g.MinRequestTimeout,


}


apiResources, ws, registrationErrors := installer.Install()


versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, staticLister{apiResources})


versionDiscoveryHandler.AddToWebService(ws)


container.Add(ws)


return utilerrors.NewAggregate(registrationErrors)


}


installer.Install()方法的边幅很长,它既然创建了 webservice,api 中各个 URL 的路由注册,handler 的绑定也会在里面实现。由于这部分代码也涉及到 apiserver 如何响应处理一个 http 请求,本篇先不探讨


go-restful 框架


不过上面提及到 go-restful 框架的几个概念,在这里进行一个简单的科普


  • container:在 http 的角度就是一个 Server,里面就包含若干个 webservice

  • webservice:webservice 从结构来说是承上启下的一个角色,它包含了一组 route,而且这组 route 都会有一个共同的 basePath 或者说他们的 URL 的 prefix 是相同的

  • route:route 对应具体的一个 URL,它需要指定具体的路径 Path,请求方法 Method 和处理函数 Handler,以及一些参数 Parameter 等等。


他们的层次结构如下


container


|--webservice


|--Route


AggregratorServer


用于处理聚合进来的 api 请求,实际是做七层转发,它的创建流程与 APIExtensionServer 的最为相似


  • 创建 GeneriAPIServer

  • 实例化 Aggregrator

  • 实例化 APIGroupInfo

  • InstallAPIGroup


实际创建 AggregratorServer 的代码位于/vendor/k8s.io/kube-aggregrator/pkg/apiserver/apiserver.go


func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.DelegationTarget) (*APIAggregator, error) {


//创建 GeneriAPIServer


genericServer, err := c.GenericConfig.New("kube-aggregator", delegationTarget)


//实例化 Aggregrator


s := &APIAggregator{...}


//实例化 APIGroupInfo


apiGroupInfo := apiservicerest.NewRESTStorage(c.GenericConfig.MergedResourceConfig, c.GenericConfig.RESTOptionsGetter)


//InstallAPIGroup


if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {


return nil, err


}


}

运行 http server

api 的路由绑定完毕,最后就是要把 http server 跑起来,prepared.Run 调用的是由 preparedGenericAPIServer 实现的 Run 方法,经过多层调用最终把 server 跑起来,调用链如下


prepared.Run /vendor/k8s.io/kube-aggregrator/pkg/apiserver/apiserver.go


|--s.runnable.Run(stopCh)


|==preparedGenericAPIServer.Run /vendor/k8s.io/apiserver/pkg/server/genericapiserver.go


|--s.NonBlockingRun


|--s.SecureServingInfo.Serve /vendor/k8s.io/kube-aggregrator/pkg/server/secure_serving.go


|--&http.Server{}


|--RunServer


http server 的对象是在 SecureServingInfo.Serve 创建的,即调用链的 s.SecureServingInfo.Serve,最终让 server 开始监听是在 RunServer 处。它接收了 http.Server 作为参数。至此 apiserver 运行起来,接收来自各个组件或客户端的请求。


小结


--


本篇讲述了 k8s-apiserver 的启动流程,介绍了 apiserver 包含了 3 个 server 组件,apiserver 的服务实际上由这三个组件提供,讲述了他们创建流程,实例化底层的 GenericServer,实例化各自的 Server 类,实例化 ApiGroupInfo 来建立资源与存储操作间的映射关系,最后 InstallAPI。还专门挑了 k8s 核心资源类型的 ApiGroup 注册过程介绍。整个启动过程的调用链如下


Run /cmd/kube-apiserver/app/server.go


|--CreateServerChain


| |--CreateKubeAPIServerConfig


| | |--buildGenericConfig


| | |--genericapiserver.NewConfig


| | |--s.Authentication.ApplyTo

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
apiserver源码分析——启动流程,java工程师技术栈