日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Kubernetes API 聚合开发汇总

發(fā)布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Kubernetes API 聚合开发汇总 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2. Kubernetes API 聚合開發(fā)

自定義資源實際上是為了擴展 kubernetes 的 API,向 kubenetes API 中增加新類型,可以使用以下三種方式:

  • 修改 kubenetes 的源碼,顯然難度比較高,也不太合適
  • 創(chuàng)建自定義 API server 并聚合到 API 中
  • 創(chuàng)建自定義資源(CRD)

2.1 CRD存在的問題

  • 只支持 etcd
  • 只支持JSON,不支持 protobuf (一種高性能的序列化語言)
  • 只支持2種子資源接口 ( /status 和 /scale)
  • 不支持優(yōu)雅刪除
  • 顯著增加 api server 負擔(dān)
  • 只支持 CRUD 原語
  • 不支持跨 API groups 共享存儲
  • 2.2 自定義API server相比CRD的優(yōu)勢

  • 底層存儲無關(guān)(像metrics server 存在內(nèi)存里面)
  • 支持 protobuf
  • 支持任意自定義子資源
  • 可以實現(xiàn)優(yōu)雅刪除
  • 支持復(fù)雜驗證
  • 支持自定義語義
  • 2.3 使用自定義API server前先考慮以下幾點

  • 你的 API 是否屬于 聲明式的
  • 是否想使用 kubectl 命令來管理
  • 是否要作為 kubenretes 中的對象類型來管理,同時顯示在 kubernetes dashboard 上
  • 是否可以遵守 kubernetes 的 API 規(guī)則限制,例如 URL 和 API group、namespace 限制
  • 是否可以接受該 API 只能作用于集群或者 namespace 范圍
  • 想要復(fù)用 kubernetes API 的公共功能,比如 CRUD、watch、內(nèi)置的認(rèn)證和授權(quán)等
  • 2.4 Kubernetes APIServer 和 自定義APIServer的關(guān)系

    2.4.1 Kubernetes 的 Aggregated API是什么?

    1.1 概述

    Aggregated(聚合的)API server 是為了將原來的 API server 這個巨石(monolithic)應(yīng)用給拆分開,為了方便用戶開發(fā)自己的 API server 集成進來,而不用直接修改 Kubernetes 官方倉庫的代碼,這樣一來也能將 API server 解耦,方便用戶使用實驗特性,簡而言之,它是允許k8s的開發(fā)人員編寫一個自己的服務(wù),可以把這個服務(wù)注冊到k8s的api里面,這樣,就像k8s自己的api一樣,自定義的服務(wù)只要運行在k8s集群里面,k8s 的Aggregate通過service名稱就可以轉(zhuǎn)發(fā)到我們自定義的service里面去了。這些 API server 可以跟 kube-apiserver 無縫銜接,使用 kubectl 也可以管理它們。

    在 1.7+ 版本及以后,聚合層apiserver和 kube-apiserver 一起運行。在擴展資源被注冊前,聚合層不執(zhí)行任何操,要注冊其 API,用戶必需添加一個 APIService 對象,該對象需在 Kubernetes API 中聲明 URL 路徑,聚合層將發(fā)送到該 API 路徑(e.g. /apis/myextension.mycompany.io/v1/…)的所有對象代理到注冊的 APIService。

    通常,通過在集群中的一個 Pod 中運行一個 extension-apiserver 來實現(xiàn) APIService。如果已添加的資源需要主動管理,這個 extension-apiserver 通常需要和一個或多個controller配對。

    1.2 設(shè)計理念

    • api的擴展性:這樣k8s的開發(fā)人員就可以編寫自己的API服務(wù)器來公開他們想要的API。集群管理員應(yīng)該能夠使用這些服務(wù),而不需要對核心庫存儲庫進行任何更改。
    • 豐富了APIs:核心kubernetes團隊阻止了很多新的API提案。通過允許開發(fā)人員將他們的API作為單獨的服務(wù)器公開,并使集群管理員能夠在不對核心庫存儲庫進行任何更改的情況下使用它們,這樣就無須社區(qū)繁雜的審查了。
    • 開發(fā)分階段實驗性API的地方:新的API可以在單獨的聚集服務(wù)器中開發(fā),當(dāng)它穩(wěn)定之后,那么把它們封裝起來安裝到其他集群就很容易了。
    • 確保新API遵循kubernetes約定:如果沒有這里提出的機制,社區(qū)成員可能會被迫推出自己的東西,這可能會或可能不遵循kubernetes約定。

    1.3 Kubernetes Aggregated原理解析

    我們說的自定義API其實就是和Metrics Server的實現(xiàn)方式一樣,都是通過注冊API的形式來完成和Kubernetes的集成的,也就是在API Server增加原本沒有的API。不過添加API還可以通過CRD的方式完成,不過我們這里直說聚合方式。看下圖:

    Kube-Aggregator類似于一個七層負載均衡,將來自用戶的請求攔截轉(zhuǎn)發(fā)給其他服務(wù)器,并且負責(zé)整個 APIServer 的 Discovery 功能。

    通過APIServices對象關(guān)聯(lián)到某個Service來進行請求的轉(zhuǎn)發(fā),其關(guān)聯(lián)的Service類型進一步?jīng)Q定了請求轉(zhuǎn)發(fā)形式。Aggregator包括一個GenericAPIServer和維護自身狀態(tài)的Controller。其中 GenericAPIServer主要處理apiregistration.k8s.io組下的APIService資源請求。

    主要controller包括:

    • apiserviceRegistrationController:負責(zé)APIServices中資源的注冊與刪除;
    • availableConditionController:維護APIServices的可用狀態(tài),包括其引用Service是否可用等;
    • autoRegistrationController:用于保持API中存在的一組特定的APIServices;
    • crdRegistrationController:負責(zé)將CRD GroupVersions自動注冊到APIServices中;
    • openAPIAggregationController:將APIServices資源的變化同步至提供的OpenAPI文檔;

    假設(shè)有兩個路由分別訪問API,實際上我們訪問API的時候訪問的是一個aggregator的代理層,下面橙色的都是可用的服務(wù)后端。我們訪問上圖中的2個URL其實是被代理到不同的后端,在這個機制下你可以添加更多的后端,比如舉例說說Custome-metrics-apiserver綠色線條的路徑。

    注冊自定義API文件

    該文件的主要作用就是向Api server注冊一個api,此API名稱是關(guān)聯(lián)到一個service名稱上。

    apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata:name: v1beta1.custom.metrics.k8s.iolabels:api: custom-metrics-apiserverapiserver: "true" spec:version: v1beta1 #API版本group: custom.metrics.k8s.io #API所屬的組groupPriorityMinimum: 2000service:name: custom-metrics-apiserver #自定義API所關(guān)聯(lián)的service名稱,當(dāng)訪問這個自定義API后轉(zhuǎn)發(fā)到哪個service處理,就根據(jù)這個service名稱選擇namespace: defaultversionPriority: 10caBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0"

    上面定義了資源類型為APIService,service名稱為custom-metrics-apiserver,空間為default的一個資源聚合接口。

    下面帶大家從源代碼的角度來看,代碼路徑:
    staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go ,和k8s其它controller一樣,watch變化分發(fā)到add、update和delete方法

    apiServiceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{AddFunc: c.addAPIService,UpdateFunc: c.updateAPIService,DeleteFunc: c.deleteAPIService,})

    主要監(jiān)聽兩種資源apiService和service,路徑:

    staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go

    func (s *APIAggregator) AddAPIService(apiService *v1.APIService) error {// if the proxyHandler already exists, it needs to be updated. The aggregation bits do not// since they are wired against listers because they require multiple resources to respondif proxyHandler, exists := s.proxyHandlers[apiService.Name]; exists {proxyHandler.updateAPIService(apiService)if s.openAPIAggregationController != nil {s.openAPIAggregationController.UpdateAPIService(proxyHandler, apiService)}return nil}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 handlerproxyHandler := &proxyHandler{localDelegate: s.delegateHandler,proxyCurrentCertKeyContent: s.proxyCurrentCertKeyContent,proxyTransport: s.proxyTransport,serviceResolver: s.serviceResolver,egressSelector: s.egressSelector,}proxyHandler.updateAPIService(apiService)if s.openAPIAggregationController != nil {s.openAPIAggregationController.AddAPIService(proxyHandler, apiService)}s.proxyHandlers[apiService.Name] = proxyHandlers.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler)s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler)// if we're dealing with the legacy group, we're done hereif apiService.Name == legacyAPIServiceName {return nil}// if we've already registered the path with the handler, we don't want to do it again.if s.handledGroups.Has(apiService.Spec.Group) {return nil}// it's time to register the group aggregation endpointgroupPath := "/apis/" + apiService.Spec.GroupgroupDiscoveryHandler := &apiGroupHandler{codecs: aggregatorscheme.Codecs,groupName: apiService.Spec.Group,lister: s.lister,delegate: s.delegateHandler,}// aggregation is protecteds.GenericAPIServer.Handler.NonGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)s.handledGroups.Insert(apiService.Spec.Group)return nil }

    結(jié)合上面的源碼:

    proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version

    例子就是/apis/custom-metrics.k8s.io/v1beta1,而處理方法請求的handle就是

    // register the proxy handlerproxyHandler := &proxyHandler{localDelegate: s.delegateHandler,proxyCurrentCertKeyContent: s.proxyCurrentCertKeyContent,proxyTransport: s.proxyTransport,serviceResolver: s.serviceResolver,egressSelector: s.egressSelector,}proxyHandler.updateAPIService(apiService)

    updateAPIService就是更新這個proxy的后端service,路徑:

    staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go

    func (r *proxyHandler) updateAPIService(apiService *apiregistrationv1api.APIService) {if apiService.Spec.Service == nil {r.handlingInfo.Store(proxyHandlingInfo{local: true})return}proxyClientCert, proxyClientKey := r.proxyCurrentCertKeyContent()newInfo := proxyHandlingInfo{name: apiService.Name,restConfig: &restclient.Config{TLSClientConfig: restclient.TLSClientConfig{Insecure: apiService.Spec.InsecureSkipTLSVerify,ServerName: apiService.Spec.Service.Name + "." + apiService.Spec.Service.Namespace + ".svc",CertData: proxyClientCert,KeyData: proxyClientKey,CAData: apiService.Spec.CABundle,},},serviceName: apiService.Spec.Service.Name,serviceNamespace: apiService.Spec.Service.Namespace,servicePort: *apiService.Spec.Service.Port,serviceAvailable: apiregistrationv1apihelper.IsAPIServiceConditionTrue(apiService, apiregistrationv1api.Available),}if r.egressSelector != nil {networkContext := egressselector.Cluster.AsNetworkContext()var egressDialer utilnet.DialFuncegressDialer, err := r.egressSelector.Lookup(networkContext)if err != nil {klog.Warning(err.Error())} else {newInfo.restConfig.Dial = egressDialer}} else if r.proxyTransport != nil && r.proxyTransport.DialContext != nil {newInfo.restConfig.Dial = r.proxyTransport.DialContext}newInfo.proxyRoundTripper, newInfo.transportBuildingError = restclient.TransportFor(newInfo.restConfig)if newInfo.transportBuildingError != nil {klog.Warning(newInfo.transportBuildingError.Error())}r.handlingInfo.Store(newInfo) }

    上述源碼中restConfig就是調(diào)用service的客戶端參數(shù),其中

    ServerName: apiService.Spec.Service.Name + "." + apiService.Spec.Service.Namespace + ".svc"

    指的就是具體的service。

    2.4.2 認(rèn)證流程

    工作方式

    與自定義資源定義(CRD)不同,除標(biāo)準(zhǔn)的 Kubernetes kube-apiserver 外,Aggregation API 還涉及另一個服務(wù)器:Extension apiserver。Kubernetes kube-apiserver 將需要與自定義的 Extension apiserver 通信,并且自定義的 Extension apiserver 也需要與 Kubernetes kube-apiserver 通信。為了確保此通信的安全,Kubernetes kube-apiserver 使用 x509 證書向 Extension apiserver 認(rèn)證。具體流程如下:

  • Kubernetes kube-apiserver:對發(fā)出請求的用戶身份認(rèn)證,并對請求的 API 路徑執(zhí)行鑒權(quán)
  • Kubernetes kube-apiserver (aggregator):將請求轉(zhuǎn)發(fā)到 Extension apiserver (aggregated apiserver)
  • Extension apiserver:認(rèn)證來自 Kubernetes kube-apiserver 的請求
  • Extension apiserver:對來自原始用戶的請求鑒權(quán)
  • Extension apiserver:執(zhí)行
  • Kubernetes kube-apiserver 認(rèn)證和授權(quán)

    假設(shè)我們已經(jīng)在 Kubernetes kube-apiserver 注冊了 Extension apiserver。

    當(dāng)用戶請求訪問 path ,Kubernetes kube-apiserver 使用它的標(biāo)準(zhǔn)認(rèn)證和授權(quán)配置來對用戶認(rèn)證,以及對特定 path 的鑒權(quán),到目前為止,所有內(nèi)容都是標(biāo)準(zhǔn)的 Kubernetes API 請求,認(rèn)證與鑒權(quán),接下來 Kubernetes kube-apiserver 現(xiàn)在準(zhǔn)備將請求發(fā)送到 Extension apiserver。

    Kubernetes kube-apiserver認(rèn)證時,認(rèn)證接受會將認(rèn)證信息刪除,處理邏輯如下:

    • 1.通過context獲取user信息
    • 2.構(gòu)造請求,刪除requestheader信息,通過user重新填充
    • 3.通過proxyRoundTripper轉(zhuǎn)發(fā)請求

    代碼路徑:

    staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go

    func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {value := r.handlingInfo.Load()if value == nil {r.localDelegate.ServeHTTP(w, req)return}handlingInfo := value.(proxyHandlingInfo)if handlingInfo.local {if r.localDelegate == nil {http.Error(w, "", http.StatusNotFound)return}r.localDelegate.ServeHTTP(w, req)return}if !handlingInfo.serviceAvailable {proxyError(w, req, "service unavailable", http.StatusServiceUnavailable)return}if handlingInfo.transportBuildingError != nil {proxyError(w, req, handlingInfo.transportBuildingError.Error(), http.StatusInternalServerError)return}// 通過context獲取useruser, ok := genericapirequest.UserFrom(req.Context())if !ok {proxyError(w, req, "missing user", http.StatusInternalServerError)return}// write a new location based on the existing request pointed at the target service// 構(gòu)造請求url,通過apiservice配置的service/namespace隨機得到某個endpoint后端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.Hostlocation.Path = req.URL.Pathlocation.RawQuery = req.URL.Query().Encode()newReq, cancelFn := newRequestForProxy(location, req)defer cancelFn()if handlingInfo.proxyRoundTripper == nil {proxyError(w, req, "", http.StatusNotFound)return}// we need to wrap the roundtripper in another roundtripper which will apply the front proxy headersproxyRoundTripper, upgrade, err := maybeWrapForConnectionUpgrades(handlingInfo.restConfig, handlingInfo.proxyRoundTripper, req)if err != nil {proxyError(w, req, err.Error(), http.StatusInternalServerError)return}// 包裹請求信息,將user信息放到header中proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)// if we are upgrading, then the upgrade path tries to use this request with the TLS config we provide, but it does// NOT use the roundtripper. Its a direct call that bypasses the round tripper. This means that we have to// attach the "correct" user headers to the request ahead of time. After the initial upgrade, we'll be back// at the roundtripper flow, so we only have to muck with this request, but we do have to do it.if upgrade {transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetGroups(), user.GetExtra())}// 調(diào)用后端handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})handler.ServeHTTP(w, newReq) }

    根據(jù)擴展apiserver找到后端時通過service獲取對應(yīng)endpoint列表,隨機選擇某個endpoint、實現(xiàn)如下,源碼路徑:

    staging/src/k8s.io/apiserver/pkg/util/proxy/proxy.go

    func ResolveEndpoint(services listersv1.ServiceLister, endpoints listersv1.EndpointsLister, namespace, id string, port int32) (*url.URL, error) {svc, err := services.Services(namespace).Get(id)if err != nil {return nil, err}svcPort, err := findServicePort(svc, port)if err != nil {return nil, err}switch {case svc.Spec.Type == v1.ServiceTypeClusterIP, svc.Spec.Type == v1.ServiceTypeLoadBalancer, svc.Spec.Type == v1.ServiceTypeNodePort:// these are finedefault:return nil, fmt.Errorf("unsupported service type %q", svc.Spec.Type)}eps, err := endpoints.Endpoints(namespace).Get(svc.Name)if err != nil {return nil, err}if len(eps.Subsets) == 0 {return nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", svc.Name))}// Pick a random Subset to start searching from.ssSeed := rand.Intn(len(eps.Subsets))// Find a Subset that has the port.for ssi := 0; ssi < len(eps.Subsets); ssi++ {ss := &eps.Subsets[(ssSeed+ssi)%len(eps.Subsets)]if len(ss.Addresses) == 0 {continue}for i := range ss.Ports {if ss.Ports[i].Name == svcPort.Name {// Pick a random address.ip := ss.Addresses[rand.Intn(len(ss.Addresses))].IPport := int(ss.Ports[i].Port)return &url.URL{Scheme: "https",Host: net.JoinHostPort(ip, strconv.Itoa(port)),}, nil}}}return nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", id)) }

    ProxyRoundTripper創(chuàng)建路徑:

    staging/src/k8s.io/client-go/transport/round_trippers.go

    func NewAuthProxyRoundTripper(username string, groups []string, extra map[string][]string, rt http.RoundTripper) http.RoundTripper {return &authProxyRoundTripper{username: username,groups: groups,extra: extra,rt: rt,} }func (rt *authProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {req = utilnet.CloneRequest(req)// 設(shè)置user信息SetAuthProxyHeaders(req, rt.username, rt.groups, rt.extra)return rt.rt.RoundTrip(req) }// SetAuthProxyHeaders stomps the auth proxy header fields. It mutates its argument. func SetAuthProxyHeaders(req *http.Request, username string, groups []string, extra map[string][]string) {// 清除原始url的requestheader信息req.Header.Del("X-Remote-User")req.Header.Del("X-Remote-Group")for key := range req.Header {if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {req.Header.Del(key)}}// 通過user重新填充信息req.Header.Set("X-Remote-User", username)for _, group := range groups {req.Header.Add("X-Remote-Group", group)}for key, values := range extra {for _, value := range values {req.Header.Add("X-Remote-Extra-"+headerKeyEscape(key), value)}} }

    Kubernetes kube-apiserver 代理請求

    Kubernetes kube-apiserver 現(xiàn)在將請求發(fā)送或代理到注冊以處理該請求的 Extension apiserver。為此,它需要了解幾件事:

  • Kubernetes kube-apiserver 應(yīng)該如何向 Extension apiserver 認(rèn)證,以通知 Extension apiserver 通過網(wǎng)絡(luò)發(fā)出的請求來自有效的 Kubernetes kube-apiserver?
  • Kubernetes kube-apiserver 應(yīng)該如何通知 Extension apiserver 原始請求已通過認(rèn)證的用戶名和組?
  • 簡而言之,就是 Kubernetes kube-apiserver 已經(jīng)認(rèn)證和鑒權(quán)用戶的請求,怎么將這些信息傳遞給 Extension apiserver,為提供這兩條信息,我們必須使用若干啟動參數(shù)來配置 Kubernetes apiserver。

    Kubernetes kube-apiserver 客戶端認(rèn)證

    Kubernetes kube-apiserver 通過 TLS 連接到 Extension apiserver,并使用客戶端證書認(rèn)證,這里 Kubernetes kube-apiserver (aggregator or proxy) 是 Extension apiserver 的客戶端。必須在啟動時使用提供的參數(shù)向 Kubernetes kube-apiserver 提供以下內(nèi)容:

    • 通過 --proxy-client-key-file 指定簽名私鑰文件
    • 通過 --proxy-client-cert-file 指定驗簽證書文件
    • 通過 --requestheader-client-ca-file 簽署客戶端證書文件的 CA 證書
    • 通過 --requestheader-allowed-names 在簽署的客戶證書中有效的公用名(CN)

    Kubernetes kube-apiserver 將使用由 –proxy-client-*-file 指示的文件來通過 Extension apiserver 驗證。為了使合規(guī)的 Extension apiserver 能夠?qū)⒃撜埱笠暈橛行?#xff0c;必須滿足以下條件:

  • 連接必須使用由 CA 簽署的客戶端證書,該證書的證書位于 --requestheader-client-ca-file 中。
  • 連接必須使用客戶端證書,該客戶端證書的 CN 是 --requestheader-allowed-names 中列出的證書之一。 **注意:**您可以將此選項設(shè)置為空白,即為--requestheader-allowed-names=""。這將向擴展 apiserver 指示任何 CN 是可接受的。
  • 使用這些選項啟動時,Kubernetes kube-apiserver 將:

  • 使用它們來通過 Extension apiserver 的認(rèn)證。
  • 在名為 kube-system 命名空間中創(chuàng)建一個 configmap extension-apiserver-authentication ,它將在其中放置 CA 證書和允許的 CN。反過來,Extension apiserver 可以檢索這些內(nèi)容以驗證請求。
  • 保存原始請求用戶名和組信息

    當(dāng) Kubernetes kube-apiserver 將請求代理到 Extension apiserver 時,它將向 Extension apiserver 通知原始請求已成功通過其驗證的用戶名和組。它在其代理請求的 http 標(biāo)頭中提供這些。您必須將要使用的標(biāo)頭名稱告知 Kubernetes kube-apiserver。

    • 通過--requestheader-username-headers 標(biāo)明用來保存用戶名的頭部
    • 通過--requestheader-group-headers 標(biāo)明用來保存 group 的頭部
    • 通過--requestheader-extra-headers-prefix 標(biāo)明用來保存拓展信息前綴的頭部

    這些標(biāo)頭名稱也放置在extension-apiserver-authentication 的 configmap 中,因此 Extension apiserver 可以檢索和使用它們。

    Extension apiserver 認(rèn)證

    Extension apiserver 在收到來自 Kubernetes kube-apiserver 的代理請求后,必須驗證該請求確實來自有效的身份驗證代理,該認(rèn)證代理由 Kubernetes kube-apiserver 履行。Extension apiserver 通過以下方式對其認(rèn)證:

  • 如上所述,從kube-system中的 configmap 中檢索以下內(nèi)容:
    • 客戶端 CA 證書 --requestheader-client-ca-file。
    • 允許名稱(CN)列表 --requestheader-allowed-names。
    • 用戶名,組和其他信息的頭部。
  • 使用以下證書檢查 TLS 連接是否已通過認(rèn)證:
    • 由其證書與檢索到的 CA 證書匹配的 CA 簽名。
    • 在允許的 CN 列表中有一個 CN,除非列表為空,在這種情況下允許所有 CN。
    • 從適當(dāng)?shù)念^部中提取用戶名和組。

    如果以上均通過,則該請求是來自合法認(rèn)證代理(在本例中為 Kubernetes kube-apiserver)的有效代理請求。

    為了具有檢索 configmap 的權(quán)限,Extension apiserver 需要適當(dāng)?shù)慕巧T?kube-system 名字空間中有一個默認(rèn)角色extension-apiserver-authentication-reader 可用于設(shè)置。

    Extension apiserver 執(zhí)行

    如果SubjectAccessReview通過,則擴展 apiserver 執(zhí)行請求。

    2.4.3 部署過程

    安裝cfssl

    wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O /usr/local/bin/cfssl wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -O /usr/local/bin/cfssljson wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -O /usr/local/bin/cfssl-certinfo cd /usr/local/bin/ chmod +x cfssl cfssljson cfssl-certinfo

    創(chuàng)建CA

    CA 配置文件
    $ cat > aggregator-ca-config.json <<EOF {"signing": {"default": {"expiry": "87600h"},"profiles": {"aggregator": {"usages": ["signing","key encipherment","server auth","client auth"],"expiry": "87600h"}}} } EOF
    • profiles : 可以定義多個 profiles,分別指定不同的過期時間、使用場景等參數(shù);后續(xù)在簽名證書時使用某個 profile。
    • signing :表示該證書可用于簽名其它證書;生成的 aggregator-ca.pem 證書中 CA=TRUE。
    • server auth :表示 Client 可以用該 CA 對 Server 提供的證書進行驗證。
    • client auth :表示 Server 可以用該 CA 對 Client 提供的證書進行驗證。
    創(chuàng)建CA證書簽名請求
    $ cat > aggregator-ca-csr.json <<EOF {"CN": "aggregator","key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","ST": "Shanghai","L": "Shanghai","O": "k8s","OU": "wzlinux"}],"ca": {"expiry": "87600h"} }
    • “CN” :Common Name,kube-apiserver 從證書中提取該字段作為請求的用戶名 (User Name);瀏覽器使用該字段驗證網(wǎng)站是否合法。
    • “O” :Organization,kube-apiserver 從證書中提取該字段作為請求用戶所屬的組 (Group);
    生成CA證書和私鑰
    cfssl gencert -initca aggregator-ca-csr.json | cfssljson -bare aggregator-ca

    創(chuàng)建Kubernetes證書

    創(chuàng)建aggregator證書簽名請求
    $ cat > aggregator-csr.json <<EOF {"CN": "aggregator","hosts": ["127.0.0.1","172.18.0.101","172.18.0.102","172.18.0.103","10.96.0.1","kubernetes","kubernetes.default","kubernetes.default.svc","kubernetes.default.svc.cluster","kubernetes.default.svc.cluster.local"],"key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","ST": "Shanghai","L": "Shanghai","O": "k8s","OU": "wzlinux"}] }

    如果 hosts 字段不為空則需要指定授權(quán)使用該證書的 IP 或域名列表,由于該證書后續(xù)kubernetes master 集群使用,所以上面指定kubernetes master 集群的主機 IP 和 kubernetes 服務(wù)的服務(wù) IP(一般是 kube-apiserver 指定的 service-cluster-ip-range 網(wǎng)段的第一個 IP,如 10.96.0.1)。

    生成aggreagtor證書和私鑰
    cfssl gencert -ca=aggregator-ca.pem -ca-key=aggregator-ca-key.pem -config=aggregator-ca-config.json -profile=aggregator aggregator-csr.json | cfssljson -bare aggregator
    分發(fā)證書

    將生成的證書和秘鑰文件(后綴名為.pem)拷貝到 Master 節(jié)點的 /etc/kubernetes/pki 目錄下備用。

    開啟聚合層API

    kube-apiserver 增加以下啟動配置:

    --requestheader-client-ca-file=/etc/kubernetes/pki/aggregator-ca.pem --requestheader-allowed-names=aggregator --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --proxy-client-cert-file=/etc/kubernetes/pki/aggregator.pem --proxy-client-key-file=/etc/kubernetes/pki/aggregator-key.pem

    前面創(chuàng)建的證書的 CN 字段的值必須和參數(shù) –requestheader-allowed-names 指定的值 aggregator 相同

    重啟 kube-apiserver:

    $ systemctl daemon-reload $ systemctl restart kube-apiserver

    如果 kube-proxy 沒有在 Master 上面運行,kube-proxy 還需要添加配置:

    --enable-aggregator-routing=true

    2.5 實現(xiàn)自定義API(聚合)服務(wù)

    API聚合這個方式實現(xiàn)相對復(fù)雜一點,但靈活度很高,基本業(yè)務(wù)上的大部分需求都可以滿足。

    2.5.1 工具介紹

    雖然官方給了一個sample-apiserver,我們可以照著實現(xiàn)自己的Aggregated APIServer。但完全手工編寫還是太費勁了,這里使用官方推薦的工具apiserver-builder幫助快速創(chuàng)建項目骨架。

    apiserver-builder構(gòu)建AA方案的API接口服務(wù)的原理還是比較清晰的,總之就是kubernetes里最常見的控制器模式,這里就不具體介紹了,官方文檔既有文字又有圖片講得還是挺細致的,強烈推薦大家多看看,學(xué)習(xí)一下。

    apiserver-builder這個工具與kubebuilder和operator-sdk非常相似,他們都依賴一個底層庫controller-gen,apiserver-builder該工具生成的工程與kubebuilder生成的工程也非常相似,其中一個不同的地方就是kubebuilder因為不需要自定義apiserver,因此,apiserver-builder生成的工程會有一個自定義apiserver,控制器部分的邏輯兩者都一樣,都是通過調(diào)協(xié)方法實現(xiàn)自定義資源狀態(tài)的維護。

    2.5.2 API聚合服務(wù)開發(fā)

    用例源碼下載

    git clone https://github.com/kubernetes/sample-apiserver.git

    源碼編譯

    cd sample-apiserver 編譯二進制文件: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o artifacts/simple-image/kube-sample-apiserver 編譯docker鏡像: docker build -t kube-sample-apiserver:latest ./artifacts/simple-image

    集群中部署

    踩坑:
    artifacts/example/deployment.yaml文件中需要修改鏡像及版本,要與集群中對應(yīng);
    artifacts/example下所有文件中涉及的命名空間統(tǒng)一修改為default,auth-reader.yaml中metadata下的命名空間為kube-system保持不變。

    kubectl apply -f artifacts/example

    訪問測試

    查看sercet
    kubectl get sa apiserver -o json
    查看sercet里面的tocken,并將該tocken拷貝到test.log的文件中后續(xù)訪問是作為參數(shù)傳入
    kubectl describe secret apiserver
    查看角色
    kubectl get clusterrole
    查看角色綁定
    kubectl get clusterrolebinding
    REST接口訪問
    curl -k -H "Authorization: Bearer $(cat test.log)" https://10.131.180.168:6443/apis/wardle.example.com/v1alpha1/flunders

    2.5.3 apiserver-builder實現(xiàn)API聚合服務(wù)開發(fā)

    工具使用當(dāng)前最新版本V2.0beta

    初始化項目

    apiserver-boot init repo --domain example.com

    創(chuàng)建一個非命名空間范圍的api-resource

    apiserver-boot create group version resource --group demo --version v1beta1 --non-namespaced=true --kind Foo

    創(chuàng)建Foo這個api-resource的子資源

    apiserver-boot create subresource --subresource bar --group demo --version v1beta1 --kind Foo

    生成上述創(chuàng)建的api-resource類型的相關(guān)代碼,包括deepcopy接口實現(xiàn)代碼、versioned/unversioned類型轉(zhuǎn)換代碼、api-resource類型注冊代碼、api-resource類型的Controller代碼、api-resource類型的AdmissionController代碼

    解決工程依賴問題并生成腳手架代碼

    go mod init go mod vendor make generate

    可以直接在本地將etcd, apiserver, controller運行起來

    apiserver-boot run local

    上述這樣操作之后,就可以訪問我們的APIServer了,如下面的命令:

    curl -k https://127.0.0.1:9443/apis/demo.example.com/v1beta1/foos

    當(dāng)然也可以新建一個yaml文件,然后用kubectl命令直接對api-resource進行操作:
    創(chuàng)建Foo資源的yaml

    echo 'apiVersion: demo.example.com/v1beta1 kind: Foo metadata:name: foo-examplenamespace: test spec: {}' > sample/foo.yaml

    如果在apiserver的main方法里補上一些代碼,以開啟swagger-ui,還能更方便地看到這些API接口:

    func main() {version := "v0"server.StartApiServer("/registry/example.com", apis.GetAllApiBuilders(), openapi.GetOpenAPIDefinitions, "Api", version, func(apiServerConfig *apiserver.Config) error {...apiServerConfig.RecommendedConfig.EnableSwaggerUI = trueapiServerConfig.RecommendedConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()return nil}) }

    然后瀏覽器訪問https://127.0.0.1:9443/swagger-ui/就可以在swagger的Web頁面上看到創(chuàng)建出來的所有API接口

    定制API接口服務(wù)

    像上面這樣創(chuàng)建的API接口,接口是都有了,但接口沒有啥意義,一般要根據(jù)實際情況定義api-resource的spec、status等結(jié)構(gòu)體。

    type Foo struct {metav1.TypeMeta `json:",inline"`metav1.ObjectMeta `json:"metadata,omitempty"`Spec FooSpec `json:"spec,omitempty"`Status FooStatus `json:"status,omitempty"` }// FooSpec defines the desired state of Foo type FooSpec struct { }// FooStatus defines the observed state of Foo type FooStatus struct { }

    定制Controller

    默認(rèn)生成的api-resource的Reconcile邏輯如下:

    // Reconcile reads that state of the cluster for a Foo object and makes changes based on the state read // and what is in the Foo.Spec // TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes // a Deployment as an example // +kubebuilder:rbac:groups=demo.jeremyxu2010.me,resources=foos,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=demo.jeremyxu2010.me,resources=foos/status,verbs=get;update;patch func (r *ReconcileFoo) Reconcile(request reconcile.Request) (reconcile.Result, error) {// Fetch the Foo instanceinstance := &demov1beta1.Foo{}err := r.Get(context.TODO(), request.NamespacedName, instance)if err != nil {if errors.IsNotFound(err) {// Object not found, return. Created objects are automatically garbage collected.// For additional cleanup logic use finalizers.return reconcile.Result{}, nil}// Error reading the object - requeue the request.return reconcile.Result{}, err}return reconcile.Result{}, nil }

    可以參考:operator-sdk-samples

    打包部署

    程序?qū)懞煤?#xff0c;通過以下命令即可生成容器鏡像及kubernetes的部署manifest文件:

    生成二進制文件

    apiserver-boot build executables

    生成容器鏡像

    apiserver-boot build container --image demo/foo-apiserver:latest

    生成kubernetes的部署manifest文件,可直接在kubernetes里apply即完成部署

    apiserver-boot build config --name foo-apiserver --namespace default --image demo/foo-apiserver:latest

    觀察生成的kubernetes部署manifest文件config/apiserver.yaml,可以發(fā)現(xiàn)最終會創(chuàng)建一個Deployment,一個Service和一個APIService類型的kubernetes資源,同時APIService的caBundle及apiserver的TLS證書也配置妥當(dāng)了。

    總結(jié)

    以上是生活随笔為你收集整理的Kubernetes API 聚合开发汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。