您将配置一个支持GPU的节点池,该节点池可从零扩缩,确保仅在实际需要时才供应这些昂贵的资源。这种设置是构建经济高效的共享ML平台的基础。我们将结合Kubernetes的污点(taints)、容忍度(tolerations)和资源请求,以创建一个系统,使集群自动扩缩器(Cluster Autoscaler)能够做出明智的、了解GPU的扩缩决策。您将部署一个明确请求GPU的Pod,并观察整个自动化过程,从Pod最初的Pending状态,到新GPU节点的供应,以及最终工作负载的调度。前提条件开始之前,请确保您的环境已具备以下条件:一个在支持GPU实例的云服务商(如GKE、EKS或AKS)上正常运行的Kubernetes集群。kubectl已配置为与您的集群通信。相应的云命令行工具(例如gcloud、aws、az)已通过认证。Kubernetes集群自动扩缩器(Cluster Autoscaler)必须已部署并在您的集群中运行。我们假设已存在一个基线安装,如前一节所述。本次练习侧重于调整其行为以适应GPU工作负载。GPU自动扩缩机制了解GPU的自动扩缩逻辑不依赖于集群自动扩缩器中的特殊“GPU模式”。相反,它是标准Kubernetes调度原语共同运作的结果。该过程如下进行:节点池污点化: 我们为GPU创建一个专用节点池,并对其应用一个污点,例如nvidia.com/gpu=present:NoSchedule。此污点阻止任何Pod调度到这些节点上,除非它明确具有匹配的容忍度。这将我们昂贵的GPU资源圈定给真正需要它们的工作负载使用。资源请求: Pod清单被定义为通过resources.limits请求GPU,并包含对前一步骤中应用的污点的toleration(容忍度)。触发扩容: 当提交此Pod时,Kubernetes调度器会尝试放置它。它发现没有可用的、Pod可以调度的GPU节点。Pod进入Pending(待处理)状态。自动扩缩器行动: 集群自动扩缩器检测到Pending状态的Pod。它分析Pod的需求(GPU资源、容忍度),并确定从污点化的GPU节点池添加一个节点将允许该Pod被调度。资源供应: 集群自动扩缩器调用云服务商的API,将GPU节点池的所需节点数量增加一个。调度: 一旦新节点加入集群并报告其为Ready(就绪)状态,Kubernetes调度器将待处理的Pod调度到该节点上。以下图表说明了这一系列事件的完整流程。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fillcolor="#e9ecef", fontname="Helvetica"]; edge [fontname="Helvetica", fontsize=10]; subgraph cluster_k8s { label="Kubernetes控制平面"; style=dashed; bgcolor="#f8f9fa"; Scheduler [fillcolor="#a5d8ff"]; Autoscaler [label="集群自动扩缩器", fillcolor="#a5d8ff"]; PendingPod [label="Pod(待处理)\n- 请求:nvidia.com/gpu: 1\n- 容忍:NoSchedule", shape=Mrecord, fillcolor="#ffc9c9"]; } subgraph cluster_cloud { label="云服务商"; style=dashed; bgcolor="#f8f9fa"; CloudAPI [label="云API", fillcolor="#d0bfff"]; GpuNodePool [label="GPU节点池\n(最小=0, 最大=5)\n(污点:NoSchedule)", shape=cylinder, fillcolor="#bac8ff"]; } User [shape=oval, fillcolor="#b2f2bb"]; NewNode [label="新GPU节点", fillcolor="#96f2d7"]; RunningPod [label="Pod(运行中)", shape=Mrecord, fillcolor="#96f2d7"]; User -> Scheduler [label="1. kubectl apply -f pod.yaml"]; Scheduler -> PendingPod [label="2. 调度失败,将Pod标记为待处理"]; Autoscaler -> PendingPod [label="3. 检测到不可调度的Pod"]; Autoscaler -> CloudAPI [label="4. 从GPU池请求新节点"]; CloudAPI -> GpuNodePool [label="5. 供应节点"]; GpuNodePool -> NewNode [label="6. 创建虚拟机"]; NewNode -> Scheduler [label="7. 新节点加入集群"]; Scheduler -> RunningPod [label="8. 将Pod调度到新节点上"]; NewNode -> RunningPod [style=invis]; }从Pod提交到成功调度到新供应的GPU节点上的自动扩缩流程。步骤1:创建一个污点化且支持自动扩缩的节点池首先,我们定义一个符合自动扩缩条件的节点池,且专门用于GPU工作负载。两个最重要的参数是将最小规模设置为0并应用NoSchedule污点。将min-nodes设置为零是我们节约成本策略的核心。以下是在Google Kubernetes Engine (GKE)中创建此类节点池的示例命令。对于EKS或AKS,原理是相同的,尽管具体参数会有所不同。# Example for GKE gcloud container node-pools create gpu-pool \ --project "<your-gcp-project>" \ --cluster "<your-cluster-name>" \ --zone "<your-cluster-zone>" \ --machine-type "n1-standard-4" \ --accelerator "type=nvidia-tesla-t4,count=1" \ --enable-autoscaling \ --min-nodes "0" \ --max-nodes "5" \ --node-taints "nvidia.com/gpu=present:NoSchedule" \ --node-labels "app-type=gpu-workloads"运行此命令后,您会得到一个名为gpu-pool的节点池。它目前没有节点,但已准备好扩容至最多五个配备T4的节点。在此池中创建的任何节点都将自动被污点化,从而排斥没有正确容忍度的Pod。步骤2:安装NVIDIA GPU设备插件Kubernetes本身不原生理解GPU是什么。它需要一个设备插件来发现并公开节点上的GPU硬件。NVIDIA GPU Operator是管理此过程的推荐方式,因为它会自动处理驱动安装、设备插件注册和监控组件。如果您尚未安装,请添加NVIDIA Helm仓库并安装该操作符。helm repo add nvidia https://nvidia.github.io/gpu-operator helm repo update helm install gpu-operator nvidia/gpu-operator \ --wait \ --namespace gpu-operator \ --create-namespace一旦操作符的Pod开始运行,任何带有NVIDIA GPU的节点都将自动被标记为nvidia.com/gpu=true,并且其GPU容量将作为名为nvidia.com/gpu的可分配资源对Kubernetes调度器可见。步骤3:定义一个Pod以触发扩容现在,创建将触发扩容的工作负载。以下YAML清单定义了一个简单的Pod,它除了占用一个GPU外不执行任何操作。请注意两个重要部分:resources.limits用于请求GPU,以及tolerations用于允许它调度到我们污点化的节点上。将此内容保存为gpu-test-pod.yaml。apiVersion: v1 kind: Pod metadata: name: gpu-test-pod spec: restartPolicy: Never containers: - name: cuda-container image: nvidia/cuda:11.4.2-base-ubuntu20.04 command: ["/bin/bash", "-c", "sleep 3600"] # 暂停1小时 resources: limits: nvidia.com/gpu: 1 tolerations: - important: "nvidia.com/gpu" operator: "Equal" value: "present" effect: "NoSchedule"步骤4:部署Pod并观察系统所有部分准备就绪后,应用清单并观察自动化过程。部署Pod:kubectl apply -f gpu-test-pod.yaml观察Pod状态: 立即检查其状态。您将看到它处于Pending(待处理)状态。kubectl get pods -w # 初始输出将类似如下 # NAME READY STATUS RESTARTS AGE # gpu-test-pod 0/1 Pending 0 2s检查Pod事件: 为了理解它为何待处理,请描述该Pod。在Events(事件)部分,您将看到调度器发出的消息,指示它找不到合适的节点。kubectl describe pod gpu-test-pod您应该看到类似这样的事件:Warning FailedScheduling ... 0/X nodes are available: X node(s) had taints that the pod didn't tolerate. 这是预期的,也是集群自动扩缩器的触发条件。检查集群自动扩缩器日志: 在另一个终端中,查看集群自动扩缩器部署的日志。您将看到它识别出待处理的Pod并触发gpu-pool的扩容。# 查找自动扩缩器Pod kubectl get pods -n kube-system | grep cluster-autoscaler # 查看其日志 kubectl logs -f <cluster-autoscaler-pod-name> -n kube-system查找包含Scale-up、pod gpu-test-pod triggered scale-up和... expanding node group .../gpu-pool from 0 to 1的行。观察新节点出现: 在自动扩缩器日志运行的同时,观察您集群中的节点。gpu-pool中的一个新节点将会出现,最初处于NotReady(未就绪)状态,几分钟后会转换为Ready(就绪)状态。kubectl get nodes -w确认Pod调度: 一旦新节点就绪,Kubernetes调度器将自动把gpu-test-pod放置到该节点上。Pod的状态将从Pending(待处理)变为ContainerCreating(容器创建中),最终变为Running(运行中)。步骤5:缩容与清理该系统还处理缩容以节约成本。完成GPU工作负载后,请删除Pod。kubectl delete -f gpu-test-pod.yamlPod删除后,新的GPU节点现在处于空闲状态。集群自动扩缩器将识别到这种资源利用不足的情况。在其配置的超时时间(通常为10分钟)过后,它将终止该节点并将gpu-pool缩容回零。您已成功创建了一个完全弹性、按需的GPU资源池。为避免本次练习产生任何额外费用,您还可以删除节点池本身。# Example for GKE gcloud container node-pools delete gpu-pool \ --cluster "<your-cluster-name>" \ --zone "<your-cluster-zone>"