本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Gardener 專案更新
去年,我們在 Kubernetes 社群會議上和 Kubernetes 部落格上的一篇文章中介紹了 Gardener。在 SAP,我們執行 Gardener 已經兩年多了,併成功地在所有主要的超大規模提供商以及透過收購併入企業的眾多基礎設施和私有云上,管理著數千個符合標準的各種版本的叢集。
我們經常被問到,為什麼少數幾個可動態擴充套件的叢集就不能滿足需求。我們最初進入 Kubernetes 領域時也抱有類似的想法。但我們意識到,在生產場景中應用 Kubernetes 的架構和原則時,我們的內部和外部客戶很快就需要合理地分離關注點和所有權,這在大多數情況下導致了使用多個叢集。因此,可擴充套件和託管的 Kubernetes 即服務解決方案也常常是採用的基礎。特別是當一個大型組織在不同的提供商和不同的區域執行多個產品時,叢集數量很快就會上升到數百甚至數千個。
今天,我們想就過去一年中我們在可擴充套件性和可定製性方面所做的工作,以及我們為下一個里程碑計劃開展的工作提供一個更新。
簡要回顧:Gardener 是什麼?
Gardener 的主要原則是利用 Kubernetes 原語進行所有操作,通常被稱為內嵌或 kubeception。社群的反饋是,最初我們的架構圖看起來“令人不知所措”,但在稍加研究後,我們所做的一切都是“Kubernetes 方式”。人們可以重用所有關於 API、控制迴圈等方面的知識。
核心思想是使用所謂的**種子(seed)**叢集來託管終端使用者叢集(植物學上稱為**芽(shoots)**)的控制平面。
Gardener 以同構方式提供獨立於底層基礎設施提供商的純粹 Kubernetes 叢集即服務,利用上游提供的 `k8s.gcr.io/*` 映象作為開放分發(更新:`k8s.gcr.io` 已被棄用,轉而使用 `registry.k8s.io`)。該專案完全建立在 Kubernetes 擴充套件概念之上,因此添加了自定義 API 伺服器、控制器管理器和排程器來建立和管理 Kubernetes 叢集的生命週期。它透過自定義資源擴充套件了 Kubernetes API,最突出的是 Gardener 叢集規範(`Shoot` 資源),可以以宣告方式“訂購”Kubernetes 叢集(用於第一天,但也協調所有第二天管理活動)。
透過將 Kubernetes 作為基礎架構,我們能夠設計出一種組合的水平和垂直 Pod 自動伸縮器(HVPA),當配置了自定義啟發式演算法時,它可以自動地向上/向下或向外/向內伸縮所有控制平面元件。這使得快速伸縮成為可能,甚至超出了通常固定數量的主節點的容量。這一架構特性是與其他許多 Kubernetes 叢集供應工具的主要區別之一。但在我們的生產中,Gardener 不僅透過打包控制平面有效地降低了總擁有成本。它還簡化了“第二天操作”(如叢集更新或魯棒性質量)的實現。再次強調,這主要依賴於所有成熟的 Kubernetes 功能和能力。
新引入的 Gardener 擴充套件概念現在使提供商只需維護其特定的擴充套件,而無需在核心原始碼樹內進行開發。
可擴充套件性
由於 Kubernetes 在過去幾年的發展,其程式碼庫中包含了大量的特定於提供商的程式碼,這些程式碼現在正從其核心原始碼樹中外部化。Gardener 專案也發生了同樣的情況:隨著時間的推移,積累了許多針對雲提供商、作業系統、網路外掛等的具體內容。通常,這會導致在可維護性、可測試性或新版本釋出方面的工作量顯著增加。我們的社群成員 Packet 在樹內為其基礎設施貢獻了 Gardener 支援,並遭遇了上述缺點。
因此,與 Kubernetes 社群決定將他們的雲控制器管理器移出樹外,或者將卷外掛移到 CSI 等方式類似,Gardener 社群也提議並實現了類似的擴充套件概念。Gardener 核心原始碼樹現在沒有任何供應商特有的內容,這使得供應商可以專注於其基礎設施特有的內容,並使核心貢獻者再次變得更加敏捷。
通常,設定叢集需要一系列相互依賴的步驟,從證書生成和基礎設施準備開始,接著是控制平面和工作節點的供應,最後是系統元件的部署。我們想在此強調,所有這些步驟都是必要的(參見Kubernetes the Hard Way),所有 Kubernetes 叢集建立工具都以某種方式實現了相同的步驟(在一定程度上是自動化的)。
Gardener 可擴充套件性概念的總體思想是使此流程更通用,併為每個步驟刻畫自定義資源,這些資源可以作為理想的擴充套件點。

圖 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 證書服務)。
擴充套件點
除了利用自定義資源定義之外,我們還在種子叢集中有效地使用變異/驗證 Webhook。擴充套件控制器本身在這些叢集中執行,並對其負責的 CRD 和工作負載資源(如 `Deployment`、`StatefulSet` 等)作出反應。與 叢集 API 的方法類似,這些 CRD 也可以包含提供商特定的資訊。
步驟 2 - 10 [參見圖 1] 涉及基礎設施特定的元資料,指代基礎設施特定的實現,例如,對於 DNS 記錄,可能有 `aws-route53`、`google-clouddns`,或者對於隔離網路,甚至 `openstack-designate` 等等。我們將在接下來的段落中,以步驟 4 和 6 為例,基於 AWS 的實現,考察通用概念。如果您有興趣,可以在我們的可擴充套件性文件中閱讀完整記錄的 API 契約。
示例:`Infrastructure` CRD
AWS 上的 Kubernetes 叢集在使用前需要進行特定的基礎設施準備。這包括,例如,建立 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 和一個網際網路閘道器、彈性 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 的主要特性之一是它所管理的叢集在不同基礎設施之間具有同構性。因此,它仍然負責將與提供商無關的控制平面元件(如 etcd、kube-apiserver)部署到種子叢集中。提供商特定的控制平面元件(如雲控制器管理器或 CSI 控制器)的部署由專門的 `ControlPlane` CRD 觸發。然而,在本段中,我們想重點討論標準組件的定製化。
讓我們關注 kube-apiserver 和 kube-controller-manager 的 `Deployment`。我們針對 Gardener 的 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
...
透過使用 `MutatingWebhookConfiguration`,AWS 擴充套件注入了上述標誌並修改了規範,如下所示:
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 已經將許多基礎設施管理挑戰外部化。內嵌設計透過將生命週期操作委託給獨立的管理平面(種子叢集)來解決其中大部分問題。但是,如果園丁叢集或種子叢集宕機怎麼辦?我們如何擴充套件到需要並行協調的數萬個託管叢集?我們正在進一步投入,以增強 Gardener 的可擴充套件性和災難恢復功能。讓我們簡要介紹其中三個功能:
Gardenlet
從 Gardener 專案一開始,我們就開始實施operator 模式:我們有一個自定義控制器管理器,它作用於我們自己的自定義資源。現在,當你開始思考 Gardener 架構時,你會發現與 Kubernetes 架構有一些有趣的相似之處:Shoot 叢集可以與 Pod 相比,而 Seed 叢集可以看作是工作節點。基於這一觀察,我們引入了 **gardener-scheduler**。它的主要任務是為新訂購的叢集尋找合適的 Seed 叢集來託管控制平面,類似於 kube-scheduler 為新建立的 Pod 尋找合適的節點。透過為一個區域(或提供商)提供多個 Seed 叢集並分配工作負載,我們也降低了潛在故障的影響範圍。

圖 3 Kubernetes 與 Gardener 架構的相似之處。
然而,Kubernetes 和 Gardener 架構之間仍然存在顯著差異:Kubernetes 在每個節點上執行一個主要的“代理”,即 kubelet,它主要負責管理其特定節點上的 Pod 和容器。Gardener 使用其控制器管理器,該管理器負責所有種子叢集上的所有 Shoot 叢集,並從花園叢集集中執行其協調迴圈。
雖然這在當前數千個叢集的規模下執行良好,但我們的目標是實現遵循 Kubernetes 原則的真正可擴充套件性(超越單個控制器管理器的容量):我們現在正在努力將邏輯(或 Gardener operator)分發到種子叢集中,並將引入一個相應的元件,恰當地命名為 **gardenlet**。它將是 Gardener 在每個種子叢集上的主要“代理”,並且將只負責位於其特定種子叢集中的 Shoot 叢集。
gardener-controller-manager 仍將保留其對 Gardener API 其他資源的控制迴圈,但是,它將不再與種子/芽叢集通訊。
反轉控制流甚至可以允許將種子/芽叢集放置在防火牆後,而不再需要直接可訪問性(透過 VPN 隧道)。

圖 4 包含 Gardenlet 的詳細架構。
種子叢集之間的控制平面遷移
當種子叢集出現故障時,使用者的靜態工作負載將繼續執行。但是,叢集管理將不再可能,因為執行在故障種子中的 Shoot 叢集的 API 伺服器將無法訪問。
我們已經實現了將因種子災難而導致故障的控制平面重新定位到另一個種子叢集,現在正在努力全面自動化這一獨特功能。事實上,這種方法不僅可行,我們已經在生產中多次執行了故障轉移過程。
自動化故障轉移功能將使我們能夠實現更全面的災難恢復和可伸縮性特性,例如,種子叢集的自動化供應和重新平衡,或所有不可預見情況的自動化遷移。再次思考與 Kubernetes 在 Pod 逐出和節點排水方面的相似之處。
園丁環
Gardener Ring 是我們用於配置和管理 Kubernetes 叢集的新穎方法,它不依賴於外部供應工具來構建初始叢集。透過遞迴地使用 Kubernetes,我們透過避免命令式工具集,可以極大地降低管理複雜性,同時透過自穩定的迴圈系統建立新的特性。
環形方法在概念上不同於自託管和基於靜態 pod 的部署。其思想是建立由三個(或更多)Shoot 叢集組成的環,每個叢集託管其後繼者的控制平面。
一個叢集的宕機不會影響 Ring 的穩定性和可用性,並且由於控制平面是外部化的,故障叢集可以由 Gardener 的自愈能力自動恢復。只要至少有 `n/2+1` 個可用叢集的法定人數,Ring 就會始終自行穩定。在不同的雲提供商(或至少在不同的區域/資料中心)上執行這些叢集,可以減少法定人數損失的可能性。

圖 5 自穩定 Kubernetes 叢集環。
Gardener 的分散式例項共享相同資料的方式是部署獨立的 kube-apiserver 例項,這些例項連線到同一個 etcd 叢集。這些 kube-apiserver 形成了一個無節點的 Kubernetes 叢集,可以作為 Gardener 及其相關應用程式的“資料容器”。
我們內部執行著受 ring 保護的測試環境,它使我們免去了人工干預。隨著自動化控制平面遷移的到位,我們可以輕鬆引導 Ring,並解決“初始叢集問題”,同時提高整體健壯性。
開始吧!
如果您有興趣編寫擴充套件,您可能需要檢視以下資源:
- GEP-1:可擴充套件性提案文件
- GEP-4:新的 `core.gardener.cloud/v1alpha1` API
- AWS 擴充套件控制器實現示例
- Gardener 擴充套件 Golang 庫
- 擴充套件契約文件
- Gardener API 參考
當然,也歡迎對我們專案的任何其他貢獻!我們一直在尋找新的社群成員。
如果您想試用 Gardener,請檢視我們的快速安裝指南。此安裝程式將在幾分鐘內設定一個完整的 Gardener 環境,可用於測試和評估。
歡迎貢獻!
Gardener 專案以開源形式開發,並託管在 GitHub 上:https://github.com/gardener
如果您看到 Gardener 專案的潛力,請透過 GitHub 加入我們。
我們每週五歐洲中部時間上午 10-11 點舉行公開社群會議,並在 Kubernetes 工作區中設有公開的 #gardener Slack 頻道。此外,我們計劃在 2020 年第一季度舉辦Gardener 駭客松,期待與您在那裡見面!