Lea el principio de Kubernetes APIServer en un artículo

Prefacio

Todo el sistema de tecnología de Kubernetes está compuesto por un controlador y una API declarativa, y kube-apiserver es el servidor de API declarativo de Kubernetes y proporciona un puente para la interacción de otros componentes. Por lo tanto, es muy importante profundizar en la comprensión de kube-apiserver.

Lea el principio de Kubernetes APIServer en un artículo

Función general del componente

kube-apiserver, como único punto de entrada para la operación etcd de todo el clúster de Kubernetes, es responsable de la autenticación y autenticación, verificación y operaciones CRUD de los recursos de Kubernetes, y proporciona API RESTful para que otros componentes llamen:

Lea el principio de Kubernetes APIServer en un artículo

kube-apiserver contiene tres servidores APIS:

  • aggregatorServer : responsable de manejar apiregistration.k8s.iolas solicitudes de recursos de APIService en el grupo, mientras que la solicitud del usuario para interceptar se reenvía al servidor agregado (AA)
  • kubeAPIServer : Responsable de algunos procesos generales de solicitudes, incluidos: autenticación, autenticación y servicios REST de varios recursos integrados (pod, implementación, servicio, etc.), etc.
  • apiExtensionsServer : Responsable del registro de CustomResourceDefinition (CRD) apiResources y apiVersions, y maneja CRD y la correspondiente solicitud REST de CustomResource (CR) (si no se puede procesar el CR correspondiente, devolverá 404), que también es el último enlace de la delegación apiserver

También incluye bootstrap-controller, que es el principal responsable de la creación y administración del servicio de apiserver predeterminado de Kubernetes.

A continuación, se ofrece una descripción general de los componentes anteriores.

controlador de arranque

  • La lógica de creación y operación del controlador de arranque de apiserver se encuentra en el directorio k8s.io/kubernetes/pkg/master
  • bootstrap-controller se utiliza principalmente para crear y mantener el servicio apiserver predeterminado de kubernetes interno
  • kubernetes default apiserver service spec.selector está vacío. Esta es la mayor diferencia entre el servicio predeterminado de apiserver y otros servicios normales. Indica que los puntos finales correspondientes a este servicio especial no están controlados por el controlador de puntos finales, sino gestionados directamente por el controlador de arranque kube-apiserver por este código, no por el selector de pod)
  • Las principales funciones de bootstrap-controller son las siguientes:
    • Crear espacios de nombres predeterminados, kube-system y kube-public y kube-node-lease
    • Crear y mantener el servicio apiserver predeterminado de kubernetes y el punto final correspondiente
    • Proporcionar funciones de inspección y reparación basadas en Service ClusterIP ( --service-cluster-ip-rangerango especificado)
    • Proporcionar funciones de inspección y reparación basadas en Service NodePort ( --service-node-port-rangerango especificado)
// k8s.io/kubernetes/pkg/master/controller.go:142
// Start begins the core controller loops that must exist for bootstrapping
// a cluster.
func (c *Controller) Start() {
    if c.runner != nil {
        return
    }
    // Reconcile during first run removing itself until server is ready.
    endpointPorts := createEndpointPortSpec(c.PublicServicePort, "https", c.ExtraEndpointPorts)
    if err := c.EndpointReconciler.RemoveEndpoints(kubernetesServiceName, c.PublicIP, endpointPorts); err != nil {
        klog.Errorf("Unable to remove old endpoints from kubernetes service: %v", err)
    }
    repairClusterIPs := servicecontroller.NewRepair(c.ServiceClusterIPInterval, c.ServiceClient, c.EventClient, &c.ServiceClusterIPRange, c.ServiceClusterIPRegistry, &c.SecondaryServiceClusterIPRange, c.SecondaryServiceClusterIPRegistry)
    repairNodePorts := portallocatorcontroller.NewRepair(c.ServiceNodePortInterval, c.ServiceClient, c.EventClient, c.ServiceNodePortRange, c.ServiceNodePortRegistry)
    // run all of the controllers once prior to returning from Start.
    if err := repairClusterIPs.RunOnce(); err != nil {
        // If we fail to repair cluster IPs apiserver is useless. We should restart and retry.
        klog.Fatalf("Unable to perform initial IP allocation check: %v", err)
    }
    if err := repairNodePorts.RunOnce(); err != nil {
        // If we fail to repair node ports apiserver is useless. We should restart and retry.
        klog.Fatalf("Unable to perform initial service nodePort check: %v", err)
    }
    // 定期执行bootstrap controller主要的四个功能(reconciliation)  
    c.runner = async.NewRunner(c.RunKubernetesNamespaces, c.RunKubernetesService, repairClusterIPs.RunUntil, repairNodePorts.RunUntil)
    c.runner.Start()
}

Para obtener más detalles sobre el principio del código, consulte kubernetes-reading-notes .

kubeAPIServer

KubeAPIServer proporciona principalmente solicitudes de operación para recursos API integrados, registra información de enrutamiento para cada recurso API en Kubernetes y expone la API RESTful al mismo tiempo, para que los servicios dentro y fuera del clúster puedan operar recursos en Kubernetes a través de la API RESTful.

Además, kubeAPIServer es el núcleo de todo el apiserver de Kubernetes. El aggregatorServer y apiExtensionsServer que se describen a continuación se basan en kubeAPIServer para la extensión (complementa la capacidad de Kubernetes para admitir recursos definidos por el usuario)

La función principal de kubeAPIServer es agregar rutas a los recursos integrados de Kubernetes, de la siguiente manera:

  • Llame m.InstallLegacyAPIpara agregar una ruta a los recursos de la API central, en apiserver Ese es el /apicomienzo del recurso;
  • Llame a m.InstallAPIslos recursos API extendidos agregados a la ruta, en un servidor. Ese es el /apiscomienzo del recurso;
// k8s.io/kubernetes/pkg/master/master.go:332
// New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset.
// Certain config fields must be specified, including:
//   KubeletClientConfig
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Master, error) {
    ...
    // 安装 LegacyAPI(core API)
    // install legacy rest storage
    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,
            ServiceAccountMaxExpiration: c.ExtraConfig.ServiceAccountMaxExpiration,
            APIAudiences:                c.GenericConfig.Authentication.APIAudiences,
        }
        if err := m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter, legacyRESTStorageProvider); err != nil {
            return nil, err
        }
    }
    ...
    // 安装 APIs(named groups apis)
    if err := m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...); err != nil {
        return nil, err
    }
    ...
    return m, nil
}

Todo el kubeAPIServer proporciona tres tipos de interfaces de recursos API:

  • grupo principal: principalmente /api/v1en;
  • grupos con nombre: su ruta es /apis/$GROUP/$VERSION;
  • Algunas API de estado del sistema: como /metrics, /versionetc .;

La URL y la API están sustancialmente en /apis/{group}/{version}/namespaces/{namespace}/resource/{name}composición, estructura como se muestra a continuación:

Lea el principio de Kubernetes APIServer en un artículo

kubeAPIServer creará un RESTStorage correspondiente para cada recurso de API. El propósito de RESTStorage es corresponder la ruta de acceso de cada recurso y sus operaciones de almacenamiento de back-end: a través de la interfaz de REST Storage construida para determinar qué operaciones puede realizar el recurso (como : Crear, actualizar, etc.) y almacenar sus operaciones correspondientes en acción.Cada operación corresponde a un método REST estándar.Por ejemplo, crear corresponde al método REST como POST y actualizar corresponde al método REST como PUT. Finalmente, recorra secuencialmente de acuerdo con la matriz de acciones, agregue un controlador a cada operación (el controlador corresponde a la interfaz relevante implementada por REST Storage) y regístrelo en la ruta, y finalmente proporcione una API RESTful al exterior, de la siguiente manera:

// m.GenericAPIServer.InstallLegacyAPIGroup --> s.installAPIResources --> apiGroupVersion.InstallREST --> installer.Install --> a.registerResourceHandlers
// k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go:181
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) {
    ...
    // 1、判断该 resource 实现了哪些 REST 操作接口,以此来判断其支持的 verbs 以便为其添加路由
    // what verbs are supported by the storage, used to know what verbs we support per path
    creater, isCreater := storage.(rest.Creater)
    namedCreater, isNamedCreater := storage.(rest.NamedCreater)
    lister, isLister := storage.(rest.Lister)
    getter, isGetter := storage.(rest.Getter)
    ...
    // 2、为 resource 添加对应的 actions(+根据是否支持 namespace)
    // Get the list of actions for the given scope.
    switch {
    case !namespaceScoped:
        // Handle non-namespace scoped resources like nodes.
        resourcePath := resource
        resourceParams := params
        itemPath := resourcePath + "/{name}"
        nameParams := append(params, nameParam)
        proxyParams := append(nameParams, pathParam)
        ...
        // Handler for standard REST verbs (GET, PUT, POST and DELETE).
        // Add actions at the resource path: /api/apiVersion/resource
        actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister)
        actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater)
        ...
    }
    ...
    // 3、从 rest.Storage 到 restful.Route 映射
    // 为每个操作添加对应的 handler
    for _, action := range actions {
        ...
        switch action.Verb {
        ...
        case "POST": // Create a resource.
            var handler restful.RouteFunction
            // 4、初始化 handler
            if isNamedCreater {
                handler = restfulCreateNamedResource(namedCreater, reqScope, admit)
            } else {
                handler = restfulCreateResource(creater, reqScope, admit)
            }
            handler = metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, handler)
            ...
            // 5、route 与 handler 进行绑定    
            route := ws.POST(action.Path).To(handler).
                Doc(doc).
                Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
                Operation("create"+namespaced+kind+strings.Title(subresource)+operationSuffix).
                Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
                Returns(http.StatusOK, "OK", producedObject).
                // TODO: in some cases, the API may return a v1.Status instead of the versioned object
                // but currently go-restful can't handle multiple different objects being returned.
                Returns(http.StatusCreated, "Created", producedObject).
                Returns(http.StatusAccepted, "Accepted", producedObject).
                Reads(defaultVersionedObject).
                Writes(producedObject)
            if err := AddObjectParams(ws, route, versionedCreateOptions); err != nil {
                return nil, err
            }
            addParams(route, action.Params)
            // 6、添加到路由中    
            routes = append(routes, route)
        case "DELETE": // Delete a resource.
        ...
        default:
            return nil, fmt.Errorf("unrecognized action verb: %s", action.Verb)
        }
        for _, route := range routes {
            route.Metadata(ROUTE_META_GVK, metav1.GroupVersionKind{
                Group:   reqScope.Kind.Group,
                Version: reqScope.Kind.Version,
                Kind:    reqScope.Kind.Kind,
            })
            route.Metadata(ROUTE_META_ACTION, strings.ToLower(action.Verb))
            ws.Route(route)
        }
        // Note: update GetAuthorizerAttributes() when adding a custom handler.
    }
    ...
}

La estructura del código de kubeAPIServer está organizada de la siguiente manera:

1. apiserver整体启动逻辑 k8s.io/kubernetes/cmd/kube-apiserver
2. apiserver bootstrap-controller创建&运行逻辑 k8s.io/kubernetes/pkg/master
3. API Resource对应后端RESTStorage(based on genericregistry.Store)创建k8s.io/kubernetes/pkg/registry
4. aggregated-apiserver创建&处理逻辑 k8s.io/kubernetes/staging/src/k8s.io/kube-aggregator
5. extensions-apiserver创建&处理逻辑 k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver
6. apiserver创建&运行 k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/server
7. 注册API Resource资源处理handler(InstallREST&Install®isterResourceHandlers) k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints
8. 创建存储后端(etcdv3) k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/storage
9. genericregistry.Store.CompleteWithOptions初始化 k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/registry

La cadena de llamadas está organizada de la siguiente manera:

Lea el principio de Kubernetes APIServer en un artículo

Para obtener más detalles sobre el principio del código, consulte kubernetes-reading-notes .

aggregatorServer

aggregatorServer se utiliza principalmente para procesar el segundo método Aggregated APIServer (AA), que extiende los recursos de API de Kubernetes y las solicitudes de CR de proxy a AA:

Lea el principio de Kubernetes APIServer en un artículo

Aquí, combinado con el ejemplo agregado de apiserver sample-apiserver proporcionado por el funcionario de Kubernetes , el principio de resumen es el siguiente:

  • AggregatorServer se asocia con un servicio a través del objeto APIServices para reenviar la solicitud, y su tipo de servicio asociado determina además la forma de reenvío de la solicitud. AggregatorServer incluye uno GenericAPIServery mantiene su propio estado Controller. Que GenericAPIServermaneja principalmente las apiregistration.k8s.iosolicitudes de recursos de APIService en el grupo, y el controlador incluye:

    • apiserviceRegistrationController: Responsable de crear un proxy basado en el servicio de servidor agregado definido por APIService y de reenviar solicitudes de CR al servidor agregado en el backend
    • availableConditionController: Mantener el estado de disponibilidad de APIServices, incluyendo si el Servicio de referencia está disponible, etc .;
    • autoRegistrationController: Se utiliza para mantener un conjunto de servicios APIS específicos en la API;
    • crdRegistrationController: Responsable de registrar automáticamente CRD GroupVersions en APIServices;
    • openAPIAggregationController: Sincronizar los cambios de los recursos de APIServices con el documento OpenAPI proporcionado;
  • ApiserviceRegistrationController es responsable de construir un proxy basado en el servicio de servidor agregado definido por APIService y de reenviar la solicitud de CR al servidor agregado en el backend. Hay dos tipos de apiService: Local (el servicio está vacío) y Servicio (el servicio no está vacío). apiserviceRegistrationController es responsable de configurar el proxy para estos dos tipos de apiService: el tipo local se enrutará directamente a kube-apiserver para su procesamiento; mientras que el tipo de servicio configurará el proxy y convertirá la solicitud en una solicitud de servicio agregado (proxyPath: = "/ apis /" + apiService.Spec.Group + "/" + apiService.Spec.Version), y la política de equilibrio de carga solicitada es priorizar el acceso local a kube-apiserver (si el servicio es el servicio de apiserver predeterminado de kubernetes: 443) => acceso a través del servicio ClusterIP: Port (Predeterminado) O acceda seleccionando aleatoriamente el backend del punto final del servicio:

    func (s *APIAggregator) AddAPIService(apiService *v1.APIService) error {
    ...
      proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version
      // v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
      if apiService.Name == legacyAPIServiceName {
          proxyPath = "/api"
      }
      // register the proxy handler
      proxyHandler := &proxyHandler{
          localDelegate:   s.delegateHandler,
          proxyClientCert: s.proxyClientCert,
          proxyClientKey:  s.proxyClientKey,
          proxyTransport:  s.proxyTransport,
          serviceResolver: s.serviceResolver,
          egressSelector:  s.egressSelector,
      }
    ...
      s.proxyHandlers[apiService.Name] = proxyHandler
      s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler)
      s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler)
    ...
      // it's time to register the group aggregation endpoint
      groupPath := "/apis/" + apiService.Spec.Group
      groupDiscoveryHandler := &apiGroupHandler{
          codecs:    aggregatorscheme.Codecs,
          groupName: apiService.Spec.Group,
          lister:    s.lister,
          delegate:  s.delegateHandler,
      }
      // aggregation is protected
      s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)
      s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
      s.handledGroups.Insert(apiService.Spec.Group)
      return nil
    }
    // k8s.io/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go:109
    func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
      // 加载roxyHandlingInfo处理请求  
      value := r.handlingInfo.Load()
      if value == nil {
          r.localDelegate.ServeHTTP(w, req)
          return
      }
      handlingInfo := value.(proxyHandlingInfo)
    ...
      // 判断APIService服务是否正常
      if !handlingInfo.serviceAvailable {
          proxyError(w, req, "service unavailable", http.StatusServiceUnavailable)
          return
      }
      // 将原始请求转化为对APIService的请求
      // write a new location based on the existing request pointed at the target service
      location := &url.URL{}
      location.Scheme = "https"
      rloc, err := r.serviceResolver.ResolveEndpoint(handlingInfo.serviceNamespace, handlingInfo.serviceName, handlingInfo.servicePort)
      if err != nil {
          klog.Errorf("error resolving %s/%s: %v", handlingInfo.serviceNamespace, handlingInfo.serviceName, err)
          proxyError(w, req, "service unavailable", http.StatusServiceUnavailable)
          return
      }
      location.Host = rloc.Host
      location.Path = req.URL.Path
      location.RawQuery = req.URL.Query().Encode()
      newReq, cancelFn := newRequestForProxy(location, req)
      defer cancelFn()
     ...
      proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
      handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
      handler.ServeHTTP(w, newReq)
    }
    $ kubectl get APIService           
    NAME                                   SERVICE                      AVAILABLE   AGE
    ...
    v1.apps                                Local                        True        50d
    ...
    v1beta1.metrics.k8s.io                 kube-system/metrics-server   True        50d
    ...
    # default APIServices
    $ kubectl get -o yaml APIService/v1.apps
    apiVersion: apiregistration.k8s.io/v1
    kind: APIService
    metadata:
    labels:
      kube-aggregator.kubernetes.io/automanaged: onstart
    name: v1.apps
    selfLink: /apis/apiregistration.k8s.io/v1/apiservices/v1.apps
    spec:
    group: apps
    groupPriorityMinimum: 17800
    version: v1
    versionPriority: 15
    status:
    conditions:
    - lastTransitionTime: "2020-10-20T10:39:48Z"
      message: Local APIServices are always available
      reason: Local
      status: "True"
      type: Available
    
    # aggregated server    
    $ kubectl get -o yaml APIService/v1beta1.metrics.k8s.io
    apiVersion: apiregistration.k8s.io/v1
    kind: APIService
    metadata:
    labels:
      addonmanager.kubernetes.io/mode: Reconcile
      kubernetes.io/cluster-service: "true"
    name: v1beta1.metrics.k8s.io
    selfLink: /apis/apiregistration.k8s.io/v1/apiservices/v1beta1.metrics.k8s.io
    spec:
    group: metrics.k8s.io
    groupPriorityMinimum: 100
    insecureSkipTLSVerify: true
    service:
      name: metrics-server
      namespace: kube-system
      port: 443
    version: v1beta1
    versionPriority: 100
    status:
    conditions:
    - lastTransitionTime: "2020-12-05T00:50:48Z"
      message: all checks passed
      reason: Passed
      status: "True"
      type: Available
    
    # CRD
    $ kubectl get -o yaml APIService/v1.duyanghao.example.com
    apiVersion: apiregistration.k8s.io/v1
    kind: APIService
    metadata:
    labels:
      kube-aggregator.kubernetes.io/automanaged: "true"
    name: v1.duyanghao.example.com
    selfLink: /apis/apiregistration.k8s.io/v1/apiservices/v1.duyanghao.example.com
    spec:
    group: duyanghao.example.com
    groupPriorityMinimum: 1000
    version: v1
    versionPriority: 100
    status:
    conditions:
    - lastTransitionTime: "2020-12-11T08:45:37Z"
      message: Local APIServices are always available
      reason: Local
      status: "True"
      type: Available
  • Durante la creación de aggregatorServer, se creará una lista APIService predeterminada basada en todos los recursos API definidos por kube-apiserver. El nombre es $VERSION.$GROUP, estos APIServices tendrán etiquetas kube-aggregator.kubernetes.io/automanaged: onstart, por ejemplo: v1.apps apiService. autoRegistrationController crea y mantiene el APIService en estas listas, que es el apiService local que vemos; para APIService personalizado (servidor agregado), no se procesará

  • El servidor agregado implementa la interfaz CRUD API de CR (recursos API personalizados) y puede elegir de manera flexible el almacenamiento de fondo. Puede compartir etcd con core kube-apiserver, o implementar la base de datos etcd u otras bases de datos de forma independiente. La ruta de la API de CR implementada por el servidor agregado es: / apis / $ GROUP / $ VERSION, el apiserver de muestra específico es: /apis/wardle.example.com/v1alpha1, los siguientes tipos de recursos son: flunders y fischers

  • El servidor agregado implementa la integración e interacción con el núcleo kube-apiserver mediante la implementación de recursos de tipo de servicio APIS, y los campos de servicio apuntan al servicio de servidor agregado correspondiente.

  • La estructura del directorio sample-apiserver es la siguiente, puede consultar cómo escribir su propio servidor agregado:

    staging/src/k8s.io/sample-apiserver
    ├── artifacts
    │   ├── example
    │   │   ├── apiservice.yaml
        ...
    ├── hack
    ├── main.go
    └── pkg
    ├── admission
    ├── apis
    ├── apiserver
    ├── cmd
    ├── generated
    │   ├── clientset
    │   │   └── versioned
                ...
    │   │       └── typed
    │   │           └── wardle
    │   │               ├── v1alpha1
    │   │               └── v1beta1
    │   ├── informers
    │   │   └── externalversions
    │   │       └── wardle
    │   │           ├── v1alpha1
    │   │           └── v1beta1
    │   ├── listers
    │   │   └── wardle
    │   │       ├── v1alpha1
    │   │       └── v1beta1
    └── registry
    • Entre ellos, los artefactos se utilizan para implementar ejemplos de yaml
    • El directorio de pirateo almacena scripts automáticos (por ejemplo: update-codegen)
    • main.go es la entrada de inicio del servidor agregado; pkg / cmd es responsable de iniciar la lógica específica del servidor agregado; pkg / apiserver se usa para la inicialización del servidor agregado y el registro de enrutamiento
    • pkg / apis es responsable de la definición de la estructura del CR relevante, y se genera automáticamente (update-codegen)
    • paquete / admisión es responsable del código de admisión correspondiente
    • pkg / generate es responsable de generar un conjunto de clientes, informadores y oyentes para acceder a CR
    • El directorio pkg / registry es responsable de la implementación de RESTStorage relacionada con CR

Para obtener más detalles sobre el principio del código, consulte kubernetes-reading-notes .

apiExtensionsServer

apiExtensionsServer es el principal responsable del registro de CustomResourceDefinition (CRD) apiResources y apiVersions, y también procesa CRD y las correspondientes solicitudes REST de CustomResource (CR) (si el CR correspondiente no se puede procesar, devolverá 404), que también es el último enlace de la delegación apiserver

El principio se resume como sigue:

  • El recurso personalizado, denominado CR, es un tipo de recurso personalizado de Kubernetes, que corresponde a varios tipos de recursos integrados en Kubernetes, como Pod, Servicio, etc. Usando CR podemos definir cualquier tipo de recurso que queramos

  • CRD registra CR con Kubernetes en forma de archivos yaml para implementar recursos api personalizados, que es la segunda forma de ampliar los recursos de la API de Kubernetes y también es un método de uso común

  • APIExtensionServer es responsable del registro de apiResources y apiVersions de CustomResourceDefinition (CRD). También procesa CRD y la correspondiente solicitud REST de CustomResource (CR) (si no se puede procesar el CR correspondiente, devolverá 404), que también es el último enlace de la delegación apiserver.

  • crdRegistrationControllerResponsable de registrar automáticamente CRD GroupVersions en APIServices. La lógica específica es: enumerar todos los CRD, luego construir un APIService de acuerdo con los campos crd.Spec.Group y crd.Spec.Versions definidos por el CRD, y agregarlo a autoRegisterController.apiServicesToSync, y autoRegisterController lo creará y mantendrá. Esta es la razón por la que el objeto APIService correspondiente se generará después de que se cree el CRD

  • El controlador y las funciones incluidas en APIExtensionServer son las siguientes:

    • openapiController: El documento de OpenAPI cambia los recursos de crd para proporcionar sincronización y se puede acceder a través de la /openapi/v2vista;

    • crdController: Crd responsable de la información y de registrar apiVersions apiResources, la información tanto a través kubectl api-versionscomo kubectl api-resourcesver;

    • kubectl api-versionsEl comando devuelve la información de la versión de todos los recursos del clúster de Kubernetes (en realidad, se emitieron dos solicitudes, una https://127.0.0.1:6443/apiy una respectivamente https://127.0.0.1:6443/apis, y los resultados de las dos solicitudes se fusionaron al final)
    $ kubectl -v=8 api-versions 
    I1211 11:44:50.276446   22493 loader.go:375] Config loaded from file:  /root/.kube/config
    I1211 11:44:50.277005   22493 round_trippers.go:420] GET https://127.0.0.1:6443/api?timeout=32s
    ...
    I1211 11:44:50.290265   22493 request.go:1068] Response Body: {"kind":"APIVersions","versions":["v1"],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0","serverAddress":"x.x.x.x:6443"}]}
    I1211 11:44:50.293673   22493 round_trippers.go:420] GET https://127.0.0.1:6443/apis?timeout=32s
    ...
    I1211 11:44:50.298360   22493 request.go:1068] Response Body: {"kind":"APIGroupList","apiVersion":"v1","groups":[{"name":"apiregistration.k8s.io","versions":[{"groupVersion":"apiregistration.k8s.io/v1","version":"v1"},{"groupVersion":"apiregistration.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"apiregistration.k8s.io/v1","version":"v1"}},{"name":"extensions","versions":[{"groupVersion":"extensions/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"extensions/v1beta1","version":"v1beta1"}},{"name":"apps","versions":[{"groupVersion":"apps/v1","version":"v1"}],"preferredVersion":{"groupVersion":"apps/v1","version":"v1"}},{"name":"events.k8s.io","versions":[{"groupVersion":"events.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"events.k8s.io/v1beta1","version":"v1beta1"}},{"name":"authentication.k8s.io","versions":[{"groupVersion":"authentication.k8s.io/v1","version":"v1"},{"groupVersion":"authentication.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"authentication.k8s.io/v1"," [truncated 4985 chars]
    apiextensions.k8s.io/v1
    apiextensions.k8s.io/v1beta1
    apiregistration.k8s.io/v1
    apiregistration.k8s.io/v1beta1
    apps/v1
    authentication.k8s.io/v1beta1
    ...
    storage.k8s.io/v1
    storage.k8s.io/v1beta1
    v1
    
    • kubectl api-resourcesEl comando es obtener primero toda la información de la versión de API y luego llamar a la interfaz para cada versión de API para obtener todos los tipos de recursos de API bajo esa versión

      $ kubectl -v=8 api-resources
      5077 loader.go:375] Config loaded from file:  /root/.kube/config
      I1211 15:19:47.593450   15077 round_trippers.go:420] GET https://127.0.0.1:6443/api?timeout=32s
      I1211 15:19:47.602273   15077 request.go:1068] Response Body: {"kind":"APIVersions","versions":["v1"],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0","serverAddress":"x.x.x.x:6443"}]}
      I1211 15:19:47.606279   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis?timeout=32s
      I1211 15:19:47.610333   15077 request.go:1068] Response Body: {"kind":"APIGroupList","apiVersion":"v1","groups":[{"name":"apiregistration.k8s.io","versions":[{"groupVersion":"apiregistration.k8s.io/v1","version":"v1"},{"groupVersion":"apiregistration.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"apiregistration.k8s.io/v1","version":"v1"}},{"name":"extensions","versions":[{"groupVersion":"extensions/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"extensions/v1beta1","version":"v1beta1"}},{"name":"apps","versions":[{"groupVersion":"apps/v1","version":"v1"}],"preferredVersion":{"groupVersion":"apps/v1","version":"v1"}},{"name":"events.k8s.io","versions":[{"groupVersion":"events.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"events.k8s.io/v1beta1","version":"v1beta1"}},{"name":"authentication.k8s.io","versions":[{"groupVersion":"authentication.k8s.io/v1","version":"v1"},{"groupVersion":"authentication.k8s.io/v1beta1","version":"v1beta1"}],"preferredVersion":{"groupVersion":"authentication.k8s.io/v1"," [truncated 4985 chars]
      I1211 15:19:47.614700   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis/batch/v1?timeout=32s
      I1211 15:19:47.614804   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis/authentication.k8s.io/v1?timeout=32s
      I1211 15:19:47.615687   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis/auth.tkestack.io/v1?timeout=32s
      https://127.0.0.1:6443/apis/authentication.k8s.io/v1beta1?timeout=32s
      I1211 15:19:47.616794   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis/coordination.k8s.io/v1?timeout=32s
      I1211 15:19:47.616863   15077 round_trippers.go:420] GET https://127.0.0.1:6443/apis/apps/v1?timeout=32s
      ...
      NAME                              SHORTNAMES   APIGROUP                       NAMESPACED   KIND
      bindings                                                                      true         Binding
      endpoints                         ep                                          true         Endpoints
      events                            ev                                          true         Event
      limitranges                       limits                                      true         LimitRange
      namespaces                        ns                                          false        Namespace
      nodes                             no                                          false        Node
      ...
      • namingController: Compruebe si hay un conflicto de nombres en crd obj, que se puede comprobar en crd .status.conditions;

      • establishingController: Compruebe si crd está en estado normal, puede comprobarlo en crd .status.conditions;

      • nonStructuralSchemaController: Compruebe si la estructura de crd obj es normal, puede .status.conditionsverla en crd ;

      • apiApprovalController: Compruebe si crd cumple con la política de declaración de la API de Kubernetes, que se puede comprobar en crd .status.conditions;

      • finalizingController: Similar a la función de finaliza, relacionada con la supresión de CR;
  • La lógica de procesamiento de CR CRUD APIServer se resume a continuación:

    • createAPIExtensionsServer => NewCustomResourceDefinitionHandler => crdHandler => Registre la interfaz CR CRUD API:
    // New returns a new instance of CustomResourceDefinitions from the given config.
    func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
      ...
        crdHandler, err := NewCustomResourceDefinitionHandler(
          versionDiscoveryHandler,
            groupDiscoveryHandler,
          s.Informers.Apiextensions().V1().CustomResourceDefinitions(),
            delegateHandler,
          c.ExtraConfig.CRDRESTOptionsGetter,
            c.GenericConfig.AdmissionControl,
          establishingController,
            c.ExtraConfig.ServiceResolver,
          c.ExtraConfig.AuthResolverWrapper,
            c.ExtraConfig.MasterCount,
            s.GenericAPIServer.Authorizer,
            c.GenericConfig.RequestTimeout,
            time.Duration(c.GenericConfig.MinRequestTimeout)*time.Second,
            apiGroupInfo.StaticOpenAPISpec,
            c.GenericConfig.MaxRequestBodyBytes,
        )
        if err != nil {
            return nil, err
        }
        s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler)
        s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler)
        ...
        return s, nil
    }
    
    • La lógica de procesamiento de crdHandler es la siguiente:

    • Analice req (GET /apis/duyanghao.example.com/v1/namespaces/default/students) y obtenga el CRD correspondiente según el grupo (duyanghao.example.com), la versión (v1) y el campo de recursos (estudiantes) en la ruta de solicitud Contenido (crd, err: = r.crdLister.Get (crdName))

    • Obtenga crdInfo a través de crd.UID y crd.Name, si no existe, cree el crdInfo correspondiente (crdInfo, err: = r.getOrCreateServingInfoFor (crd.UID, crd.Name)). crdInfo contiene la definición de CRD y el CRD correspondiente al recurso personalizado del recurso personalizado.

    • El almacenamiento customresource.REST es creado por el grupo (duyanghao.example.com), versión (v1), tipo (estudiante), recurso (estudiantes), etc. correspondiente a CR. Dado que CR no tiene una definición de estructura específica en el código de Kubernetes, aquí Primero inicializará una estructura genérica no estructurada (utilizada para guardar todos los tipos de recursos personalizados) y realizará la operación SetGroupVersionKind en la estructura (establecer un tipo de recurso personalizado específico)

    • Después de obtener la estructura no estructurada del almacenamiento customresource.REST, se convertirá y devolverá

      // k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go:223
      func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
      ctx := req.Context()
      requestInfo, ok := apirequest.RequestInfoFrom(ctx)
      ...
      crdName := requestInfo.Resource + "." + requestInfo.APIGroup
      crd, err := r.crdLister.Get(crdName)
      ...
      crdInfo, err := r.getOrCreateServingInfoFor(crd.UID, crd.Name)
      verb := strings.ToUpper(requestInfo.Verb)
      resource := requestInfo.Resource
      subresource := requestInfo.Subresource
      scope := metrics.CleanScope(requestInfo)
      ...
      switch {
      case subresource == "status" && subresources != nil && subresources.Status != nil:
          handlerFunc = r.serveStatus(w, req, requestInfo, crdInfo, terminating, supportedTypes)
      case subresource == "scale" && subresources != nil && subresources.Scale != nil:
          handlerFunc = r.serveScale(w, req, requestInfo, crdInfo, terminating, supportedTypes)
      case len(subresource) == 0:
          handlerFunc = r.serveResource(w, req, requestInfo, crdInfo, terminating, supportedTypes)
      default:
          responsewriters.ErrorNegotiated(
              apierrors.NewNotFound(schema.GroupResource{Group: requestInfo.APIGroup, Resource: requestInfo.Resource}, requestInfo.Name),
              Codecs, schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}, w, req,
          )
      }
      if handlerFunc != nil {
          handlerFunc = metrics.InstrumentHandlerFunc(verb, requestInfo.APIGroup, requestInfo.APIVersion, resource, subresource, scope, metrics.APIServerComponent, handlerFunc)
          handler := genericfilters.WithWaitGroup(handlerFunc, longRunningFilter, crdInfo.waitGroup)
          handler.ServeHTTP(w, req)
          return
      }
      }
      

Para obtener más detalles sobre el principio del código, consulte kubernetes-reading-notes .

Conclusión

Este artículo resume el apiserver de Kubernetes desde el nivel del código fuente, incluidos: aggregatorServer, kubeAPIServer, apiExtensionsServer y bootstrap-controller. Al leer este artículo, puede tener una comprensión general de los principios internos de apiserver y también ayudar a realizar un seguimiento de la investigación en profundidad.

Refs

Supongo que te gusta

Origin blog.51cto.com/14120339/2598532
Recomendado
Clasificación