本页面介绍了如何在 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 秒后终止。
默认情况下,集群使用正常节点关停。Kubelet 会注意到终止通知,并正常终止节点上运行的 Pod。如果 Pod 是 Deployment 的一部分,则控制器会创建并调度新 Pod 来替换已终止的 Pod。
Kubelet 会尽最大努力为非系统 Pod 提供 15 秒的正常终止时间段,之后系统 Pod(具有 system-cluster-critical
或 system-node-critical
priorityClass)将有 15 秒的时间来正常终止。
在节点正常终止期间,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
,遇到不可用情况的几率可能较大。
限制
- kubelet 正常节点关停功能仅会在运行 GKE 1.20 版及更高版本的集群上启用。对于 1.20 版之前的 GKE 版本,您可以使用 Kubernetes on GCP 节点终止事件处理脚本在抢占式虚拟机终止时正常终止 Pod。
- 抢占式虚拟机不支持 Windows Server 节点池。
创建具有抢占式虚拟机的集群或节点池
您可以使用 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=true
和 cloud.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 节点成本更高。