References

Routes

Landscape

routes-landscape.svg

PathRecorderMuxrefreshMuxLocked 方法如下所示。可理解为 PathRecorderMux 中除 mux 域外,都是作为 pathHandler 的缓存存在的,这个方法的作用就是将缓存内容替换到 pathHandler 中,并存储在 mux 中。

  1. func (m *PathRecorderMux) refreshMuxLocked() {
  2. newMux := &pathHandler{
  3. muxName: m.name,
  4. pathToHandler: map[string]http.Handler{},
  5. prefixHandlers: []prefixHandler{},
  6. notFoundHandler: http.NotFoundHandler(),
  7. }
  8. if m.notFoundHandler != nil {
  9. newMux.notFoundHandler = m.notFoundHandler
  10. }
  11. for path, handler := range m.pathToHandler {
  12. newMux.pathToHandler[path] = handler
  13. }
  14. keys := sets.StringKeySet(m.prefixToHandler).List()
  15. sort.Sort(sort.Reverse(byPrefixPriority(keys)))
  16. for _, prefix := range keys {
  17. newMux.prefixHandlers = append(newMux.prefixHandlers, prefixHandler{
  18. prefix: prefix,
  19. handler: m.prefixToHandler[prefix],
  20. })
  21. }
  22. m.mux.Store(newMux)
  23. }

Listed Path Provider

listed-path-provider.svg

ListedpathProvider 用于获取暴露的 API 根路径。GenericAPIServer 对该接口的实现依赖于 APIServerHandler 及 DelegationTarget 的实现。

Install APIs

在创建 GenericAPIServer 的最后阶段,通过方法 installAPI 装入各种路由。装入路由时,会使用 routes 包中内容,routes 包是针对各种辅助功能的路由即其 Handler 信息。

routes.Index

将根路径获取路由加载至 APIServerHandler 中,代码如下所示。routes.Index 表示要装入路由的结构。

  1. if c.EnableIndex {
  2. routes.Index{}.Install(s.listedPathProvider, s.Handler.NonGoRestfulMux)
  3. }

routes.Index 路由加载方法也极其简单明白

  1. func (i Index) Install(pathProvider ListedPathProvider, mux *mux.PathRecorderMux) {
  2. handler := IndexLister{StatusCode: http.StatusOK, PathProvider: pathProvider}
  3. mux.UnlistedHandle("/", handler)
  4. mux.UnlistedHandle("/index.html", handler)
  5. }

routes.Profiling

过程与 routes.Index 几乎相同,因此只将安装路由的方法记录下来

  1. func (d Profiling) Install(c *mux.PathRecorderMux) {
  2. c.UnlistedHandleFunc("/debug/pprof", redirectTo("/debug/pprof/"))
  3. c.UnlistedHandlePrefix("/debug/pprof/", http.HandlerFunc(pprof.Index))
  4. c.UnlistedHandleFunc("/debug/pprof/profile", pprof.Profile)
  5. c.UnlistedHandleFunc("/debug/pprof/symbol", pprof.Symbol)
  6. c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
  7. }

routes.Metrics

  • 配合 Profiling 时

    1. func (m MetricsWithReset) Install(c *mux.PathRecorderMux) {
    2. register()
    3. defaultMetricsHandler := prometheus.Handler().ServeHTTP
    4. c.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
    5. if req.Method == "DELETE" {
    6. apimetrics.Reset()
    7. etcdmetrics.Reset()
    8. io.WriteString(w, "metrics reset\n")
    9. return
    10. }
    11. defaultMetricsHandler(w, req)
    12. })
    13. }
  • 不需要配合 Profiling

    1. func (m DefaultMetrics) Install(c *mux.PathRecorderMux) {
    2. register()
    3. c.Handle("/metrics", prometheus.Handler())
    4. }

routes.Version

Version 信息是加载到 RESTFul 上的,其他基本一样。

  1. func (v Version) Install(c *restful.Container) {
  2. if v.Version == nil {
  3. return
  4. }
  5. // Set up a service to return the git code version.
  6. versionWS := new(restful.WebService)
  7. versionWS.Path("/version")
  8. versionWS.Doc("git code version from which this is built")
  9. versionWS.Route(
  10. versionWS.GET("/").To(v.handleVersion).
  11. Doc("get the code version").
  12. Operation("getCodeVersion").
  13. Produces(restful.MIME_JSON).
  14. Consumes(restful.MIME_JSON).
  15. Writes(version.Info{}))
  16. c.Add(versionWS)
  17. }

Health Checker

health-checker-landscape.svg

HealthChecker 由传入的 DelegationTarget 生成,并最终注册给 GenericAPIServer。

Handler

Dispatch Procedure

api-server-http-handler-procedure.svg

FullHandlerChain 使用 HandlerChainBuilderFu 函数,将 http.Handler 包装起来,并在最后传递个 director。director 再分发给 http.ServeMux 或 PathRecorderMux。
HandlerChainBuilderFn 声明如下所示

  1. type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler

director.ServeHTTP

director 满足 http.Handler 接口,在 ServeHTTP 方法中根据 req.URL.path 实现分发功能

  1. func (d director) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  2. path := req.URL.Path
  3. // check to see if our webservices want to claim this path
  4. for _, ws := range d.goRestfulContainer.RegisteredWebServices() {
  5. switch {
  6. case ws.RootPath() == "/apis":
  7. // if we are exactly /apis or /apis/, then we need special handling in loop.
  8. // normally these are passed to the nonGoRestfulMux, but if discovery is enabled, it will go directly.
  9. // We can't rely on a prefix match since /apis matches everything (see the big comment on Director above)
  10. if path == "/apis" || path == "/apis/" {
  11. klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
  12. // don't use servemux here because gorestful servemuxes get messed up when removing webservices
  13. // TODO fix gorestful, remove TPRs, or stop using gorestful
  14. d.goRestfulContainer.Dispatch(w, req)
  15. return
  16. }
  17. case strings.HasPrefix(path, ws.RootPath()):
  18. // ensure an exact match or a path boundary match
  19. if len(path) == len(ws.RootPath()) || path[len(ws.RootPath())] == '/' {
  20. klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
  21. // don't use servemux here because gorestful servemuxes get messed up when removing webservices
  22. // TODO fix gorestful, remove TPRs, or stop using gorestful
  23. d.goRestfulContainer.Dispatch(w, req)
  24. return
  25. }
  26. }
  27. }
  28. // if we didn't find a match, then we just skip gorestful altogether
  29. klog.V(5).Infof("%v: %v %q satisfied by nonGoRestful", d.name, req.Method, path)
  30. d.nonGoRestfulMux.ServeHTTP(w, req)
  31. }

DefaultBuildHandlerChain

API Server 中的 handler 来自于 Config 结构,Config 结构在 config.go 中初始化,使用默认配置,默认配置具体实现如下所示

  1. func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
  2. handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
  3. handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)
  4. handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
  5. handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
  6. failedHandler := genericapifilters.Unauthorized(c.Serializer, c.Authentication.SupportsBasicAuth)
  7. failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
  8. handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences)
  9. handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
  10. handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
  11. handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
  12. handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
  13. handler = genericfilters.WithPanicRecovery(handler)
  14. return handler
  15. }

API

API Server 在启动前,需要安装好各种基础服务,基础服务安装代码位置在 InstallLegacyAPI,这个方法在 Kubernetes 中,被 InstallLegacyAPI 调用,用于安装 API Group。

server.APIGroupInfo

在注册过程中,最重要的信息是 server.APIGroupInfo,核心结构如下图所示
api-group-info-landscape.svg

kube-apiserver 的 APIGroupInfo 在 NewLegacyRESTStorage 中注册生成。APIGroupInfo 中的 Scheme、Codecs、ParameterCodec 在 scheme.go 中初始化。APIGroupInfo.VersionedResourcesStorageMap 初始化如下所示,使用的 RESTOptionsGetterStorage 系列中。

  1. restStorageMap := map[string]rest.Storage{
  2. "pods": podStorage.Pod,
  3. "pods/attach": podStorage.Attach,
  4. "pods/status": podStorage.Status,
  5. "pods/log": podStorage.Log,
  6. "pods/exec": podStorage.Exec,
  7. "pods/portforward": podStorage.PortForward,
  8. "pods/proxy": podStorage.Proxy,
  9. "pods/binding": podStorage.Binding,
  10. "bindings": podStorage.Binding,
  11. "podTemplates": podTemplateStorage,
  12. "replicationControllers": controllerStorage.Controller,
  13. "replicationControllers/status": controllerStorage.Status,
  14. "services": serviceRest,
  15. "services/proxy": serviceRestProxy,
  16. "services/status": serviceStatusStorage,
  17. "endpoints": endpointsStorage,
  18. "nodes": nodeStorage.Node,
  19. "nodes/status": nodeStorage.Status,
  20. "nodes/proxy": nodeStorage.Proxy,
  21. "events": eventStorage,
  22. "limitRanges": limitRangeStorage,
  23. "resourceQuotas": resourceQuotaStorage,
  24. "resourceQuotas/status": resourceQuotaStatusStorage,
  25. "namespaces": namespaceStorage,
  26. "namespaces/status": namespaceStatusStorage,
  27. "namespaces/finalize": namespaceFinalizeStorage,
  28. "secrets": secretStorage,
  29. "serviceAccounts": serviceAccountStorage,
  30. "persistentVolumes": persistentVolumeStorage,
  31. "persistentVolumes/status": persistentVolumeStatusStorage,
  32. "persistentVolumeClaims": persistentVolumeClaimStorage,
  33. "persistentVolumeClaims/status": persistentVolumeClaimStatusStorage,
  34. "configMaps": configMapStorage,
  35. "componentStatuses": componentstatus.NewStorage(componentStatusStorage{c.StorageFactory}.serversToValidate),
  36. }
  37. if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "autoscaling", Version: "v1"}) {
  38. restStorageMap["replicationControllers/scale"] = controllerStorage.Scale
  39. }
  40. if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "policy", Version: "v1beta1"}) {
  41. restStorageMap["pods/eviction"] = podStorage.Eviction
  42. }
  43. if serviceAccountStorage.Token != nil {
  44. restStorageMap["serviceaccounts/token"] = serviceAccountStorage.Token
  45. }
  46. apiGroupInfo.VersionedResourcesStorageMap["v1"] = restStorageMap

Install API Resources

注意本图中的 endpoints.APIGroupVersion 是由 server.APIGroupInfo 通过方法 getAPIGroupVersion 生成的。
install-api-resources.svg

APIGroupVersion 通过 InstallREST 方法注册路由处理函数。在该方法中,首先创建了 APIInstaller 结构,再通过其 Install 方法生成 metav1.APIResource 及 restful.WebService 结构。APIResource 大致由以下内容组成

  • Name: 资源的名称
  • SingularName: 资源的单一名称
  • Namespaced: true 表示该资源为一个命名空间
  • Group Version Kind: 组、版本、类型
  • Verbs: 操作,可为:get、list、watch、create、update、patch、delete、proxy 等
  • ShortNames: 建议的资源短名称
  • Categories: 资源归属的 Group 名称

NewREST

pod-new-rest.svg

podtemplate.NewREST 为例来简要说明过程,该方法返回值为一个 REST 对象,且只是简单的匿名包含 Store 对象,如下所示

  1. type REST struct {
  2. *genericregistry.Store
  3. }

针对 Pod 的 NewREST 方法,具体代码如下所示,首先创建一个 Store 实例,并与 PodTemplate、PodTemplateList 等核心对象关联,并提供了如何创建对象的策略。

  1. func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
  2. store := &genericregistry.Store{
  3. NewFunc: func() runtime.Object { return &api.PodTemplate{} },
  4. NewListFunc: func() runtime.Object { return &api.PodTemplateList{} },
  5. DefaultQualifiedResource: api.Resource("podtemplates"),
  6. CreateStrategy: podtemplate.Strategy,
  7. UpdateStrategy: podtemplate.Strategy,
  8. DeleteStrategy: podtemplate.Strategy,
  9. ExportStrategy: podtemplate.Strategy,
  10. ReturnDeletedObject: true,
  11. TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
  12. }
  13. options := &generic.StoreOptions{RESTOptions: optsGetter}
  14. if err := store.CompleteWithOptions(options); err != nil {
  15. panic(err) // TODO: Propagate error up
  16. }
  17. return &REST{store}
  18. }

Install API

InstallAPIGroups 将 API 注册于 APIServerHandler,其关键代码如下所示,在注册 API 之前,先将 API 使用的 Resources 注册。

  1. func (s *GenericAPIServer) InstallAPIGroups(apiGroupInfos ...*APIGroupInfo) error {
  2. // ...
  3. for _, apiGroupInfo := range apiGroupInfos {
  4. if err := s.installAPIResources(APIGroupPrefix, apiGroupInfo, openAPIModels); err != nil {
  5. return fmt.Errorf("unable to install api resources: %v", err)
  6. }
  7. // ...
  8. apiGroup := metav1.APIGroup{
  9. Name: apiGroupInfo.PrioritizedVersions[0].Group,
  10. Versions: apiVersionsForDiscovery,
  11. PreferredVersion: preferredVersionForDiscovery,
  12. }
  13. s.DiscoveryGroupManager.AddGroup(apiGroup)
  14. s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
  15. }
  16. return nil
  17. }

该方法在 Kubernetes API Server 中,被 InstallAPIs 使用,且 APIGroupInfo 由 RESTStorageProvider 生成,如下所示

  1. restStorageProviders := []RESTStorageProvider{
  2. auditregistrationrest.RESTStorageProvider{},
  3. authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authentication.Authenticator, APIAudiences: c.GenericConfig.Authentication.APIAudiences},
  4. authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer, RuleResolver: c.GenericConfig.RuleResolver},
  5. autoscalingrest.RESTStorageProvider{},
  6. batchrest.RESTStorageProvider{},
  7. certificatesrest.RESTStorageProvider{},
  8. coordinationrest.RESTStorageProvider{},
  9. extensionsrest.RESTStorageProvider{},
  10. networkingrest.RESTStorageProvider{},
  11. noderest.RESTStorageProvider{},
  12. policyrest.RESTStorageProvider{},
  13. rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer},
  14. schedulingrest.RESTStorageProvider{},
  15. settingsrest.RESTStorageProvider{},
  16. storagerest.RESTStorageProvider{},
  17. // keep apps after extensions so legacy clients resolve the extensions versions of shared resource names.
  18. // See https://github.com/kubernetes/kubernetes/issues/42392
  19. appsrest.RESTStorageProvider{},
  20. admissionregistrationrest.RESTStorageProvider{},
  21. eventsrest.RESTStorageProvider{TTL: c.ExtraConfig.EventTTL},
  22. }

GroupManager

在 InstallAPIGroups 中,可以发现,API 最终是发布在 discovery.GroupManager 上的,那么我们来看一下这个接口即其作用。
group-manager-overview.svg

discovery.rootAPIsHandler 满足 http.Handler 接口,并将 RESTFul 请求重定向到了 http.Handler 接口,如下面代码所示,WebService 返回一个 restful.WebService 对象,并将本组下全部请求交由 s.restfulHandle,而这个方法仅是简单调用 http.Handler 接口方法

  1. func (s *rootAPIsHandler) WebService() *restful.WebService {
  2. mediaTypes, _ := negotiation.MediaTypesForSerializer(s.serializer)
  3. ws := new(restful.WebService)
  4. ws.Path(APIGroupPrefix)
  5. ws.Doc("get available API versions")
  6. ws.Route(ws.GET("/").To(s.restfulHandle).
  7. Doc("get available API versions").
  8. Operation("getAPIVersions").
  9. Produces(mediaTypes...).
  10. Consumes(mediaTypes...).
  11. Writes(metav1.APIGroupList{}))
  12. return ws
  13. }

restfulHandle 方法如下所示

  1. func (s *rootAPIsHandler) restfulHandle(req *restful.Request, resp *restful.Response) {
  2. s.ServeHTTP(resp.ResponseWriter, req.Request)
  3. }

在通过 New 方法创建 GenericAPIServer 时,GroupManager 已经通过 installAPI 方法挂载至主路由上,如下所示

  1. func installAPI(s *GenericAPIServer, c *Config) {
  2. // ...
  3. routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
  4. if c.EnableDiscovery {
  5. s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
  6. }
  7. }

Extensions API Server

Relationship

api-extension-relationship.svg

CustomeResourcesDefinitionscreateAPIExtensionsServer 中创建,并在 CreateAPIServer 中作为 DelegationTarget 的实例传递给 GenericAPIServer。

API Aggregator

APIAggregator 通过 NewWithDelegate 方法创建。

Landscape

api-extension-api-aggregator.svg

Handler

从上图可以看到,aggregator 是最终暴露的 GenericAPIServer,每个 GenericAPIServer 都挂载了自己的路由,如下图所示,那么是如何关联起来的就是需要解决的问题。
api-extension-handler-relations.svg

跟踪 delegateHandler 引用情况,发现在 AddAPIService 中使用。proxyHandler 创建过程如下,创建的 proxyHandler 挂载在 GenericAPIServer 的 Mux 中,proxyHandler 的 ServeHTTP 方法自行阅读。

api-extension-aggregator-add-api-proxy-handler.svg

Group 处理通过注册 apiGroupHandler 完成,自行阅读源码即可。

Serives Controller

api-extension-aggregator-auto-api-register.svg

References

Hooks

hooks-landscape.svg

Plugin

Landscape

plugin-landscape.svg

RegisterAllAdmissionPlugins 方法注册全部内置的 Plugin。