1つの記事でKubernetesAPIServerの原理を読む

序文

Kubernetesテクノロジーシステム全体は宣言型APIとコントローラーで構成され、kube-apiserverはKubernetesの宣言型apiサーバーであり、他のコンポーネント間の相互作用のブリッジを提供します。したがって、kube-apiserverの理解を深めることは非常に重要です。

1つの記事でKubernetesAPIServerの原理を読む

全体的なコンポーネント機能

kube-apiserverは、Kubernetesクラスター全体のetcd操作の唯一のエントリとして、Kubernetesリソースの認証と認証、検証、およびCRUD操作を担当し、他のコンポーネントが呼び出すRESTfulAPIを提供します。

1つの記事でKubernetesAPIServerの原理を読む

kube-apiserverには、次の3つのAPIServerが含まれています。

  • aggregatorServerapiregistration.k8s.ioグループの下のAPIServiceリソース要求の処理を担当し、ユーザーからのインターセプト要求は集約サーバー(AA)に転送されます。
  • kubeAPIServer:さまざまな組み込みリソース(ポッド、デプロイメント、サービスなど)の認証、認証、RESTサービスなど、要求の一般的な処理を担当します。
  • apiExtensionsServer:CustomResourceDefinition(CRD)apiResourcesおよびapiVersionsの登録を担当し、CRDおよび対応するCustomResource(CR)REST要求を処理します(対応するCRを処理できない場合は、404を返します)。これは、apiserver委任の最後のリンクでもあります。

また、Kubernetesのデフォルトのapiserverサービスの作成と管理を主に担当するbootstrap-controllerも含まれています。

以下に、上記のコンポーネントの概要を示します。

ブートストラップコントローラー

  • apiserverブートストラップコントローラーの作成と操作のロジックは、k8s.io / kubernetes / pkg / masterディレクトリにあります。
  • bootstrap-controllerは主に、内部kubernetesのデフォルトのapiserverサービスを作成および維持するために使用されます
  • kubernetesのデフォルトのapiserverサービスspec.selectorは空です。これは、デフォルトのapiserverサービスと他の通常のサービスとの最大の違いです。これは、この特別なサービスに対応するエンドポイントがエンドポイントコントローラーによって制御されるのではなく、kube-apiserverブートストラップコントローラー(維持される)によって直接管理されることを示します。ポッドセレクターではなく、このコードによって)
  • bootstrap-controllerの主な機能は次のとおりです。
    • デフォルトのkube-systemおよびkube-publicおよびkube-node-lease名前名を作成します
    • kubernetesのデフォルトのapiserverサービスと対応するエンドポイントを作成して維持する
    • Service ClusterIP(--service-cluster-ip-range指定された範囲)に基づいて検査および修復機能を提供します
    • Service NodePort(--service-node-port-range指定された範囲)に基づいて検査および修復機能を提供します
// 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()
}

コード原則の詳細については、kubernetes-reading-notesを参照してください

kubeAPIServer

KubeAPIServerは、主に組み込みAPIリソースの操作要求を提供し、Kubernetesの各APIリソースのルーティング情報を登録し、同時にRESTful APIを公開します。これにより、クラスター内外のサービスがRESTfulAPIを介してKubernetesのリソースを操作できるようになります。

さらに、kubeAPIServerはKubernetes apiserver全体の中核です。以下で説明するaggregatorServerとapiExtensionsServerはすべて、拡張用のkubeAPIServerに基づいています(ユーザー定義リソースをサポートするKubernetesの機能を補完します)。

kubeAPIServerのコア機能は、次のように、Kubernetes組み込みリソースにルートを追加することです。

  • m.InstallLegacyAPIapiserverでコアAPIリソースへのルートを追加するために呼び出し/apiます。これがリソースの始まりです。
  • m.InstallAPIsapiserverで、ルートに追加された拡張APIリソースを呼び出します。これが/apisリソースの始まりです。
// 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
}

kubeAPIServer全体は、次の3種類のAPIリソースインターフェイスを提供します。

  • コアグループ:主/api/v1に;
  • 名前付きグループ:そのパスは/apis/$GROUP/$VERSION;
  • システムステータスの一部のAPI:のような/metrics/version等。

/apis/{group}/{version}/namespaces/{namespace}/resource/{name}以下に示すように、URLとAPIは実質的に構成、構造になっています。

1つの記事でKubernetesAPIServerの原理を読む

kubeAPIServerは、APIリソースごとに対応するRESTStorageを作成します。RESTStorageの目的は、各リソースのアクセスパスとそのバックエンドストレージ操作を対応させることです。構築されたRESTストレージインターフェイスを介して、リソースが実行できる操作( :作成、更新など)、対応する操作を実行中に保存します。各操作は標準のRESTメソッドに対応します。たとえば、createはPOSTとしてのRESTメソッドに対応し、updateはPUTとしてのRESTメソッドに対応します。最後に、actions配列に従って順番にトラバースし、各操作にハンドラーを追加し(ハンドラーは、REST Storageによって実装された関連インターフェイスに対応します)、ルートに登録し、最後に次のように外部にRESTfulAPIを提供します。

// 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.
    }
    ...
}

kubeAPIServerのコード構造は次のように構成されています。

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

コールチェーンは次のように構成されています。

1つの記事でKubernetesAPIServerの原理を読む

コード原則の詳細については、kubernetes-reading-notesを参照してください

aggregatorServer

aggregatorServerは主に、KubernetesAPIリソースを拡張する2番目のメソッドAggregatedAPIServer(AA)を処理するために使用され、CR要求をAAにプロキシします。

1つの記事でKubernetesAPIServerの原理を読む

ここでは、Kubernetesの公式によって提供された集約されたapiserverの例のsample-apiserverと組み合わせると、要約の原則は次のようになります。

  • AggregatorServerは、APIServicesオブジェクトを介してサービスに関連付けて要求を転送し、関連付けられたサービスタイプによって、要求転送の形式がさらに決定されます。AggregatorServerには1つが含まGenericAPIServerれ、独自の状態を維持しますControllerこれはGenericAPIServer主にapiregistration.k8s.ioグループの下でAPIServiceリソース要求を処理し、コントローラーには次のものが含まれます。

    • apiserviceRegistrationController:APIServiceによって定義された集約サーバーサービスに基づいてプロキシを構築し、CR要求をバックエンドの集約サーバーに転送する責任があります
    • availableConditionController:参照されているサービスが利用可能かどうかなど、APIServicesの可用性ステータスを維持します。
    • autoRegistrationController:APIで特定のAPIServiceのセットを維持するために使用されます。
    • crdRegistrationController:CRDGroupVersionsをAPIServicesに自動的に登録する責任があります。
    • openAPIAggregationController:APIServicesリソースの変更を提供されたOpenAPIドキュメントに同期します。
  • apiserviceRegistrationControllerは、APIServiceによって定義された集約サーバーサービスに基づいてプロキシを構築し、CRの要求をバックエンドの集約サーバーに転送する役割を果たします。apiServiceには、ローカル(サービスが空)とサービス(サービスが空ではない)の2つのタイプがあります。apiserviceRegistrationControllerは、次の2種類のapiServiceのプロキシを設定します。ローカルタイプは、処理のためにkube-apiserverに直接ルーティングされます。一方、サービスタイプは、プロキシを設定し、リクエストを集約サービスのリクエストに変換します(proxyPath:= "/ apis /" + apiService.Spec.Group + "/" + apiService.Spec.Version)、および要求された負荷分散ポリシーは、kube-apiserverへのローカルアクセスを優先することです(サービスがkubernetesのデフォルトのapiserverサービス:443の場合)=>サービスClusterIP:Portを介したアクセス(デフォルト)または、サービスエンドポイントバックエンドをランダムに選択してアクセスします。

    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
  • aggregatorServerの作成中に、kube-apiserverによって定義されたすべてのAPIリソースに基づいてデフォルトのAPIServiceリストが作成されます。名前はです$VERSION.$GROUP。これらのAPIServiceにはタグがありますkube-aggregator.kubernetes.io/automanaged: onstart(例:v1.apps apiService)。autoRegistrationControllerは、これらのリストにAPIServiceを作成して維持します。これは、ローカルapiServiceです。カスタムAPIService(集約サーバー)の場合、処理されません。

  • 集約サーバーは、CR(カスタムAPIリソース)のCRUD APIインターフェースを実装し、バックエンドストレージを柔軟に選択できます。etcdをコアkube-apiserverと共有したり、etcdデータベースや他のデータベースを個別にデプロイしたりできます。集約サーバーによって実装されるCRAPIパスは/ apis / $ GROUP / $ VERSIONであり、特定のサンプルapiserverは:/apis/wardle.example.com/v1alpha1であり、次のリソースタイプは次のとおりです。flundersおよびfischers

  • 集約サーバーは、APIServiceタイプのリソースを展開することにより、コアkube-apiserverとの統合と相互作用を実装し、サービスフィールドは対応する集約サーバーサービスを指します。

  • sample-apiserverのディレクトリ構造は次のとおりです。独自の集約サーバーの作成を参照できます。

    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
    • それらの中で、アーティファクトはyamlの例を展開するために使用されます
    • ハックディレクトリには自動スクリプトが保存されます(例:update-codegen)
    • main.goは、集約サーバーの開始エントリです。pkg/ cmdは、集約サーバーの特定のロジックを開始する役割を果たします。pkg/ apiserverは、集約サーバーの初期化とルーティング登録に使用されます。
    • pkg / apisは、自動的に生成される関連するCRの構造の定義を担当します(update-codegen)
    • pkg / Admissionは、アドミッションに関連するコードを担当します
    • pkg / generateは、CRにアクセスするためのクライアントセット、インフォーマー、およびリスナーの生成を担当します
    • pkg / registryディレクトリはCR関連のRESTStorageの実装を担当します

コード原則の詳細については、kubernetes-reading-notesを参照してください

apiExtensionsServer

apiExtensionsServerは、主にCustomResourceDefinition(CRD)apiResourcesおよびapiVersionsの登録を担当し、CRDおよび対応するCustomResource(CR)REST要求を処理し(対応するCRを処理できない場合は、404を返します)、apiserver委任の最後のリンクでもあります。

原則は次のように要約されます。

  • CRと呼ばれるカスタムリソースは、Kubernetesカスタムリソースタイプであり、ポッド、サービスなど、Kubernetesのさまざまな組み込みリソースタイプに対応します。CRを使用して、必要なリソースタイプを定義できます

  • CRDは、カスタムapi-resourcesを実装するためにyamlファイルの形式でCRをKubernetesに登録します。これは、Kubernetes APIリソースを拡張する2番目の方法であり、一般的に使用される方法でもあります。

  • APIExtensionServerは、CustomResourceDefinition(CRD)apiResourcesおよびapiVersionsの登録を担当します。また、CRDおよび対応するCustomResource(CR)REST要求を処理します(対応するCRを処理できない場合は、404を返します)。これは、apiserver委任の最後のリンクでもあります。

  • crdRegistrationControllerCRDGroupVersionsをAPIServicesに自動的に登録する責任があります。具体的なロジックは次のとおりです。すべてのCRDを列挙し、CRDで定義されたcrd.Spec.Groupフィールドとcrd.Spec.Versionsフィールドに従ってAPIServiceを構築し、それをautoRegisterController.apiServicesToSyncに追加すると、autoRegisterControllerがそれを作成および維持します。これが、CRDの作成後に対応するAPIServiceオブジェクトが生成される理由です。

  • APIExtensionServerに含まれるコントローラーと関数は次のとおりです。

    • openapiController:OpenAPIドキュメントはcrdリソースを変更して、/openapi/v2ビューからアクセスできる同期を提供します。

    • crdController:情報の責任とapiVersionsのapiResourcesを登録する青石、情報を通じて両方kubectl api-versionskubectl api-resources見ます。

    • kubectl api-versionsコマンドは、(実際には2つの要求は、1発行されたすべてのKubernetesクラスタリソースのバージョン情報を返すhttps://127.0.0.1:6443/apiと、1それぞれをhttps://127.0.0.1:6443/apis、そして2つの要求の戻り結果は最後にマージされました)
    $ 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このコマンドは、最初にすべてのAPIバージョン情報を取得し、次に各APIバージョンのインターフェイスを呼び出して、そのバージョンのすべてのAPIリソースタイプを取得することです。

      $ 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:crdobjで名前の競合があるかどうかを確認します.status.conditionsこれはcrdで確認できます

      • establishingController:crdが正常な状態か確認してください。crdで確認でき.status.conditionsます。

      • nonStructuralSchemaController:crdobj構造が正常かどうかを確認してください。crdで.status.conditions表示できます。

      • apiApprovalController:crdがKubernetesAPI宣言ポリシーに準拠しているかどうかを確認します.status.conditionsこれはcrdで確認できます

      • finalizingController:CRの削除に関連する、ファイナライズの機能と同様です。
  • CR CRUDAPIServerの処理ロジックは次のように要約されます。

    • createAPIExtensionsServer => NewCustomResourceDefinitionHandler => crdHandler => 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
    }
    
    • crdHandlerの処理ロジックは次のとおりです。

    • req(GET /apis/duyanghao.example.com/v1/namespaces/default/students)を分析し、リクエストパスのグループ(duyanghao.example.com)、バージョン(v1)、およびリソースフィールド(students)に従って対応するCRDを取得します。 Content(crd、err:= r.crdLister.Get(crdName))

    • crd.UIDおよびcrd.Nameを介してcrdInfoを取得します。存在しない場合は、対応するcrdInfo(crdInfo、err:= r.getOrCreateServingInfoFor(crd.UID、crd.Name))を作成します。crdInfoには、CRD定義と、CustomResource.RESTストレージのカスタムリソースに対応するCRDが含まれています。

    • customresource.RESTストレージは、CRに対応するグループ(duyanghao.example.com)、バージョン(v1)、種類(Student)、リソース(students)などによって作成されます。CRにはKubernetesコードで特定の構造定義がないため、ここにあります。最初に、一般的な構造Unstructured(すべてのタイプのカスタムリソースを保存するために使用)を初期化し、構造に対してSetGroupVersionKind操作を実行します(特定のカスタムリソースタイプを設定します)

    • customresource.RESTストレージから非構造化構造を取得した後、変換されて返されます

      // 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
      }
      }
      

コード原則の詳細については、kubernetes-reading-notesを参照してください

結論

この記事では、aggregatorServer、kubeAPIServer、apiExtensionsServer、bootstrap-controllerなどのKubernetesapiserverをソースコードレベルから要約します。この記事を読むことで、apiserverの内部原則を一般的に理解し、詳細な調査をフォローアップするのに役立ちます。

参照

おすすめ

転載: blog.51cto.com/14120339/2598532
おすすめ