使用先占 VM 執行容錯工作負載


本頁說明如何在 Google Kubernetes Engine (GKE) 中使用先占 VM。

總覽

先占 VM 是 Compute Engine VM 執行個體,價格低於標準 VM,但不保證可用性。先占 VM 的功能與Spot VM 類似,但建立後最多只能持續 24 小時。

在某些情況下,先占 VM 的存續時間可能會超過 24 小時。如果新的 Compute Engine 執行個體啟動速度過快,Kubernetes 無法辨識出已建立不同的 Compute Engine VM,就可能發生這種情況。底層的 Compute Engine 執行個體最長可執行 24 小時,並遵循先占 VM 的預期行為

與 Spot VM 比較

先占 VM 與 Spot VM 有許多相似之處,包括:

  • 在 Compute Engine 需要資源來執行標準 VM 時終止。
  • 適用於執行無狀態、批次或容錯工作負載。
  • 價格比標準 VM 更低。
  • 在執行 GKE 1.20 以上版本的叢集上,預設會啟用節點正常關機
  • 可搭配叢集自動配置器節點自動佈建功能使用。

與沒有最長到期時間的 Spot VM 不同,先占 VM 建立後最多只能存續 24 小時。

您可以在新的叢集和節點集區上啟用先占 VM,使用 nodeSelector 或節點親和性控管排程,並使用 taint 和容許條件,避免節點遭到先占時發生系統工作負載問題。

終止及按適當流程關閉先占 VM

當 Compute Engine 需要回收先佔 VM 使用的資源時,系統會將先佔通知傳送至 GKE。先占 VM 會在收到終止通知的 30 秒後終止。

根據預設,叢集會使用安全節點關機。 kubelet 會注意到終止通知,並正常終止在節點上執行的 Pod。如果 Pod 屬於受管理的工作負載 (例如 Deployment),控制器會建立並排定新的 Pod,取代終止的 Pod。

Kubelet 會盡量為非系統 Pod 提供 15 秒的正常終止時間,之後系統 Pod (具有 system-cluster-criticalsystem-node-critical priorityClasses) 會有 15 秒的正常終止時間。在節點正常終止期間,kubelet 會更新 Pod 的狀態,並為終止的 Pod 指派 Failed 階段和 Terminated 原因。

即使您在 Pod 資訊清單的 terminationGracePeriodSeconds 欄位中指定大於 15 秒的值,VM 仍會在終止通知傳送後 30 秒關閉。

當終止的 Pod 數量達到門檻時,垃圾收集會清除 Pod。門檻為:節點少於 100 個的叢集為 1000 個,節點數達 100 個以上的叢集為 5000 個。

您也可以使用下列指令手動刪除已終止的 Pod:

  kubectl get pods --all-namespaces | grep -i NodeShutdown | awk '{print $1, $2}' | xargs -n2 kubectl delete pod -n
  kubectl get pods --all-namespaces | grep -i Terminated | awk '{print $1, $2}' | xargs -n2 kubectl delete pod -n

Kubernetes 行為的修改

在 GKE 上使用先占 VM 會修改 Kubernetes PodDisruptionBudgets 提供的保證。先占 VM 的回收作業為非自願性,且不在PodDisruptionBudgets的保證範圍內。您可能會遇到比設定的 PodDisruptionBudget 更高的無法使用率。

限制

  • kubelet 節點正常關機功能僅適用於執行 GKE 1.20 以上版本的叢集。如果是 1.20 之前的 GKE 版本,您可以使用 Kubernetes on GCP Node Termination Event Handler,在先占 VM 終止時正常終止 Pod。
  • 先占 VM 不支援 Windows Server 節點集區。
  • 在 GKE 中,您無法變更節點關閉的寬限期長度。shutdownGracePeriodshutdownGracePeriodCriticalPods kubelet 設定欄位無法變更。

建立具有先占 VM 的叢集或節點集區

您可以使用 Google Cloud CLI 建立具有先占 VM 的叢集或節點集區。

如要建立具有先占 VM 的叢集,請執行下列指令:

gcloud container clusters create CLUSTER_NAME \
    --preemptible

CLUSTER_NAME 替換為新叢集的名稱。

如要建立具有先占 VM 的節點集區,請執行下列指令:

gcloud container node-pools create POOL_NAME \
    --cluster=CLUSTER_NAME \
    --preemptible

POOL_NAME 替換為新節點集區的名稱。

使用 nodeSelector 將 Pod 排程到先占 VM

GKE 會將 cloud.google.com/gke-preemptible=truecloud.google.com/gke-provisioning=preemptible (適用於執行 GKE 1.25.5-gke.2500 以上版本的節點) 標籤新增至使用可搶占 VM 的節點。您可以在部署作業中使用 nodeSelector,告知 GKE 將 Pod 排程到先占 VM 上。

舉例來說,以下是使用 cloud.google.com/gke-preemptible 標籤篩選先占 VM 的 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-app
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
      - name: hello-app
        image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
        resources:
          requests:
            cpu: 200m
      nodeSelector:
        cloud.google.com/gke-preemptible: "true"

為先占 VM 使用節點 taint

您可以對使用先占 VM 的節點進行 taint,這樣一來,GKE 就只會將具有對應容許條件的 Pod 放置在這些節點上。

如要為使用先占 VM 的節點集區新增節點 Taint,請在建立節點集區時使用 --node-taints 標記,類似下列指令:

gcloud container node-pools create POOL2_NAME \
    --cluster=CLUSTER_NAME \
    --node-taints=cloud.google.com/gke-preemptible="true":NoSchedule

現在,只有可容忍節點 Taint 的 Pod 會排程到節點。

如要為 Pod 新增相關容許條件,請修改部署作業,並在 Pod 規格中加入下列內容:

tolerations:
- key: cloud.google.com/gke-preemptible
  operator: Equal
  value: "true"
  effect: NoSchedule

GPU 先占 VM 的節點 Taint

先占 VM 支援使用 GPU。 您應先在叢集中建立至少一個不使用先占 VM 的節點集區,再新增使用先占 VM 的 GPU 節點集區。有了標準節點集區,GKE 就能安全地放置 DNS 等系統元件。

如果您建立的新叢集具有使用先占 VM 的 GPU 節點集區,或是將使用先占 VM 的新 GPU 節點集區新增至沒有標準節點集區的叢集,GKE 不會自動將 nvidia.com/gpu=present:NoSchedule taint 新增至節點。GKE 可能會將系統 Pod 排程至先占 VM,這可能會導致中斷。此外,GPU 節點比非 GPU 節點昂貴,因此這種行為也會增加資源消耗量。

後續步驟