sig-kubernetes icon indicating copy to clipboard operation
sig-kubernetes copied to clipboard

【提问】apiserver 中 CRD 的 api group 是在哪里创建的呢?

Open lianghao208 opened this issue 5 years ago • 2 comments

问题描述

在 apiserver 启动过程中,会为不同的资源注册不同的 api。如:kubeApiServer负责内置资源的 api 创建,extensionServer 负责 crd 这种外部资源的 api 创建(只负责 apiextensions.k8s.io/v1 这个组的资源的 api 创建),aggregatorServer 负责 apiregistration 的api创建(只负责 apiextensions.k8s.io 这个组的资源的 api 创建)。 但在我们写 CRD 时,会自定义 CR 的 api 。比如 podlog 是我写的 CRD。执行 kubectl get podlog 命令的时候,会去调 apiServer对应的 api 去获取cr的相关信息: pod-log crd

图中 log.pod.log.io/v1 是我自定义的 api group,那么这个 group 是什么时候创建的呢?它的创建逻辑在哪呢?

lianghao208 avatar Aug 15 '20 15:08 lianghao208

apiextension apiserver创建时, 会初始化crdHandler

func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
         ...
	crdHandler, err := NewCustomResourceDefinitionHandler(
		versionDiscoveryHandler,
		groupDiscoveryHandler,
		s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
		delegateHandler,
		c.ExtraConfig.CRDRESTOptionsGetter,
		c.GenericConfig.AdmissionControl,
		establishingController,
		c.ExtraConfig.ServiceResolver,
		c.ExtraConfig.AuthResolverWrapper,
		c.ExtraConfig.MasterCount,
		s.GenericAPIServer.Authorizer,
		c.GenericConfig.RequestTimeout,
		c.GenericConfig.MaxRequestBodyBytes,
	)
        ...
}

看一下crdHandler的serveHTTP方法

func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	requestInfo, ok := apirequest.RequestInfoFrom(ctx)
	if !ok {
		responsewriters.InternalError(w, req, fmt.Errorf("no RequestInfo found in the context"))
		return
	}
	if !requestInfo.IsResourceRequest {
		pathParts := splitPath(requestInfo.Path)
		// only match /apis/<group>/<version>
		// only registered under /apis
		if len(pathParts) == 3 {
			r.versionDiscoveryHandler.ServeHTTP(w, req)
			return
		}
		// only match /apis/<group>
		if len(pathParts) == 2 {
			r.groupDiscoveryHandler.ServeHTTP(w, req)
			return
		}

		r.delegate.ServeHTTP(w, req)
		return
	}
        ...
}

我们使用crd资源时, 需要先创建crd, 创建crd后, 会有消费者调用sync方法, 这时给groupDiscoveryHandler和versionDiscoveryHandler设置值

# vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go:76
func (c *DiscoveryController) sync(version schema.GroupVersion) error {
        ...
	c.groupHandler.setDiscovery(version.Group, discovery.NewAPIGroupHandler(Codecs, apiGroup))

	if !foundVersion {
		c.versionHandler.unsetDiscovery(version)
		return nil
	}
	c.versionHandler.setDiscovery(version, discovery.NewAPIVersionHandler(Codecs, version, discovery.APIResourceListerFunc(func() []metav1.APIResource {
		return apiResourcesForDiscovery
	})))

	return nil
}

具体代码, 自己在找代码看看吧, 大概就在这一块~

sundongmin avatar Aug 17 '20 12:08 sundongmin

customresource_discovery_controller

太感谢了!!之前一直没找到这段逻辑,一直在翻 apiserver 启动流程的代码,忽略了 crd 创建的时候会去动态创建 apisever 的路由。大佬你读源码的能力太强了!!!

lianghao208 avatar Aug 17 '20 14:08 lianghao208