雲控制器管理器的雞和蛋問題

Kubernetes 1.31 完成了 Kubernetes 歷史上最大規模的遷移,移除了樹內雲驅動。雖然元件遷移現已完成,但這給使用者和安裝程式專案(例如 kOps 或 Cluster API)帶來了一些額外的複雜性。我們將探討這些額外的步驟和故障點,併為叢集所有者提出建議。這次遷移很複雜,一些邏輯必須從核心元件中提取出來,從而構建了四個新的子系統。

  1. 雲控制器管理器(Cloud controller manager) (KEP-2392)
  2. API 伺服器網路代理(API server network proxy) (KEP-1281)
  3. kubelet 憑據提供程式外掛(kubelet credential provider plugins) (KEP-2133)
  4. 將儲存遷移到使用 CSI (KEP-625)

雲控制器管理器是控制平面的一部分。它是一個關鍵元件,取代了之前存在於 kube-controller-manager 和 kubelet 中的一些功能。

Components of Kubernetes

Kubernetes 的元件

雲控制器管理器最關鍵的功能之一是節點控制器,它負責節點的初始化。

如下圖所示,當 kubelet 啟動時,它會向 apiserver 註冊 Node 物件,併為節點新增汙點,以便首先由雲控制器管理器處理。初始的 Node 缺少特定於雲驅動的資訊,例如節點地址以及帶有云驅動特定資訊(如節點、區域和例項型別資訊)的標籤。

Chicken and egg problem sequence diagram

“雞生蛋還是蛋生雞”問題時序圖

這個新的初始化過程給節點就緒增加了一些延遲。以前,kubelet 能夠在建立節點的同時初始化節點。由於邏輯已移至雲控制器管理器,對於那些不像控制平面其他元件那樣部署控制器管理器(通常是靜態 Pod、獨立二進位制檔案或帶有容忍汙點的 DaemonSet/Deployment 並使用 `hostNetwork`)的 Kubernetes 架構來說,這可能在叢集引導期間導致“雞生蛋還是蛋生雞”問題(下文將詳細介紹)。

依賴問題的示例

如上所述,在引導期間,雲控制器管理器可能無法排程,從而導致叢集無法正常初始化。以下是一些具體示例,說明該問題如何表現以及可能發生的根本原因。

這些示例假設您使用 Kubernetes 資源(例如 Deployment、DaemonSet 或類似資源)來運行雲控制器管理器以控制其生命週期。因為這些方法依賴 Kubernetes 來排程雲控制器管理器,所以必須小心確保它能夠正確排程。

示例:由於未初始化的汙點,雲控制器管理器無法排程

正如 Kubernetes 文件中所述,當 kubelet 啟動時帶有命令列標誌 `--cloud-provider=external` 時,其對應的 `Node` 物件將被新增一個名為 `node.cloudprovider.kubernetes.io/uninitialized` 的 NoSchedule 汙點。由於雲控制器管理器負責移除這個 NoSchedule 汙點,這可能導致由 Kubernetes 資源(如 `Deployment` 或 `DaemonSet`)管理的雲控制器管理器無法排程的情況。

如果在控制平面初始化期間雲控制器管理器無法被排程,那麼生成的 `Node` 物件都將帶有 `node.cloudprovider.kubernetes.io/uninitialized` 這個 NoSchedule 汙點。這也意味著這個汙點不會被移除,因為雲控制器管理器負責移除它。如果這個 NoSchedule 汙點不被移除,那麼關鍵的工作負載,例如容器網路介面控制器,將無法排程,叢集將處於不健康狀態。

示例:由於未就緒的汙點,雲控制器管理器無法排程

下一個示例可能出現在容器網路介面(CNI)等待雲控制器管理器(CCM)提供 IP 地址資訊,而 CCM 沒有容忍將被 CNI 移除的汙點的情況下。

Kubernetes 文件對 `node.kubernetes.io/not-ready` 汙點的描述如下:

“節點控制器透過監控節點的健康狀況來檢測節點是否就緒,並相應地新增或移除此汙點。”

導致 Node 資源帶有此汙點的一個條件是該節點上的容器網路尚未初始化。由於雲控制器管理器負責向 Node 資源新增 IP 地址,而容器網路控制器需要這些 IP 地址來正確配置容器網路,因此在某些情況下,節點可能會永久卡在未就緒和未初始化的狀態。

這種情況的發生原因與第一個示例類似,但在這種情況下,`node.kubernetes.io/not-ready` 汙點帶有 NoExecute 效果,因此會導致雲控制器管理器無法在帶有該汙點的節點上執行。如果雲控制器管理器無法執行,它將無法初始化節點。這將級聯導致容器網路控制器無法正常執行,節點最終將同時帶有 `node.cloudprovider.kubernetes.io/uninitialized` 和 `node.kubernetes.io/not-ready` 汙點,使叢集處於不健康狀態。

我們的建議

運行雲控制器管理器沒有唯一的“正確方法”。具體細節將取決於叢集管理員和使用者的特定需求。在規劃叢集和雲控制器管理器的生命週期時,請考慮以下指導:

對於在它們所管理的同一個叢集中執行的雲控制器管理器。

  1. 使用主機網路模式,而不是 Pod 網路:在大多數情況下,雲控制器管理器需要與基礎架構關聯的 API 服務端點通訊。將 “hostNetwork” 設定為 true 將確保雲控制器使用主機網路而不是容器網路,因此將具有與主機作業系統相同的網路訪問許可權。它還將消除對網路外掛的依賴。這將確保雲控制器可以訪問基礎架構端點(請務必根據您的基礎架構提供商的說明檢查您的網路配置)。
  2. 使用可擴充套件的資源型別。`Deployments` 和 `DaemonSets` 對於控制雲控制器的生命週期很有用。它們可以輕鬆地執行多個副本以實現冗餘,並利用 Kubernetes 排程來確保在叢集中的正確放置。當使用這些原語來控制雲控制器的生命週期並執行多個副本時,您必須記住啟用領導者選舉,否則您的控制器將相互衝突,可能導致節點在叢集中無法初始化。
  3. 將控制器管理器容器部署到控制平面。可能存在其他需要執行在控制平面之外的控制器(例如,Azure 的節點管理器控制器)。但是,控制器管理器本身應該部署到控制平面。使用節點選擇器或親和性節來將雲控制器的排程指向控制平面,以確保它們在受保護的空間中執行。雲控制器對於向叢集新增和移除節點至關重要,因為它們構成了 Kubernetes 和物理基礎架構之間的聯絡。在控制平面上執行它們將有助於確保它們以與其他核心叢集控制器相似的優先順序執行,並且與非特權使用者工作負載有一定的隔離。
    1. 值得注意的是,使用反親和性節來防止雲控制器在同一主機上執行,對於確保單個節點故障不會降低雲控制器效能也非常有用。
  4. 確保容忍度允許操作。在雲控制器容器的清單中使用容忍度,以確保它能排程到正確的節點,並且在節點初始化的情況下也能執行。這意味著雲控制器應該容忍 `node.cloudprovider.kubernetes.io/uninitialized` 汙點,並且還應該容忍與控制平面相關的任何汙點(例如,`node-role.kubernetes.io/control-plane` 或 `node-role.kubernetes.io/master`)。容忍 `node.kubernetes.io/not-ready` 汙點也很有用,以確保即使節點尚未可用於健康監控,雲控制器也能執行。

對於不在其管理的叢集上執行的雲控制器管理器(例如,在單獨叢集上的託管控制平面中),規則受到運行雲控制器管理器的叢集環境的依賴關係的更多限制。在自管理叢集上執行的建議可能不適用,因為衝突型別和網路限制會有所不同。對於這些場景,請查閱您的拓撲的架構和要求。

示例

這是一個 Kubernetes Deployment 的示例,突出了上面顯示的指導。需要注意的是,這僅用於演示目的,對於生產用途,請查閱您的雲提供商的文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: cloud-controller-manager
  name: cloud-controller-manager
  namespace: kube-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: cloud-controller-manager
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app.kubernetes.io/name: cloud-controller-manager
      annotations:
        kubernetes.io/description: Cloud controller manager for my infrastructure
    spec:
      containers: # the container details will depend on your specific cloud controller manager
      - name: cloud-controller-manager
        command:
        - /bin/my-infrastructure-cloud-controller-manager
        - --leader-elect=true
        - -v=1
        image: registry/my-infrastructure-cloud-controller-manager@latest
        resources:
          requests:
            cpu: 200m
            memory: 50Mi
      hostNetwork: true # these Pods are part of the control plane
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: "kubernetes.io/hostname"
            labelSelector:
              matchLabels:
                app.kubernetes.io/name: cloud-controller-manager
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
        operator: Exists
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 120
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 120
      - effect: NoSchedule
        key: node.cloudprovider.kubernetes.io/uninitialized
        operator: Exists
      - effect: NoSchedule
        key: node.kubernetes.io/not-ready
        operator: Exists

在決定如何部署雲控制器管理器時,值得注意的是,不推薦使用叢集比例或基於資源的 Pod 自動縮放。運行雲控制器管理器的多個副本是確保高可用性和冗餘的好做法,但無助於提高效能。通常,任何給定時間只有一個雲控制器管理器例項在協調一個叢集。