使用抢占式虚拟机运行容错工作负载


本页面介绍了如何在 Google Kubernetes Engine (GKE) 中使用抢占式虚拟机。

概览

抢占式虚拟机是价格低于标准虚拟机但不保证可用性的 Compute Engine 虚拟机实例。抢占式虚拟机提供与 Spot 虚拟机类似的功能,但在创建后最长只能使用 24 小时。

在某些情况下,抢占式虚拟机可能持续超过 24 小时。如果新的 Compute Engine 实例启动速度太快,并且 Kubernetes 无法识别创建的其他 Compute Engine 虚拟机时,可能会发生这种情况。底层 Compute Engine 实例最长持续 24 小时,并遵循预期的抢占式虚拟机行为

与 Spot 虚拟机比较

抢占式虚拟机与 Spot 虚拟机有许多相似之处,包括:

  • 在 Compute Engine 需要资源运行标准虚拟机时会终止。
  • 适用于运行无状态、批量或容错工作负载。
  • 价格低于标准虚拟机。
  • 在运行 GKE 1.20 及更高版本的集群上,默认启用安全节点关停
  • 支持集群自动扩缩器节点自动预配

与没有最长到期时间的 Spot 虚拟机相比,抢占式虚拟机仅在创建后最长只能使用 24 小时。

您可以在新集群和节点池上启用抢占式虚拟机、使用 nodeSelector 或节点亲和性来控制调度,以及使用污点和容忍来避免节点被抢占时系统工作负载出现问题。

终止并正常关闭抢占式虚拟机

当 Compute Engine 需要收回抢占式虚拟机使用的资源时,系统会向 GKE 发送抢占通知。抢占式虚拟机在收到终止通知 30 秒后终止。

在运行 GKE 1.20 及更高版本的集群上,kubelet 安全节点关停功能默认启用。Kubelet 会注意到终止通知,并正常终止节点上运行的 Pod。 如果 Pod 是 Deployment 的一部分,则控制器会创建并安排新 Pod,以替换已终止的 Pod。

kubelet 会根据节点池的 GKE 版本尽力提供如下正常终止时间段:

  • 高于 1.22.8-gke.200:非系统 Pod 为 15 秒,之后还会为系统 Pod(具有 system-cluster-criticalsystem-node-critical 优先级类)提供额外 15 秒来正常终止。
  • 1.22.8-gke.200 及更早版本:非系统 Pod 为 25 秒,之后还会为系统 Pod(具有 system-cluster-criticalsystem-node-critical 优先级类)提供额外 5 秒来正常终止。

在节点正常终止期间,kubelet 会更新 Pod 的状态,为终止的 Pod 分配 Failed 阶段和 Terminated 原因。

当节点数少于 100 的集群的终止 Pod 数量达到阈值 1,000,或者节点数超过 100 的集群的终止 Pod 数量达到阈值 5,000 时,垃圾回收功能会清理 Pod。

您还可以使用以下命令手动删除已终止的 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 上使用抢占式虚拟机会修改 Kubernetes 提供的一些保证和限制条件,例如:

  • GKE 会在收到 Compute Engine 的抢占通知 30 秒后为 Pod 关闭抢占式虚拟机,不提供宽限期。

  • 抢占式虚拟机的弃用是非自愿的,不在 PodDisruptionBudgets 的保证范围内。相对于已配置的 PodDisruptionBudget,遇到不可用情况的几率可能较大。

限制

创建具有抢占式虚拟机的集群或节点池

您可以使用 Google Cloud CLI 创建具有抢占式虚拟机的集群或节点池。

如需创建具有抢占式虚拟机的集群,请运行以下命令:

gcloud container clusters create CLUSTER_NAME \
    --preemptible

CLUSTER_NAME 替换为新集群的名称。

如需创建具有抢占式虚拟机的节点池,请运行以下命令:

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

POOL_NAME 替换为新节点池的名称。

使用 nodeSelector 在抢占式虚拟机上调度 pod

GKE 会将 cloud.google.com/gke-preemptible=truecloud.google.com/gke-provisioning=preemptible(对于运行 GKE 1.25.5-gke.2500 或更高版本的节点)标签添加到使用抢占式虚拟机的节点。您可以在部署中使用 nodeSelector 来指示 GKE 将 Pod 调度到抢占式虚拟机上。

例如,以下 Deployment 使用 cloud.google.com/gke-preemptible 标签过滤抢占式虚拟机:

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"

为抢占式虚拟机使用节点污点

您可以为使用抢占式虚拟机的节点设置污点,以便 GKE 只能在这些节点上放置具有相应容忍设置的 Pod。

如需向使用抢占式虚拟机的节点池添加节点污点,请在创建节点池时使用 --node-taints 标志,类似于以下命令:

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

现在,只有能容忍节点污点的 Pod 才会被调度到该节点。

如需向 Pod 添加相关容忍,请修改部署并将以下内容添加到 Pod 规范中:

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

GPU 抢占式虚拟机的节点污点

抢占式虚拟机支持使用 GPU。在添加使用抢占式虚拟机的 GPU 节点池之前,您应该在集群中至少再创建一个不使用抢占式虚拟机的节点池。具有标准节点池可确保 GKE 可以安全地放置 DNS 等系统组件。

如果您创建的新集群中具有使用抢占式虚拟机的 GPU 节点池,或者将使用抢占式虚拟机的新 GPU 节点池添加到尚不具有标准节点池的集群,则 GKE 不会自动将 nvidia.com/gpu=present:NoSchedule 污点添加到节点。GKE 可能会将系统 Pod 调度到抢占式虚拟机上,这可能会导致发生中断。此行为还会增加资源消耗量,因为 GPU 节点比非 GPU 节点成本更高。

后续步骤