本页面介绍了如何使用动态工作负载调度程序和 ProvisioningRequest 优化 GPU 大规模批量工作负载的可获取性。
我们建议将动态工作负载调度器用于可在非高峰时段运行且具有已定义的 GPU 容量管理条件的大规模批量工作负载。这些工作负载可能是深度学习模型训练,或者是需要大量具有原子预配模型的 GPU 的模拟,这意味着所有资源都是同时创建。
如需在不使用动态工作负载调度器的情况下在 Google Kubernetes Engine (GKE) 中运行 GPU 工作负载,请参阅在 GKE Standard 节点池中运行 GPU。
何时使用动态工作负载调度器
如果您的工作负载满足以下所有条件,我们建议您使用动态工作负载调度器:
- 您可以请求 GPU 来运行工作负载。
- 您的预留 GPU 容量有限或完全没有,并且您希望提高 GPU 资源的可获取性。
- 您的工作负载在时间上非常灵活,您的用例可以承受等待获取所有请求的容量,例如,GKE 在最繁忙时段以外分配 GPU 资源时。
- 您的工作负载需要多个节点,并且只有在所有 GPU 节点均已预配并同时准备就绪之后(例如分布式机器学习训练)才能开始运行。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行
gcloud components update
以获取最新版本。
- 确保您拥有以下任一项:
- 1.28.3-gke.1098000 版或更高版本的现有 Standard 集群。
- 或者 1.30.3-gke.1451000 版或更高版本的现有 Autopilot 集群。
- 确保使用动态工作负载调度程序为包含工作负载的节点池配置中断设置,以防止工作负载中断。使用动态工作负载调度器时,我们建议您永久停用节点自动升级。
- 确保您熟悉动态工作负载调度器的限制。
- 确保至少维护一个未启用动态工作负载调度程序的节点池,以便集群能够正常运行。
将节点池与动态工作负载调度器搭配使用
您可以使用以下三种方法中的任意一种来指定动态工作负载调度器可以使用集群中的特定节点池:
创建节点池
使用 gcloud CLI 创建启用了动态工作负载调度器的节点池:
gcloud beta container node-pools create NODEPOOL_NAME \
--cluster=CLUSTER_NAME \
--location=LOCATION \
--enable-queued-provisioning \
--accelerator type=GPU_TYPE,count=AMOUNT,gpu-driver-version=DRIVER_VERSION \
--machine-type=MACHINE_TYPE \
--enable-autoscaling \
--num-nodes=0 \
--total-max-nodes TOTAL_MAX_NODES \
--location-policy=ANY \
--reservation-affinity=none \
--no-enable-autorepair
请替换以下内容:
NODEPOOL_NAME
:您为节点池选择的名称。CLUSTER_NAME
:集群的名称。LOCATION
:集群的 Compute Engine 区域,例如us-central1
。GPU_TYPE
:GPU 类型。AMOUNT
:要挂接到节点池中节点的 GPU 数量。DRIVER_VERSION
:要安装的 NVIDIA 驱动程序版本。可以是以下各项之一:default
:为您的 GKE 版本安装默认驱动程序版本。latest
:为您的 GKE 版本安装最新可用的驱动程序版本。仅适用于使用 Container-Optimized OS 的节点。
TOTAL_MAX_NODES
:整个节点池自动扩缩的节点数上限。MACHINE_TYPE
:节点的 Compute Engine 机器类型。我们建议您选择加速器优化机器类型。
您可以视需要使用以下标志:
--no-enable-autoupgrade
:推荐。停用节点自动升级。只有未在发布渠道中注册的 GKE 集群支持此功能。如需了解详情,请参阅为现有节点池停用节点自动升级。--node-locations=COMPUTE_ZONES
:GKE 在其中创建 GPU 节点的一个或多个可用区的英文逗号分隔列表。这些可用区必须与集群位于同一区域。选择具有可用 GPU 的可用区。--enable-gvnic
:此标志可启用 GPU 节点池上的 gVNIC 以提高网络流量速度。
此命令会创建一个具有以下配置的节点池:
- GKE 会启用排队的预配和集群自动扩缩。
- 节点池最初没有节点。
--enable-queued-provisioning
标志会启用动态工作负载调度器,并将cloud.google.com/gke-queued
污点添加到节点池。--no-enable-autorepair
和--no-enable-autoupgrade
标志会停用节点的自动修复和升级功能,这可能会中断在已修复或升级的节点上运行的工作负载。您只能在未在发布渠道中注册的集群上停用节点自动升级。
更新现有节点池并启用动态工作负载调度器
为现有节点池启用动态工作负载调度器。查看正确配置节点池的前提条件。
前提条件
确保使用
--reservation-affinity=none
标志创建节点池。此标志稍后在启用动态工作负载调度器时需要,因为在创建节点池后便无法更改预留亲和性。请务必至少维护一个未启用动态工作负载调度程序处理的节点池,以便集群能够正常运行。
确保节点池为空。您可以调整节点池的大小,使其没有节点。
确保已启用并正确配置自动扩缩。
确保已停用自动修复。
为现有节点池启用动态工作负载调度器
您可以使用 gcloud CLI 为现有节点池启用动态工作负载调度器:
gcloud beta container node-pools update NODEPOOL_NAME \
--cluster=CLUSTER_NAME \
--location=LOCATION \
--enable-queued-provisioning
请替换以下内容:
NODEPOOL_NAME
:所选节点池的名称。CLUSTER_NAME
:集群的名称。LOCATION
:集群的 Compute Engine 区域,例如us-central1
。
此节点池更新命令会导致以下配置更改:
--enable-queued-provisioning
标志会启用动态工作负载调度器,并将cloud.google.com/gke-queued
污点添加到节点池。
(可选)您还可以更新以下节点池设置:
- 停用节点自动升级:我们建议您停用节点自动升级,因为使用动态工作负载调度器时不支持节点池升级。如需停用节点自动升级,请确保您的 GKE 集群未在发布渠道中注册。
- 在 GPU 节点池上启用 gVNIC:Google 虚拟 NIC (gVNIC) 可提高 GPU 节点的网络流量速度。
启用节点自动预配功能,以便为动态工作负载调度器创建节点池
您可以使用节点自动预配功能来管理运行 1.29.2-gke.1553000 版或更高版本的集群的动态工作负载调度器的节点池。启用节点自动预配并启用动态工作负载调度器后,GKE 会创建包含关联工作负载所需资源的节点池。
如需启用节点自动预配功能,请考虑以下设置并完成配置 GPU 限制中的步骤:
- 在启用该功能时指定动态工作负载调度器所需的资源。如需列出可用的
resourceTypes
,请运行gcloud compute accelerator-types list
。 - 我们建议您使用
--no-enable-autoprovisioning-autoupgrade
和--no-enable-autoprovisioning-autorepair
标志停用节点自动升级和节点自动修复功能。如需了解详情,请参阅使用动态工作负载调度程序为包含工作负载的节点池配置中断设置。 - 我们建议您允许 GKE 在自动预配的 GPU 节点中自动安装 GPU 驱动程序。如需了解详情,请参阅将节点自动预配与 GPU 搭配使用来安装驱动程序。
使用动态工作负载调度程序运行批量工作负载
如需使用动态工作负载调度器,我们建议您使用 Kueue。Kueue 会根据配额和层次结构,在团队之间共享资源,从而实现 Job 排队,确定 Job 应等待的时间和应开始的时间。这简化了使用排入队列的虚拟机所需的设置。
当您使用自己的内部批量调度工具或平台时,可以在不使用 Kueue 的情况下使用动态工作负载调度器。如需为不使用 Kueue 的作业配置动态工作负载调度器,请参阅不使用 Kueue 的作业的动态工作负载调度器。
使用 Kueue 的作业动态工作负载调度器
以下部分介绍了如何为使用 Kueue 的作业配置动态工作负载调度器。您可以配置以下常见节点池设置:
- 动态工作负载调度器节点池设置。
- 预留和动态工作负载调度器节点池设置。
本部分使用 ai-on-gke
仓库的 dws-examples
目录中的示例。我们已根据 Apache2 许可在 dws-examples
目录中发布示例。
准备环境
在 Cloud Shell 中,运行以下命令:
git clone https://github.com/GoogleCloudPlatform/ai-on-gke cd ai-on-gke/tutorials-and-examples/workflow-orchestration/dws-examples
在集群中安装最新的 Kueue 版本:
VERSION=v0.7.0 kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
如需详细了解 Kueue 安装,请参阅安装。
为动态工作负载调度器节点池专用设置创建 Kueue 资源
使用以下清单时,创建名为 dws-cluster-queue
的集群级队列和名为 dws-local-queue
的 LocalQueue 命名空间。在此命名空间中引用 dws-cluster-queue
队列的作业使用动态工作负载调度器来获取 GPU 资源。
部署 LocalQueue:
kubectl create -f ./dws-queues.yaml
输出类似于以下内容:
resourceflavor.kueue.x-k8s.io/default-flavor created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created
clusterqueue.kueue.x-k8s.io/dws-cluster-queue created
localqueue.kueue.x-k8s.io/dws-local-queue created
如果您要运行在其他命名空间中使用动态工作负载调度器的 Job,您可以使用上述模板创建其他 LocalQueues
。
运行 Job
在以下清单中,示例作业使用动态工作负载调度器:
此清单包含以下与动态工作负载调度器配置相关的字段:
kueue.x-k8s.io/queue-name: dws-local-queue
标签指示 GKE 由 Kueue 负责编排该 Job。此标签还定义了 Job 排入的队列。suspend: true
标志指示 GKE 创建 Job 资源,但尚不调度 Pod。在节点准备好执行 Job 时,Kueue 会将此标志更改为false
。nodeSelector
指示 GKE 仅在指定的节点池上调度作业。该值应与NODEPOOL_NAME
(启用了队列预配的节点池的名称)匹配。
运行 Job:
kubectl create -f ./job.yaml
输出类似于以下内容:
job.batch/sample-job created
检查作业的状态:
kubectl describe job sample-job
输出类似于以下内容:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Suspended 5m17s job-controller Job suspended Normal CreatedWorkload 5m17s batch/job-kueue-controller Created Workload: default/job-sample-job-7f173 Normal Started 3m27s batch/job-kueue-controller Admitted by clusterQueue dws-cluster-queue Normal SuccessfulCreate 3m27s job-controller Created pod: sample-job-9qsfd Normal Resumed 3m27s job-controller Job resumed Normal Completed 12s job-controller Job completed
动态工作负载调度器与 Kueue 的集成还支持开源生态系统中提供的其他工作负载类型,如下所示:
- RayJob
- JobSet v0.5.2 或更高版本
- Kubeflow MPIJob、TFJob、PyTorchJob.
- 工作流编排器常用的 Kubernetes Pod
- Flux 迷你集群
如需详细了解此支持,请参阅 Kueue 的批量用户。
为预留和动态工作负载调度器节点池设置创建 Kueue 资源
使用以下清单,您可以创建两个与两个不同的节点池(reservation-nodepool
和 dws-nodepool
)相关联的 ResourceFlavors
。这些节点池的名称仅作示例之用。根据您的节点池配置修改这些名称。此外,在 ClusterQueue
配置中,传入作业会尝试使用 reservation-nodepool
;如果容量不足,则这些作业使用动态工作负载调度器获取 GPU 资源。
使用以下命令部署清单:
kubectl create -f ./dws_and_reservation.yaml
输出类似于以下内容:
resourceflavor.kueue.x-k8s.io/reservation created
resourceflavor.kueue.x-k8s.io/dws created
clusterqueue.kueue.x-k8s.io/cluster-queue created
localqueue.kueue.x-k8s.io/user-queue created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created
运行 Job
与上述设置相反,此清单不包含 nodeSelector
字段,因为它由 Kueue 填充,具体取决于 ClusterQueue
中的可用容量。
运行 Job:
kubectl create -f ./job-without-node-selector.yaml
输出类似于以下内容:
job.batch/sample-job-v8xwm created
如需了解作业使用的节点池,您需要了解作业使用的 ResourceFlavor。
问题排查
如需详细了解 Kueue 的问题排查,请参阅排查 Kueue 中的预配请求问题
不使用 Kueue 的作业的动态工作负载调度器
通过 ProvisioningRequest API 为每个作业创建请求。动态工作负载调度器不会启动 Pod,只会预配节点。
创建以下
provisioning-request.yaml
清单:apiVersion: v1 kind: PodTemplate metadata: name: POD_TEMPLATE_NAME namespace: NAMESPACE_NAME labels: cloud.google.com/apply-warden-policies: "true" template: spec: nodeSelector: cloud.google.com/gke-nodepool: NODEPOOL_NAME tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" containers: - name: pi image: perl command: ["/bin/sh"] resources: limits: cpu: "700m" nvidia.com/gpu: 1 requests: cpu: "700m" nvidia.com/gpu: 1 restartPolicy: Never --- apiVersion: autoscaling.x-k8s.io/v1beta1 kind: ProvisioningRequest metadata: name: PROVISIONING_REQUEST_NAME namespace: NAMESPACE_NAME spec: provisioningClassName: queued-provisioning.gke.io parameters: maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS" podSets: - count: COUNT podTemplateRef: name: POD_TEMPLATE_NAME
替换以下内容:
NAMESPACE_NAME
:Kubernetes 命名空间的名称。命名空间必须与 Pod 的命名空间相同。PROVISIONING_REQUEST_NAME
:ProvisioningRequest
的名称。您将在 Pod 注解中引用此名称。MAX_RUN_DURATION_SECONDS
:(可选)节点的最长运行时(以秒为单位),最长为 7 天(默认值)。如需了解详情,请参阅动态工作负载调度器的工作原理。创建请求后,您将无法更改此值。此字段在 GKE 1.28.5-gke.1355000 版或更高版本中为预览版。COUNT
:请求的 Pod 数量。节点以原子方式调度到一个可用区中。POD_TEMPLATE_NAME
:Kubernetes 的标准名称。GKE 在预配请求 PodSet 中引用此值。NODEPOOL_NAME
:您为节点池选择的名称。
应用清单:
kubectl apply -f provisioning-request.yaml
配置 Pod
在作业规范中,使用以下注解将 Pod 关联到 ProvisioningRequest
:
apiVersion: batch/v1
kind: Job
spec:
template:
metadata:
annotations:
cluster-autoscaler.kubernetes.io/consume-provisioning-request: PROVISIONING_REQUEST_NAME
cluster-autoscaler.kubernetes.io/provisioning-class-name: "queued-provisioning.gke.io"
spec:
...
Pod 注解键 cluster-autoscaler.kubernetes.io/consume-provisioning-request
定义要使用的 ProvisioningRequest
。GKE 使用 consume-provisioning-request
和 provisioning-class-name
注解执行以下操作:
- 仅在动态工作负载调度器预配的节点中调度 Pod。
- 避免重复计算集群自动扩缩器中 Pod 和动态工作负载调度器之间的资源请求。
- 注入
safe-to-evict: false
注解,以防止集群自动扩缩器在节点之间移动 Pod 和中断批量计算。您可以通过在 Pod 注解中指定safe-to-evict: true
来更改此行为。
观察动态工作负载调度器的状态
动态工作负载调度器的状态定义是否可以安排 Pod。 您可以使用 Kubernetes 监视功能高效地观察更改,或您已用来跟踪 Kubernetes 对象状态的其他工具。下表介绍了动态工作负载调度器的可能状态以及每种可能的结果:
动态工作负载调度器状态 | 说明 | 可能的结果 |
---|---|---|
待处理 | 该请求尚未被发现并且未被处理。 | 处理完成后,请求将转换为 Accepted 或 Failed 状态。 |
Accepted=true |
请求被接受,正在等待资源变为可用状态。 | 如果找到资源并预配了节点,请求应转换为 Provisioned 状态;否则,请求应转换为 Failed 状态。 |
Provisioned=true |
节点已准备就绪。 | 您可以在 10 分钟内启动 Pod 以使用预配的资源。此时间之后,集群自动扩缩器会将节点视为不需要的节点并将其移除。 |
Failed=true |
由于存在错误,无法预配这些节点。Failed=true 是一种终端状态。 |
根据条件的 Reason 和 Message 字段中的信息对条件进行问题排查。
创建并重试新的动态工作负载调度器请求。 |
Provisioned=false |
尚未预配节点。 | 如果为 如果为 |
启动 Pod
当动态工作负载调度器请求达到 Provisioned=true
状态时,您可以运行作业来启动 Pod。这样可以避免待处理或失败请求的无法安排的 Pod 数量激增,这可能会影响 kube-scheduler 和集群自动扩缩器的性能。
或者,如果您不在意 Pod 无法安排,则可以使用动态工作负载调度器并行创建 Pod。
取消动态工作负载调度器请求
如需在预配之前取消请求,您可以删除 ProvisioningRequest
:
kubectl delete provreq PROVISIONING_REQUEST_NAME -n NAMESPACE
在大多数情况下,删除 ProvisioningRequest
会停止创建节点。但是,根据时间(例如,如果已预配节点),则最终可能仍会创建节点。在这些情况下,如果未创建 Pod,集群自动扩缩器会在 10 分钟后移除节点。
动态工作负载调度器的工作原理
使用 ProvisioningRequest API
时,动态工作负载调度器会执行以下操作:
- 您会告诉 GKE,您的工作负载可以等待一段不确定的时间,直到所有必需的节点都准备好立即使用。
- 集群自动扩缩器接受您的请求并计算所需的节点数,将它们视为一个单元。
- 请求会等待单个可用区中的所有可用资源都可用。 对于运行 1.29.1-gke.1708000 及更高版本的集群,系统会使用可用容量信息来选择此区域,以确保等待时间更短。对于运行早期版本的集群,系统在选择可用区时不会考虑此信息,这可能会导致在等待时间更长的可用区排队。
- 集群自动扩缩器会在必要的节点可用时同时预配这些节点。
- 工作负载的所有 Pod 都可以在新预配的节点上一起运行。
- 预配的节点限制为运行七天或更早的时间(如果您设置
maxRunDurationSeconds
参数指示工作负载需要较少的运行时间)。如需了解详情,请参阅限制虚拟机的运行时(预览版)。GKE 1.28.5-gke.1355000 或更高版本提供此功能。此时间之后,节点及其上运行的 Pod 将被抢占。如果 Pod 更快地完成并且节点未被利用,则集群自动扩缩器会根据自动扩缩配置文件将其移除。 - 节点不会在动态工作负载调度器之间重复使用。每个
ProvisioningRequest
指示使用新 7 天运行时创建新节点。
配额
由动态工作负载调度器请求预配的所有虚拟机都使用抢占式配额。
处于 Accepted
状态的 ProvisioningRequests
的数量受专用配额限制。您可以为每个项目配置配额,每个区域对应一个配额配置。
在 Google Cloud 控制台中查看配额
如需在 Google Cloud 控制台中查看配额限制的名称和当前用量,请按照以下步骤操作:
转到 Google Cloud 控制台中的配额页面。
在
过滤条件框中,选择指标属性,输入active_resize_requests
并按 Enter 键。
默认值为 100。如需增加配额,请按照申请更高配额限制指南中列出的步骤操作。
检查动态工作负载调度器是否受配额限制
如果您的动态工作负载调度器请求花费的时间超出预期,请检查该请求是否不受配额限制。您可能需要申请更多配额。
对于运行 1.29.2-gke.1181000 或更高版本的集群,请检查特定配额限制是否会阻止您的请求得到满足:
kubectl describe provreq PROVISIONING_REQUEST_NAME \
--namespace NAMESPACE
输出类似于以下内容:
…
Last Transition Time: 2024-01-03T13:56:08Z
Message: Quota 'NVIDIA_P4_GPUS' exceeded. Limit: 1.0 in region europe-west4.
Observed Generation: 1
Reason: QuotaExceeded
Status: False
Type: Provisioned
…
在此示例中,GKE 无法部署节点,因为 europe-west4
区域没有足够的配额。
使用动态工作负载调度器为具有工作负载的节点池配置中断设置
要求节点池中所有节点或大多数节点的可用性的工作负载对逐出很敏感。不支持自动修复或升级使用 ProvisioningRequest API
预配的节点,因为这些操作会逐出该节点上运行的所有工作负载,并且无法安排工作负载。
为了最大限度地减少使用动态工作负载调度器正在运行的工作负载的中断,我们建议您采取以下措施:
- 根据集群的发布渠道注册,请遵循以下最佳实践以防止节点自动升级中断您的工作负载:
- 停用节点自动修复功能。
- 使用维护窗口和排除项来最大限度地减少正在运行的工作负载的中断,同时确保 GKE 可以在某个时间段内中断节点池以进行自动维护。如果您使用这些维护工具,则必须设置 GKE 可以中断节点池的特定时间段,因此我们建议您在没有正在运行的工作负载时设置此时间段。
- 为确保您的节点池保持最新状态,我们建议您在没有活跃的动态工作负载调度器请求且节点池为空时手动升级节点池。
限制
- 不支持 Pod 之间的反亲和性。在节点预配期间,集群自动扩缩器不会考虑 pod 之间的反亲和性规则,这可能会导致工作负载无法调度。当在同一节点池中预配两个或更多动态工作负载调度器对象的节点时,可能会发生这种情况。
- 仅支持 GPU 节点。
- 动态工作负载调度器不支持预留。创建节点池时,您必须指定
--reservation-affinity=none
。动态工作负载调度器要求并仅支持ANY
位置政策用于集群自动扩缩。 - 单个动态工作负载调度器请求最多可以创建 1000 个虚拟机,这是单个节点池的每个可用区的节点数上限。
- GKE 使用 Compute Engine
ACTIVE_RESIZE_REQUESTS
配额来控制队列中待处理的动态工作负载调度器请求数量。默认情况下,此配额在 Google Cloud 项目级限制为 100。如果您尝试创建大于此配额的动态工作负载调度器请求,新请求将失败。 - 由于节点一起预配,因此使用动态工作负载调度器的节点池对中断很敏感。如需了解详情,请参阅为使用动态工作负载调度器的工作负载的节点池配置中断设置。
- 您可能会看到 Google Cloud 控制台中列出其他短期有效的虚拟机。这是预期行为,因为 Compute Engine 可能会创建虚拟机,并在预配所有必需机器的容量可用时立即移除这些虚拟机。
- 动态工作负载调度器集成仅支持一个 PodSet。如果您想混合使用不同的 Pod 模板,请使用请求的资源最多的模板。不支持混合使用不同的机器类型,例如具有不同 GPU 类型的虚拟机。
后续步骤
- 详细了解 GKE 中的 GPU。
- 了解如何在 Autopilot 中部署 GPU 工作负载。