管理數千個集群:Gardener項目更新

  • 2019 年 12 月 10 日
  • 筆記

作者:Rafael Franzke(SAP),Vasu Chandrasekhara(SAP)

去年,我們在Kubernetes社區會議和Kubernetes博客上介紹了Gardener。在SAP,我們已經運行了兩年多的Gardener,並且成功地管理了所有大型雲供應商上不同版本的數千個通過一致性的集群,以及大量的基礎架構和私有雲,這些私有雲通常是通過收購來加入企業的。

https://kubernetes.io/blog/2018/05/17/gardener/

https://gardener.cloud/

https://k8s-testgrid.appspot.com/conformance-gardener

我們經常被問到,為什麼少量的動態可伸縮集群是不夠的。我們也以類似的心態,開始了我們的Kubernetes之旅。但是我們意識到,將Kubernetes的架構和原則應用到生產場景中,我們的內部和外部客戶很快就需要合理地分離關注點和所有權,這在大多數情況下導致使用多個集群。因此,作為服務解決方案的可伸縮和受管理的Kubernetes通常是採用的基礎。特別是,當一個較大的組織,在不同的供應商和不同的地區運行多個產品時,集群的數量將很快增加到數百甚至數千。

今天,我們想對過去一年,在可擴展性和可定製性方面的實現,以及下一個里程碑的工作計划進行更新。

簡要回顧:Gardener是什麼?

Gardener的主要原則,是利用Kubernetes的基本功能做所有操作,通常稱為先啟(inception)或kubeception。來自社區的反饋是,我們最初的架構圖看起來「overwhelming」,但在對材料進行了一些深入研究之後,我們所做的一切都是「Kubernetes方式」。可以重用所有關於api、調節循環等的知識。

https://github.com/gardener/documentation/wiki/Architecture

其基本思想是使用所謂的種子(seed)集群來承載最終用戶(end-user)集群(植物學上稱為shoot)的控制平面。Gardener利用上游k8s.gcr.io/*的鏡像作為開放的發行版,將vanilla Kubernetes集群作為獨立於底層基礎設施供應商的服務。該項目完全構建在Kubernetes擴展概念之上,並因此添加了一個自定義API服務器、一個控制器-管理器(controller-manager)和一個調度器來創建和管理Kubernetes集群的生命周期。它使用自定義資源擴展了Kubernetes API,最突出的是Gardener集群規範(Shoot資源),可以使用它以聲明式的方式「命令」Kubernetes集群(針對day-1,也可以協調day-2的所有管理活動)。

通過利用Kubernetes作為基礎設施,我們能夠設計一個組合水平和垂直Pod自動伸縮器(HVPA),當配置自定義啟發式時,自動縮放所有控制平面組件上/下或大/小。這使得快速擴展成為可能,甚至超出了某些固定數量的主節點的能力。與許多其他Kubernetes集群配置工具相比,此架構特性是主要的區別之一。但在我們的生產中,Gardener不僅通過容器控制平面有效地降低了總的擁有成本。它還簡化了「第2天操作」(如集群更新或健壯性質量)的實現。再一次,本質上依靠所有成熟的Kubernetes功能和能力。

https://github.com/gardener/hvpa-controller

新引入的Gardener擴展概念,現在允許提供者只維護其特定的擴展,而無需在核心源樹中進行開發。

可擴展性

由於Kubernetes代碼庫在過去幾年的增長,它包含了大量特定於提供程序的代碼,這些代碼現在正從其核心源代碼樹外部化。同樣的事情也發生在了Gardener項目上:隨着時間的推移,雲提供商、操作系統、網絡插件等方面的細節已經積累了很多。通常,當涉及到可維護性、可測試性或新版本時,這將導致大量工作的增加。我們的社區成員Packet為他們的樹內基礎設施提供了Gardener支持,並遭受了上述缺點。

因此,與Kubernetes社區決定將他們的雲-控制器-管理器移出樹,或者將卷插件移到CSI等類似,Gardener社區也提出並實現了類似的擴展概念。Gardener核心源代碼樹現在沒有任何提供者細節,允許供應商只關注他們的基礎設施細節,並使核心貢獻者再次變得更加敏捷。

通常,設置集群需要一系列相互依賴的步驟,從證書的生成和基礎設施的準備開始,接着是控制平面和工作節點的提供,最後是系統組件的部署。我們在這裡要強調的是,所有這些步驟都是必要的(cf. Kubernetes the Hard Way),所有Kubernetes集群創建工具都以差不多這樣或那樣的方式實現了相同的步驟(在某種程度上是自動化的)。

https://github.com/kelseyhightower/kubernetes-the-hard-way

Gardener的可擴展性概念的總體思想,是使這個流更加通用,並為每個步驟開闢定製的資源,這些資源可以作為理想的擴展點。

https://github.com/gardener/gardener/blob/0.31.1/pkg/controllermanager/controller/shoot/shoot_control_reconcile.go#L69-L298

圖1:具有擴展點的集群協調流。

使用Gardener的流框架,我們隱式地為集群的所有基礎設施和所有可能的狀態創建了一個可複製的狀態機。

Gardener擴展性方法定義了自定義資源,作為以下類別的理想擴展點:

  • DNS提供商(例如,Route53, CloudDNS等)
  • Blob存儲提供商(如S3、GCS、ABS等),
  • 基礎設施提供商(如AWS、GCP、Azure等)、
  • 操作系統(例如:CoreOS Container Linux, Ubuntu, FlatCar Linux等)
  • 網絡插件(如Calico、Flannel、Cilium等)、
  • 非必要的擴展(例如,Let』s Encrypt證書服務)。

擴展點

除了利用自定義資源定義之外,我們還有效地使用了種子集群中的變異/驗證(mutating/validating)webhook。擴展控制器本身在這些集群中運行,並對它們負責的CRD和工作負載資源(如Deployment、StatefulSet等)做出反應。與Cluster API的方法類似,這些CRD也可能包含特定於提供者的信息。

https://cluster-api.sigs.k8s.io/

圖1步驟2-10涉及特定於基礎設施的元數據,涉及特定於基礎設施的實現,例如,對於DNS記錄,可能有aws-route53、google-clouddns,或者對於隔離網絡,甚至是openstack-designate等等。我們將在下一段中以步驟4和步驟6作為一般概念的示例(基於AWS的實現)。如果您感興趣,可以閱讀我們的可擴展性文檔中的完整文檔化的API契約。

https://github.com/gardener/gardener/tree/master/docs/extensions

例子:Infrastructure CRD

Kubernetes在AWS上的集群在使用之前需要一定的基礎設施準備。這包括,例如,創建VPC、子網等。Infrastructure CRD的目的是觸發這種準備:

apiVersion: extensions.gardener.cloud/v1alpha1  kind: Infrastructure  metadata:    name: infrastructure    namespace: shoot--foobar--aws  spec:    type: aws    region: eu-west-1    secretRef:      name: cloudprovider      namespace: shoot--foobar—aws    sshPublicKey: c3NoLXJzYSBBQUFBQ...    providerConfig:      apiVersion: aws.provider.extensions.gardener.cloud/v1alpha1      kind: InfrastructureConfig      networks:        vpc:          cidr: 10.250.0.0/16        zones:        - name: eu-west-1a          internal: 10.250.112.0/22          public: 10.250.96.0/22          workers: 10.250.0.0/19

基於Shoot資源,Gardener創建了這個Infrastructure資源,作為其調節流的一部分。特定於AWS的providerConfig是最終用戶在Shoot資源中的配置的一部分,Gardener不進行評估,而是傳遞給種子集群中的擴展控制器。

在當前的實現中,AWS擴展在eu-west-1a區域創建了一個新的VPC和三個子網。此外,它還創建了NAT和internet網關、彈性IP、路由表、安全組、IAM角色、實例配置文件和EC2密鑰對。

完成任務後,它將報告狀態和一些特定於提供程序的輸出:

apiVersion: extensions.gardener.cloud/v1alpha1  kind: Infrastructure  metadata:    name: infrastructure    namespace: shoot--foobar--aws  spec: ...  status:    lastOperation:      type: Reconcile      state: Succeeded    providerStatus:      apiVersion: aws.provider.extensions.gardener.cloud/v1alpha1      kind: InfrastructureStatus      ec2:        keyName: shoot--foobar--aws-ssh-publickey      iam:        instanceProfiles:        - name: shoot--foobar--aws-nodes          purpose: nodes        roles:        - arn: "arn:aws:iam::<accountID>:role/shoot..."          purpose: nodes      vpc:        id: vpc-0815        securityGroups:        - id: sg-0246          purpose: nodes        subnets:        - id: subnet-1234          purpose: nodes          zone: eu-west-1b        - id: subnet-5678          purpose: public          zone: eu-west-1b

providerStatus中的信息可用於後續步驟,例如配置雲控制器-管理器或檢測機器控制器-管理器。

例子:集群控制平面的部署

Gardener的主要特性之一是跨不同基礎設施管理的集群具有同質性。因此,它仍然負責將獨立於提供商的控制平面組件部署到seed集群(如etcd、kube-apiserver)。特定於提供商的控制平面組件(如雲控制器-管理器或CSI控制器)的部署由專ControlPlane CRD觸發。但是,在這一段中,我們將重點討論標準組件的定製。

讓我們同時關注kube-apiserver和kube-controller-manager部署。我們的AWS擴展還沒有使用CSI,而是依賴於樹內EBS卷插件。因此,它需要啟用PersistentVolumeLabel許可插件,並向kube-apiserver提供雲提供商配置。類似地,kube-controller-manager將被指示使用其樹內卷插件。

kube-apiserver Deployment包含kube-apiserver容器,由Gardener部署,像這樣:

containers:  - command:    - /hyperkube    - apiserver    - --enable-admission-plugins=Priority,...,NamespaceLifecycle    - --allow-privileged=true    - --anonymous-auth=false    ...

AWS擴展使用一個可變的webhookconfiguration注入上述的標誌並修改規範如下:

containers:  - command:    - /hyperkube    - apiserver    - --enable-admission-plugins=Priority,...,NamespaceLifecycle,PersistentVolumeLabel    - --allow-privileged=true    - --anonymous-auth=false    ...    - --cloud-provider=aws    - --cloud-config=/etc/kubernetes/cloudprovider/cloudprovider.conf    - --endpoint-reconciler-type=none    ...    volumeMounts:    - mountPath: /etc/kubernetes/cloudprovider      name: cloud-provider-config  volumes:  - configMap:      defaultMode: 420      name: cloud-provider-config    name: cloud-provider-config

kube-controller-manager Deployment的處理方式與此類似。

種子集群中的Webhook可以用來突變任何與由Gardener或任何其他擴展部署的shoot集群控制平面相關的東西。對於shoot集群中的資源,也有類似的webhook概念,以防擴展控制器需要自定義由Gardener部署的系統組件。

註冊擴展控制器

Gardener API使用兩個特殊的資源來註冊和安裝擴展。註冊本身是通過ControllerRegistration資源聲明的。最簡單的選擇是定義Helm chart和一些值來生成chart,但是,任何其他部署機制也可以通過自定義代碼來支持。

Gardener確定在特定的種子集群中是否需要擴展控制器,並創建用於觸發部署的ControllerInstallation。

到目前為止,每個註冊的擴展控制器都被部署到每個種子集群,這在一般情況下是不必要的。將來,Gardener將變得更有選擇性,只在特定的種子簇上部署所需的擴展。

我們的動態註冊方法,允許在運行的系統中添加或刪除擴展,而不需要重新構建或重新啟動任何組件。

圖2:帶有擴展控制器的Gardener架構。

現狀

我們最近引進了新的core.gardener.cloud API組,充分整合了向前和向後兼容的Shoot資源,並允許提供商使用Gardener而無需修改其核心源代碼樹中的任何內容。

我們已經調整了所有的控制器來使用這個新的API組,並且已經棄用了舊的API。最終,幾個月後我們將刪除它,因此建議最終用戶儘快開始遷移到新的API。

除此之外,我們還啟用了所有相關的擴展來提供Shoot健康狀態,並執行了相應的合同。基本思想是CRD可能有.status.conditions,由Gardener獲取並與標準健康檢查合併到Shoot狀態字段中。

此外,我們還希望實現一些易於使用的庫函數,以便為CRD實現默認和驗證webhook,從而驗證最終用戶控制的providerConfig字段。

最後,我們將把gardener/gardener-extensions倉庫分割成單獨的倉庫,並將其僅用於通用庫函數,可用於編寫擴展控制器。

下一步

Kubernetes將許多基礎設施管理方面的挑戰具體化了。先啟的設計通過將生命周期操作委託給一個單獨的管理平面(種子集群)來解決大部分問題。但是,如果garden或種子集群倒下了怎麼辦?我們如何擴展數以萬計的需要並行協調的託管集群?我們正在進一步投入加強Gardener的可伸縮性和災難恢復功能。讓我們更詳細地簡要介紹其中的三個特性:

Gardenlet

從Gardener項目一開始,我們就開始實現操作器模式:我們有一個自定義控制器-管理器,它對我們自己的自定義資源起作用。現在,當您開始考慮Gardener架構時,您會發現與Kubernetes架構有一些有趣的相似之處:Shoot集群可以與Pod進行比較,而種子集群可以視為工作節點。在這個觀察的指導下,我們引入了gardener-scheduler。它的主要任務是找到一個合適的種子集群來承載新命令集群的控制平面,類似於kube-scheduler為新創建的pod找到合適的節點。通過為一個區域(或提供商)提供多個種子集群並分配工作負載,我們還減少了潛在異常的爆炸半徑。

圖3:Kubernetes和Gardener架構之間的相似性。

但是,Kubernetes和Gardener架構之間仍然有一個顯著的區別:Kubernetes在每個節點上運行一個主「代理」,即kubelet,它主要負責管理特定節點上的pod和容器。Gardener使用它的控制器-管理器,它負責所有種子集群上的所有Shoot集群,並從garden集群中央執行調節循環。

雖然這適用於今天的成千上萬的集群規模,我們的目標是使後真正的可伸縮性Kubernetes原則(超出了單個controller-manager)的能力:我們現在正在分發邏輯(或Gardener操作器)到種子集群,將引入相應的組件,以gardenlet命名。它將是Gardener在每個種子集群的主要「代理人」,並將只負責的芽集群位於其特定的種子集群。

Gardener-控制-管理器將仍然保持對GardenerAPI的其他資源的調節循環,但是,它將不再與種子/shoot集群對話。

反轉控制流甚至可以在防火牆後放置種子/shoot集群,而不再需要直接訪問(通過VPN隧道)。

圖4:帶有Gardenlet的詳細架構。

種子集群間的控制平面遷移

當種子集群失敗時,用戶的靜態工作負載將繼續運行。但是,管理集群將不再可能,因為在失敗的種子中運行的shoot集群的API服務器將不再可用。

我們已經實施了將被一些種子災難擊中的失敗的控制平面轉移到另一個種子上,現在正在努力使這種獨特的能力完全自動化。實際上,這種方法不僅可行,而且在我們的生產中已經多次執行了故障轉移過程。

自動故障轉移功能將使我們能夠實現更全面的災難恢復和可伸縮性質量,例如,種子集群的自動供應和重新平衡,或者在所有不可預見的情況下實現自動遷移。再一次,想想與Kubernetes在pod eviction和node drain方面的相似之處。

Gardener環

Gardener環(Ring)是我們提供和管理Kubernetes集群的新方法,它不需要為初始集群依賴外部供應工具。通過遞歸地使用Kubernetes,我們可以通過避免命令式的工具集來大幅降低管理複雜性,同時通過自穩定的循環系統來創建新的質量。

從概念上講,Ring(環)方法不同於自託管和基於靜態pod的部署。這個想法是創建一個由三個(或更多)shoot集群組成的環,每個集群都有它的後繼者的控制平面。

一個集群的中斷不會影響環的穩定性和可用性,當控制平面外部化時,失敗的集群可以通過Gardener的自修復功能自動恢復。只要有至少n/2+1個可用集群的仲裁,該環將始終保持自身穩定。在不同的雲提供商(或至少在不同的地區/數據中心)上運行這些集群可以減少仲裁損失的可能性。

圖5:Kubernetes集群的自穩定環。

Gardener的分佈式實例共享相同數據的方式是,部署單獨的kube-apiserver實例與相同的etcd集群通信。這些kube-apiserver正在形成一個無節點的Kubernetes集群,可以用作Gardener及其相關應用程序的「數據容器」。

我們正在運行受環內部保護的測試環境,它使我們免於人工干預。通過自動控制平面遷移,我們可以輕鬆地引導Ring,並解決「初始群集問題」,並提高整體穩健性。

來開始!

如果你有興趣寫一個擴展,你可能想看看以下資源:

  • GEP-1: Extensibility proposal document
  • GEP-4: New core.gardener.cloud/v1alpha1 API
  • Example extension controller implementation for AWS
  • Gardener Extensions Golang library
  • Extension contract documentation
  • Gardener API Reference https://github.com/gardener/gardener/blob/master/docs/proposals/01-extensibility.md https://github.com/gardener/gardener/blob/master/docs/proposals/04-new-core-gardener-cloud-apis.md https://github.com/gardener/gardener-extensions/tree/master/controllers/provider-aws https://godoc.org/github.com/gardener/gardener-extensions/pkg https://github.com/gardener/gardener/tree/master/docs/extensions https://gardener.cloud/api-reference/

當然,任何其他貢獻,我們的項目是非常歡迎的!我們一直在尋找新的社區成員。

如果你想嘗試Gardener,請查看我們的快速安裝指南。該安裝程序將設置一個完整的Gardener環境,只需幾分鐘準備好用於測試和評估。

https://gardener.cloud/installer/

歡迎貢獻!

Gardener項目是作為開放源代碼開發的,並託管在GitHub上:https://github.com/gardener

如果你看到Gardener項目的潛力,請通過GitHub加入我們。

我們每周在中歐時間每周五上午10-11點召開一次社區會議,並在Kubernetes的工作區內設一條公共的#gardener Slack頻道。同時,我們計劃在2020年第一季度舉辦Gardener黑客松,期待與您的見面!

https://docs.google.com/document/d/1314v8ziVNQPjdBrWp-Y4BYrTDlv7dq2cWDFIa9SMaP4

https://kubernetes.slack.com/messages/gardener

https://docs.google.com/document/d/1EQ_kt70gwybiL7FY8F7Dx–GtiNwdv0oRDwqQqAIYMk/edit#heading=h.a43vkkp847f1