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.
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:
kube-apiserver contiene tres servidores APIS:
- aggregatorServer : responsable de manejar
apiregistration.k8s.io
las 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-range
rango especificado) - Proporcionar funciones de inspección y reparación basadas en Service NodePort (
--service-node-port-range
rango 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.InstallLegacyAPI
para agregar una ruta a los recursos de la API central, en apiserver Ese es el/api
comienzo del recurso; - Llame a
m.InstallAPIs
los recursos API extendidos agregados a la ruta, en un servidor. Ese es el/apis
comienzo 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/v1
en; - grupos con nombre: su ruta es
/apis/$GROUP/$VERSION
; - Algunas API de estado del sistema: como
/metrics
,/version
etc .;
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:
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:
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:
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
GenericAPIServer
y mantiene su propio estadoController
. QueGenericAPIServer
maneja principalmente lasapiregistration.k8s.io
solicitudes 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 backendavailableConditionController
: 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 etiquetaskube-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.
-
crdRegistrationController
Responsable 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/v2
vista; -
crdController
: Crd responsable de la información y de registrar apiVersions apiResources, la información tanto a travéskubectl api-versions
comokubectl api-resources
ver; kubectl api-versions
El comando devuelve la información de la versión de todos los recursos del clúster de Kubernetes (en realidad, se emitieron dos solicitudes, unahttps://127.0.0.1:6443/api
y una respectivamentehttps://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-resources
El 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.conditions
verla 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.