Kubernetes、集羣聯邦和資源分發

Kubernetes 從比較早的版本就聲稱單機羣可以支持 5,000 節點,而且也沒有計劃在短期內提高單個 Kubernetes 集羣支撐的節點數,如果需要在 Kubernetes 中支持 5,000 以上的節點,更推薦使用集羣聯邦(Federation)的方式。

People frequently ask how far we are going to go in improving Kubernetes scalability. Currently we do not have plans to increase scalability beyond 5000-node clusters (within our SLOs) in the next few releases. If you need clusters larger than 5000 nodes, we recommend to use federation to aggregate multiple Kubernetes clusters.[^1]

[^1]: Scalability updates in Kubernetes 1.6: 5,000 node and 150,000 pod clusters https://kubernetes.io/blog/2017/03/scalability-updates-in-kubernetes-1-6/

對雲服務稍微熟悉一點的讀者朋友應該都知道可用區(Available Zone、AZ)的概念,我們在使用 AWS、谷歌雲等服務提供的實例時,需要選擇實例所在的區域以及可用區。區域(Region)是地理上的概念,以 AWS 爲例,它在北京和寧夏都有數據中心,每個數據中心都有 3 個可用區。

aws-asia-pacific-region-maps

圖 1 - AWS 亞太地區

作爲雲服務廠商提供的 AZ,每個 AZ 可能會包含幾萬、甚至十幾萬節點,想使用單個集羣管理這種規模的節點數量是非常困難的,所以管理多個集羣成爲了該規模下必須面對的問題。

集羣聯邦聽起來是一種非常高端的技術,但是實際上我們可以將它理解成更靈活、易用的多集羣。當我們僅僅提到多個集羣時,這些集羣更像是一些獨立的孤島,彼此之間沒有太多的聯繫,但是聯邦集羣將這些獨立的集羣『打包』成了一個整體,上層的用戶不需要關心集羣這一層級。

multi-cluster-and-federation

圖 2 - 多集羣和集羣聯邦

聯邦集羣引入的新的控制面板需要兩個比較重要的功能:跨集羣的服務發現和跨集羣的調度。其中,跨集羣服務發現打通了多個集羣的網絡,讓請求可以跨越不同集羣的邊界;而跨集羣調度可以保證服務的穩定性以及可用性。

在這篇文章中,我們將以 kubefed 和 karmada 兩個集羣聯邦項目爲例介紹集羣聯邦可能遇到的問題,多個項目的對比也能讓我們清晰地意識到不同設計選擇帶來的影響。

kubefed

kubefed 是個非常老牌的 Kubernetes 集羣聯邦項目,這個項目目前就掛在官方的倉庫下,由官方的多集羣興趣小組開發,到今天已經將近四年了。從這個項目的年頭來看,Kubernetes 集羣聯邦已經是一個非常老的話題了,但是到今天也沒有比較完美的解決方案。

kubefed-architecture

圖 3 - kubefed 架構

所有集羣聯邦的方案都需要我們把資源從管理集羣同步資源到聯邦集羣,傳播(Propagation)是該項目引入的一個術語,它會將宿主集羣中的資源分配到所有的聯邦集羣中。這個機制會需要引入以下三個概念:Templates、Placement 和 Overrides:

kubefed-concepts

圖 4 - Kubefed 概念

其中 Template 中定義了該資源的一些基本信息,以 Deployment 爲例,可能包含部署的容器鏡像、環境變量以及實例數等信息;Placement 決定該資源需要部署在哪些集羣中,如上圖所示,該資源會部署在 Cluster1、Cluster2 兩個集羣中;最後的 Override 是會覆寫原有 Template 中的資源,以滿足當前集羣的一些特定需求,例如實例數、拉鏡像使用的祕鑰等與集羣有關的屬性。

在具體的實現上,kubefed 選擇爲集羣中的所有資源生成對應的聯邦資源,例如 Deployment 和對應的 FederatedDeployment。聯邦資源中的 spec 字段存儲了 Deployment 資源的模板,而 overrides 中定義了資源同步到不同集羣時需要做的變更。

kind: FederatedDeployment
...
spec:
  ...
  overrides:
  # Apply overrides to cluster1
    - clusterName: cluster1
      clusterOverrides:
        # Set the replicas field to 5
        - path: "/spec/replicas"
          value: 5
        # Set the image of the first container
        - path: "/spec/template/spec/containers/0/image"
          value: "nginx:1.17.0-alpine"
        # Ensure the annotation "foo: bar" exists
        - path: "/metadata/annotations"
          op: "add"
          value:
            foo: bar
        # Ensure an annotation with key "foo" does not exist
        - path: "/metadata/annotations/foo"
          op: "remove"
        # Adds an argument `-q` at index 0 of the args list
        # this will obviously shift the existing arguments, if any
        - path: "/spec/template/spec/containers/0/args/0"
          op: "add"
          value: "-q"

聯邦集羣的控制平面會根據上述 FederatedDeployment 爲不同的集羣分別生成對應的 Deployment 並推送到下層管理的聯邦集羣中,這也是集羣聯邦 kubefed 解決的主要問題。

從理論上講,所有的集羣聯邦組件只要實現了定義模板覆寫字段的能力就滿足了全部需求,然而在實際應用上,不同的方案也會提供一些類似語法糖的特性幫助我們更好的在不同集羣之間實現更復雜的資源分發能力。kubefed 提供了 ReplicaSchedulingPreference^2 在不同集羣中實現更加智能的分發策略:

apiVersion: scheduling.kubefed.io/v1alpha1
kind: ReplicaSchedulingPreference
metadata:
  name: test-deployment
  namespace: test-ns
spec:
  targetKind: FederatedDeployment
  totalReplicas: 9
  clusters:
    A:
      minReplicas: 4
      maxReplicas: 6
      weight: 1
    B:
      minReplicas: 4
      maxReplicas: 8
      weight: 2

上述調度的策略可以實現工作負載在不同集羣之間的權重,在集羣資源不足甚至出現問題時將實例遷移到其他集羣,這樣既能夠提高服務部署的靈活性和可用性,基礎架構工程師也可以更好地平衡多個集羣的負載。

kubefed 在較早的版本中還是包含跨集羣的服務發現功能的,但是在最新的分支中已經將與服務發現的相關功能都移除了,這可能也是因爲跨集羣的服務發現功能非常複雜,社區中目前已經有很多第三方的工具可以提供基於 DNS 的聯邦 Ingress 資源,不需要 kubefed 的支持 [^3]。

[^3]: kubefed: remove FederatedIngress feature #1284 https://github.com/kubernetes-sigs/kubefed/issues/1284

karmada

Kubefed 雖然是 Kubernetes 社區早期的集羣聯邦項目,雖然已經經歷了比較長的時間,但是一直都處於實驗階段,到今天項目基本也陷入停止維護的狀態。Karmada 是對 Kubefed 項目的延續,它繼承了來自 kubefed 中的一些概念,目前也處於積極開發和維護中,這也是目前社區中比較活躍和成熟的集羣聯邦項目。

Notice: this project is developed in continuation of Kubernetes Federation v1 and v2. Some basic concepts are inherited from these two versions.^4

karmada-architecture

圖 5 - Karmada 架構

Karmada 這種多級的集羣管理都是天然的樹形結構,如上圖所示,根節點上的 Karmada 控制面板主要包含三個主要組件,API Server、Controller Manager 和 Scheduler,相信瞭解 Kubernetes 控制面板的讀者應該都可以想象到這三個不同組件的作用。

需要注意的是 Karmada 的 Controller Manager 中不包含 Kubernetes Controller Manager 中控制器,如果我們在 Karmada 集羣中創建了 Deployment 資源,Karmada 的控制面也不會根據 Deployment 創建 Pod,它只負責 Karmada 原生 CRD 的同步和管理工作,包括 Cluster、PropagationPolicy、ResourceBinding 和 Work 資源。

karmada-resource-relation

圖 5 - Karmada 概念

正如我們在上面提到的,Karmada 中的概念也幾乎全盤繼承自 Kubefed,我們可以根據上圖總結出 Karmada 將資源模板轉換成成員集羣的資源需要經過以下幾個步驟:

  1. Deployment、Service、ConfigMap 等資源模板經過 PropagationPolicy 生成一組 ResourceBinding,每個 ResourceBinding 都對應特定的成員集羣;

  2. ResourceBinding 根據 OverridePolicy 改變一些資源以適應的不同成員集羣,例如:集羣名等參數,這些資源定義會存儲在 Work 對象中;

  3. Work 對象中存儲的資源定義會被提交到成員集羣中,成員集羣中的 Controller Manager 等控制面板組件會負責這些資源的處理,例如:根據 Deployment 創建 Pod 等。

# propagationpolicy.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: example-policy
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: nginx
  placement:
    clusterAffinity:
      clusterNames:
        - member1

# overridepolicy.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
  name: example-override
  namespace: default
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: nginx
  overrideRules:
    - targetCluster:
        clusterNames:
          - member1
      overriders:
        plaintext:
          - path: "/metadata/annotations"
            operator: add
            value:
              foo: bar

Karmada 與 Kubefed 的概念是非常相似的,它們都需要在 API 中解決兩個問題:資源模板應該部署到哪個集羣中、資源模板在該集羣中需要實現做哪些特定變更。上述兩個問題是『模板實例化』的過程中一定會面對的,而 Kubefed 和 Karmada 兩個組件也選擇了不同的接口:

kubefed-karmada-api

圖 6 - Kubefed 和 Karmada API 對比

上述兩種方案各有優缺點,創建對應資源可以將全部的定義整合在一起,但是如果在集羣中引入了新的自定義資源(Custom Resource)需要引入額外的工作;創建獨立的 PropagationPolicy 和 OverridePolicy 可以簡化引入新資源需要的步驟,但是可能需要額外的看板才能看到資源模板最終生成的資源。

在該場景下,作者更傾向於 Karmada 的做法,因爲隨着 Kubernetes 中資源種類的增加,使用 PropagationPolicy 和 OverridePolicy 能夠降低集羣的維護成本,我們不需要爲每一個新的資源創建聯邦類型的映射,而引入額外的看板展示最終生成的資源也不是不能接受的成本。

需要注意的是,我們在 Karmada 控制面只會分發 Deployment 等『高級』資源,管理集羣中的 Controller Manager 不會根據 Deployment 創建 Pod,這樣才能減輕控制面的壓力,實現多集羣聯邦的管理。

除了資源的分發之外,Karmada 還在 PropagationPolicy 加入 replicaScheduling 字段管理工作負載實例的分發,能夠提供故障轉移和按照權重分發的功能:

# duplicated.yaml
replicaScheduling:
  replicaSchedulingType: Duplicated

# divided.yaml
replicaScheduling:
  replicaDivisionPreference: Weighted
  replicaSchedulingType: Divided
  weightPreference:
    staticWeightList:
      - targetCluster:
          clusterNames:
            - member1
        weight: 1
      - targetCluster:
          clusterNames:
            - member2
        weight: 2

karmada-deployment-failover

圖 7 - Karmada 故障轉移

Karmada 的調度器需要解決工作負載調度到哪裏調度多少的問題,與更細粒度的 Kubernetes 的調度器相比,這是一種粒度較粗的調度,因爲上下文的不足,它不需要也沒有辦法保證調度的全局最優解,而提供跨集羣的部署和故障轉移就已經可以滿足常見的需求了。

總結

集羣的聯邦主要解決的還是兩個問題,單集羣的擴展性、跨可用區(地域、雲)的集羣管理。在一個單個集羣可以做到 100k 節點的系統中,我們幾乎聽不到聯邦這一概念,如果 Kubernetes 集羣的控制面足夠強大、能夠承擔足夠多的壓力,那麼多集羣和集羣聯邦的概念在社區中也不會特別熱門。隨着 Kubernetes 項目逐漸走入成熟,Pipeline、聯邦和集羣管理這些細分領域也都會逐漸完善,相信我們會逐漸在社區中看到成熟的聯邦集羣解決方案。

推薦閱讀

真沒什麼邏輯 系統設計、微服務架構和雲原生技術

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2im1eklspwFdeYjH3sUGAw