本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

介紹 PodTopologySpread

管理 Pod 在叢集中的分佈非常困難。眾所周知的 Kubernetes Pod 親和性和反親和性功能,允許在不同拓撲中對 Pod 放置進行一些控制。但是,這些功能僅解決了 Pod 分佈用例的一部分:要么將無限數量的 Pod 放置到單個拓撲中,要么不允許兩個 Pod 在同一拓撲中共存。在這兩個極端情況之間,通常需要將 Pod 均勻分佈在拓撲中,以便實現更好的叢集利用率和應用程式的高可用性。

PodTopologySpread 排程外掛程式(最初提議為 EvenPodsSpread)旨在填補該空白。我們在 1.18 版本中將其升級為 beta 版。

API 變更

在 Pod 的 spec API 中引入了一個新的欄位 topologySpreadConstraints

spec:
  topologySpreadConstraints:
  - maxSkew: <integer>
    topologyKey: <string>
    whenUnsatisfiable: <string>
    labelSelector: <object>

由於此 API 嵌入在 Pod 的 spec 中,因此您可以在所有高階工作負載 API 中使用此功能,例如 Deployment、DaemonSet、StatefulSet 等。

讓我們看一個叢集範例來理解此 API。

API

  • labelSelector 用於尋找符合條件的 Pod。對於每個拓撲,我們計算符合此標籤選擇器的 Pod 數量。在上面的範例中,給定 labelSelector 為 "app: foo","zone1" 中的匹配數為 2;而 "zone2" 中的數量為 0。
  • topologyKey 是在節點標籤中定義拓撲的鍵。在上面的範例中,如果某些節點具有 "zone=zone1" 標籤,則將其分組到 "zone1" 中;而其他節點則分組到 "zone2" 中。
  • maxSkew 描述了 Pod 可以不均勻分佈的最大程度。在上面的範例中
    • 如果我們將傳入的 Pod 放置到 "zone1",則 "zone1" 上的偏差將變為 3("zone1" 中匹配了 3 個 Pod;"zone2" 中匹配的 Pod 全域最小值為 0),這違反了 "maxSkew: 1" 限制。
    • 如果將傳入的 Pod 放置到 "zone2",則 "zone2" 上的偏差為 0("zone2" 中匹配了 1 個 Pod;"zone2" 本身匹配的 Pod 全域最小值為 1),這滿足了 "maxSkew: 1" 限制。請注意,偏差是針對每個合格的節點計算的,而不是全域偏差。
  • whenUnsatisfiable 指定當無法滿足 "maxSkew" 時,應採取什麼措施
    • DoNotSchedule(預設值)告訴排程器不要排程它。這是一個硬性限制。
    • ScheduleAnyway 告訴排程器仍然排程它,同時優先考慮減少偏差的節點。這是一個軟性限制。

進階用法

正如功能名稱 "PodTopologySpread" 所暗示的那樣,此功能的基本用法是以絕對均勻的方式 (maxSkew=1) 或相對均勻的方式 (maxSkew>=2) 執行您的工作負載。有關更多詳細資訊,請參閱官方文件

除了這種基本用法之外,還有一些進階用法範例,使您的工作負載能夠在高可用性和叢集利用率方面受益。

與 NodeSelector / NodeAffinity 一起使用

您可能已經發現我們沒有 "topologyValues" 欄位來限制 Pod 將要排程到的拓撲。預設情況下,它將搜尋所有節點並按 "topologyKey" 對其進行分組。有時這可能不是理想的情況。例如,假設有一個叢集,其中的節點標記為 "env=prod"、"env=staging" 和 "env=qa",現在您想要跨區域將 Pod 均勻地放置到 "qa" 環境中,這可能嗎?

答案是肯定的。您可以利用 NodeSelector 或 NodeAffinity API 規範。在底層,PodTopologySpread 功能將尊重這一點,並在滿足選擇器的節點之間計算分佈限制。

Advanced-Usage-1

如上圖所示,您可以指定 `spec.affinity.nodeAffinity` 將「搜尋範圍」限制為「qa」環境,並且在此範圍內,Pod 將排程到滿足 topologySpreadConstraints 的區域。在這種情況下,它是 "zone2"。

多個 TopologySpreadConstraints

直觀地理解單個 TopologySpreadConstraint 的工作原理。多個 TopologySpreadConstraints 的情況又是如何呢?在內部,每個 TopologySpreadConstraint 都是獨立計算的,結果集將被合併以產生最終的結果集 - 即,合適的節點。

在以下範例中,我們希望同時將 Pod 排程到具有 2 個要求的叢集

  • 在區域之間均勻放置 Pod
  • 在節點之間均勻放置 Pod

Advanced-Usage-2

對於第一個限制,zone1 中有 3 個 Pod,zone2 中有 2 個 Pod,因此傳入的 Pod 只能放置到 zone2 以滿足 "maxSkew=1" 限制。換句話說,結果集是 nodeX 和 nodeY。

對於第二個限制,nodeB 和 nodeX 中有太多 Pod,因此傳入的 Pod 只能放置到 nodeA 和 nodeY。

現在我們可以得出結論,唯一合格的節點是 nodeY - 來自集合 {nodeX, nodeY}(來自第一個限制)和 {nodeA, nodeY}(來自第二個限制)的交集。

多個 TopologySpreadConstraints 功能強大,但請務必理解與前面的「NodeSelector/NodeAffinity」範例的區別:一個是獨立計算結果集然後相互連接;而另一個是根據節點約束的篩選結果計算 topologySpreadConstraints。

除了在所有 topologySpreadConstraints 中使用「硬性」限制外,您還可以結合使用「硬性」限制和「軟性」限制,以適應更多樣化的叢集情況。

PodTopologySpread 預設值

PodTopologySpread 是一個 Pod 層級 API。因此,要使用此功能,工作負載作者需要了解叢集的底層拓撲,然後在每個工作負載的 Pod spec 中指定適當的 `topologySpreadConstraints`。雖然 Pod 層級 API 提供了最大的靈活性,但也可以指定叢集層級的預設值。

預設的 PodTopologySpread 限制允許您為叢集中的所有工作負載指定分佈,並針對其拓撲進行客製化。操作員/管理員可以在啟動 kube-scheduler 時,在排程設定檔組態 API中將限制指定為 PodTopologySpread 外掛程式引數。

範例組態可能如下所示

apiVersion: kubescheduler.config.k8s.io/v1alpha2
kind: KubeSchedulerConfiguration
profiles:
  pluginConfig:
  - name: PodTopologySpread
    args:
      defaultConstraints:
      - maxSkew: 1
        topologyKey: example.com/rack
        whenUnsatisfiable: ScheduleAnyway

在組態預設限制時,標籤選擇器必須留空。kube-scheduler 將從 Pod 到 Services、ReplicationControllers、ReplicaSets 或 StatefulSets 的成員資格中推斷出標籤選擇器。Pod 始終可以透過 PodSpec 提供自己的預設限制來覆寫預設限制。

總結

PodTopologySpread 允許您使用靈活且富有表現力的 Pod 層級 API 為您的工作負載定義分佈限制。過去,工作負載作者使用 Pod 反親和性規則來強制或提示排程器在每個拓撲域中執行單個 Pod。相比之下,新的 PodTopologySpread 限制允許 Pod 指定可以是必需的(硬性)或期望的(軟性)偏差級別。該功能可以與節點選擇器和節點親和性配對,以將分佈限制在特定域中。Pod 分佈限制可以針對不同的拓撲定義,例如主機名稱、區域、區域、機架等。

最後,叢集操作員可以定義要應用於所有 Pod 的預設限制。這樣,Pod 不需要知道叢集的底層拓撲。