尽管Kubernetes可以将Pod调度到带有GPU的节点,但其默认行为将每个加速器视为一个不可分割的单元。这种“全有或全无”的分配方式经常导致显著的资源浪费,因为从交互式开发到轻量级推理的许多ML任务都不需要现代GPU的全部算力。为了构建一个经济高效且运行顺畅的ML平台,您必须采用更精密的调度和共享机制。在这一点上,适用于Kubernetes的NVIDIA GPU Operator变得不可或缺。它自动管理所有必要的NVIDIA软件组件,包括驱动程序、容器工具包和设备插件,这些组件将GPU公开为可调度的资源。我们将在此之上实行两种高级共享策略:基于软件的时间切片和基于硬件的多实例GPU (MIG)。NVIDIA GPU Operator:生产环境的基准在划分资源之前,您需要一种管理它们的机制。NVIDIA GPU Operator是生产环境的标准。它使用Kubernetes中的Operator模式来管理集群中每个节点上GPU相关软件的生命周期。一旦安装,Kubernetes调度器就能识别nvidia.com/gpu资源,允许您在Pod配置中请求GPU。一个请求完整GPU的基本Pod如下所示:apiVersion: v1 kind: Pod metadata: name: full-gpu-training-pod spec: containers: - name: cuda-container image: nvidia/cuda:11.8.0-base-ubuntu22.04 command: ["/bin/bash", "-c", "--"] args: [ "while true; do nvidia-smi; sleep 30; done;" ] resources: limits: nvidia.com/gpu: 1 # 请求一个完整、独占的GPU这对于大型训练任务运作良好,但对小型任务而言效率不高。通过时间切片共享GPU时间切片允许多个容器共享一个物理GPU。GPU的调度机制在不同容器的进程之间快速切换上下文,给每个进程分配一部分GPU执行时间。这是一种软件层面的方案,不提供内存隔离,但对于提高非计算密集型或对延迟不敏感的工作负载的利用率非常有效,例如:用于模型开发和检查的Jupyter Notebook。低流量推理服务。并行运行多个小型实验。您可以通过向GPU Operator应用配置来启用时间切片。此配置定义GPU应如何划分。例如,您可以指定将一个GPU分成三个“份额”。然后,设备插件会向Kubernetes调度器宣告此分数资源。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fontname="sans-serif"]; edge [fontname="sans-serif"]; bgcolor="transparent"; subgraph cluster_k8s_node { label="Kubernetes 节点(带1个GPU)"; style=filled; color="#e9ecef"; gpu [label="物理GPU", shape=cylinder, style=filled, fillcolor="#74c0fc"]; pod1 [label="Pod A\n(开发笔记本)", fillcolor="#d8f5a2"]; pod2 [label="Pod B\n(实验)", fillcolor="#d8f5a2"]; pod3 [label="Pod C\n(低流量API)", fillcolor="#d8f5a2"]; } pod1 -> gpu [label=" 共享GPU计算时间"]; pod2 -> gpu [label=" "]; pod3 -> gpu [label=" "]; }时间切片允许多个Pod共享单个物理GPU。GPU的执行上下文在进程之间切换,这对于具有间歇性计算需求的工作负载很有效,但由于上下文切换会引入性能开销。一个请求某个份额的Pod将使用如下所示的资源限制:# 假设节点已配置为每个GPU提供3个时间切片 # 并宣告资源 'nvidia.com/gpu.shared'。 apiVersion: v1 kind: Pod metadata: name: dev-notebook-pod spec: containers: - name: jupyter-container image: jupyter/tensorflow-notebook resources: limits: nvidia.com/gpu.shared: 1 # 请求一个GPU份额时间切片的主要权衡是性能。上下文切换会引入延迟,并且由于内存不隔离,一个“吵闹的邻居”Pod可能会耗尽GPU内存,导致共享相同GPU的其他Pod出现CUDA内存错误。通过多实例GPU (MIG) 实现硬件划分对于需要严格性能保障和安全隔离的工作负载,多实例GPU (MIG) 是更好的方案。在NVIDIA Ampere架构GPU(如A100和H100)及更新版本上可用,MIG将单个GPU划分成最多七个独立的、硬件隔离的GPU实例。每个MIG实例都有自己的:专用流式多处理器 (SM)。专用内存和内存控制器。专用L2缓存。这种硬件级别的划分提供可预测的性能和强大的故障隔离。如果一个Pod的内核在其MIG实例上失败,它不会影响在同一物理GPU上不同实例上运行的其他Pod。这使得MIG成为安全多租户以及在单个GPU上托管具有严格服务质量 (QoS) 要求的多推理模型的一种优秀技术。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fontname="sans-serif"]; edge [fontname="sans-serif"]; bgcolor="transparent"; subgraph cluster_k8s_node { label="Kubernetes 节点 (A100 GPU)"; style=filled; color="#e9ecef"; subgraph cluster_gpu { label="物理GPU (MIG已启用)"; style="dashed"; color="#868e96"; mig1 [label="MIG 实例\n(1g.10gb)", shape=cylinder, style=filled, fillcolor="#91a7ff"]; mig2 [label="MIG 实例\n(1g.10gb)", shape=cylinder, style=filled, fillcolor="#91a7ff"]; mig3 [label="MIG 实例\n(2g.20gb)", shape=cylinder, style=filled, fillcolor="#748ffc"]; mig4 [label="MIG 实例\n(3g.40gb)", shape=cylinder, style=filled, fillcolor="#5c7cfa"]; } } pod1 [label="Pod A\n(请求 1g.10gb)", fillcolor="#d8f5a2"]; pod2 [label="Pod B\n(请求 1g.10gb)", fillcolor="#d8f5a2"]; pod3 [label="Pod C\n(请求 3g.40gb)", fillcolor="#eebefa"]; pod1 -> mig1 [label=" 独占访问"]; pod2 -> mig2 [label=" 独占访问"]; pod3 -> mig4 [label=" 独占访问"]; }多实例GPU (MIG) 将单个物理GPU划分为多个完全隔离的GPU实例。每个实例都有自己的专用内存、缓存和计算资源,确保可预测的性能和安全。当节点上启用MIG时,设备插件将可用的MIG配置宣告为不同的资源类型。Pod在其资源限制中请求特定的MIG实例配置。apiVersion: v1 kind: Pod metadata: name: mig-inference-pod spec: containers: - name: triton-server image: nvcr.io/nvidia/tritonserver:23.10-py3 resources: limits: # 请求特定的MIG配置:1 GPC,10GB内存 nvidia.com/mig-1g.10gb: 1Kubernetes仅会将此Pod调度到具有可用1g.10gb MIG实例的节点上。选择合适的调度策略默认分配、时间切片和MIG之间的选择完全取决于您的工作负载对性能、隔离性和成本的要求。特性最佳适用场景隔离性性能硬件默认(1 Pod/GPU)重型训练、HPC进程级最大、专用任何NVIDIA GPU时间切片开发笔记本、低流量API无(共享内存)可变,有开销任何NVIDIA GPUMIG多租户推理、严格QoS强(硬件)可预测、已划分Ampere架构及更新版本掌握这些高级调度技术,您可以将由昂贵GPU组成集群,从一组单一资源转换为一个灵活、精细且高效的平台。这使得您能够在相同的共享基础设施上支持更多种类的ML工作负载,从开发到生产,直接提高资源利用率并降低运营成本。这些精细的资源定义还为集群自动扩缩器提供了必要的信号,使其做出更智能的决策,这是我们接下来会讨论的话题。