计算和自动缩放 - Amazon EKS

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

计算和自动缩放

GPU 资源优化和成本管理

使用知名标签安排具有 GPU 要求的工作负载

对于对不同 GPU 特性敏感 AI/ML 的工作负载(例如 GPU、GPU 内存),我们建议使用与 Karpenter托管节点组一起使用的节点类型支持的已知调度标签来指定 GPU 要求。未能定义这些可能会导致 Pod 被调度到 GPU 资源不足的实例上,从而导致故障或性能降低。我们建议使用 NodeSelector节点亲和性来指定 Pod 应在哪个节点上运行,并在 Pod 的资源部分设置计算资源(CPU、内存 GPUs 等)。

示例

例如,在使用 Karpenter 时使用 GPU 名称节点选择器:

apiVersion: v1 kind: Pod metadata: name: gpu-pod-example spec: containers: - name: ml-workload image: <image> resources: limits: nvidia.com/gpu: 1 # Request one NVIDIA GPU nodeSelector: karpenter.k8s.aws/instance-gpu-name: "l40s" # Run on nodes with NVIDIA L40S GPUs

使用 Kubernetes 设备插件进行公开 GPUs

要 GPUs 在节点上公开,必须将 NVIDIA GPU 驱动程序安装在节点的操作系统上,容器运行时必须配置为允许 Kubernetes 调度器将容器分配给可用节点。 GPUsNVIDIA Kubernetes 设备插件的设置过程取决于你正在使用的 EKS 加速 AMI:

  • Bottlerocket 加速的 AMI:这款 AMI 包含 NVIDIA GPU 驱动程序,NVIDIA Kubernetes 设备插件已预先安装并随时可用,开箱即用 GPU 支持。无需额外配置即可向 Kubernetes GPUs 调度器公开。

  • AL2023 加速的 AMI:此 AMI 包含 NVIDIA GPU 驱动程序,但未预装 NVIDIA Kubernetes 设备插件您必须单独安装和配置设备插件,通常是通过 DaemonSet。请注意,如果您使用 eksctl 创建集群并在中指定 GPU 实例类型(例如g5.xlarge),则eksctl会自动选择加速的 AMI 并在节点组中的每个实例上安装 NVIDIA Kubernetes 设备插件。 ClusterConfig要了解更多信息,请参阅 eksctl 文档中的 GPU 支持

要验证 NVIDIA 设备插件是否处于活动状态且 GPUs 已正确公开,请运行:

kubectl describe node | grep nvidia.com/gpu

此命令检查nvidia.com/gpu资源是否在节点的容量和可分配的资源范围内。例如,应显示具有一个 GPU 的节点nvidia.com/gpu: 1。有关更多信息,请参阅 Kubernetes GPU 调度指南

使用 ML 容量块为 P 和 Trainium 实例提供容量保障

机器学习容量块允许您预留备受追捧的 GPU 实例,特别是 P 实例(例如 p6-b200、p5、p5e、p5e、p5en、p4d、p4de)和 Trainium 实例(例如 trn1、trn2),以便几乎立即启动或在将来的某个日期启动,以支持您的短时机器学习 (ML) 工作负载。这些预留空间非常适合确保模型训练和微调等计算密集型任务的容量。 EC2 容量块定价包括预留费和操作系统费。要了解有关定价的更多信息,请参阅 ML EC2 容量块定价

要在 Amazon EKS 上 GPUs 为 AI/ML 工作负载预留以获得可预测的容量保证,我们建议使用 ML 容量块进行短期容量预留,或使用按需容量预留 (ODCRs) 来保证通用容量。

  • ODCRs 允许您在特定可用区域中预留一段时间的 EC2 实例容量(例如 g5 或 p5 等 GPU 实例),从而确保可用性,即使在高需求期间也是如此。 ODCRs 没有长期承诺,但无论是已用容量还是闲置容量,都要按按需费率支付预留容量。在 EKS 中 ODCRs ,由 Karpenter托管节点组等节点类型提供支持。要 ODCRs 在 Karpenter 中设置优先级,请将配置 NodeClass 为使用该capacityReservationSelectorTerms字段。请参阅 Karpenter 文档。 NodePools

  • 容量块是 GPU(例如 p5、p4d)或 Trainium(trn1、trn2)实例的专用预留机制,专为模型训练、微调或实验等短期机器学习工作负载而设计。您可以从将来的某个日期开始在定义的时间段(通常为 24 小时到 182 天)内预留容量,只需为预留的时间付费。它们是预付费的,需要对容量需求进行预先规划,并且不支持自动扩展,但它们是托管在 EC2 UltraClusters 一起的,可以实现低延迟联网。他们仅在保留期内收费。要了解更多信息,请参阅查找和购买容量块,或者按照为机器学习创建带容量块的托管节点组中的说明开始使用容量块设置托管节点组

通过 AWS 管理控制台预留容量,并将您的节点配置为使用 ML 容量块。根据工作负载计划计划预留并在暂存集群中进行测试。有关更多信息,请参阅容量块文档

考虑为 G Amazon EC2 EC2 实例预留按需、Amazon Spot 或按需容量预留 (ODCRs)

对于 G Amazon EC2 实例,请考虑按需实例、Amazon EC2 Spot 实例和按需容量预留的不同购买选项。 ODCRs允许您在特定可用区域中将 EC2 实例容量预留一段时间,即使在高需求期间也能确保可用性。与仅适用于 P 和 Trainium 实例的 ML 容量块不同,它 ODCRs 可用于更广泛的实例类型,包括 G 实例,因此适用于需要不同 GPU 功能的工作负载,例如推理或图形。使用 Amazon EC2 竞价型实例时,能够在不同的实例类型、大小和可用区域之间进行多样化是能够在竞价上停留更长时间的关键。

ODCRs 没有长期承诺,但无论是已用容量还是闲置容量,都要按按需费率支付预留容量。 ODCRs 可以创建以供立即使用,也可以安排在将来的某个日期使用,这为容量规划提供了灵活性。在 Amazon EKS ODCRs 中,由 Karpenter托管节点组等节点类型提供支持。要 ODCRs 在 Karpenter 中设置优先级,请将配置 NodeClass 为使用该capacityReservationSelectorTerms字段。请参阅 Karpenter 文档。 NodePools 有关创建 ODCRs的更多信息(包括 CLI 命令),请参阅按需容量预留入门

考虑其他加速实例类型和大小

选择适当的加速实例和大小对于在 Amazon EKS 上优化机器学习工作负载的性能和成本至关重要。例如,不同的 GPU 实例系列具有不同的性能和功能,例如 GPU 内存。为了帮助您选择性价比最高的选项,请在 “加速计算” 下的 “EC2 实例类型” 页面中查看可用的 GPU 实例。评估多种实例类型和大小,以找到最适合您的特定工作负载要求的实例类型和大小。考虑诸如数量 GPUs、内存和网络性能之类的因素。通过仔细选择正确的 GPU 实例类型和大小,您可以在 EKS 集群中实现更好的资源利用率和成本效益。

如果您在 EKS 节点中使用 GPU 实例,则默认情况下,它的 nvidia-device-plugin-daemonset pod 将位于kube-system命名空间中。要快速了解你是否充分利用了实例中的 GPU,你可以使用 nvidia-smi,如下所示:

kubectl exec nvidia-device-plugin-daemonset-xxxxx \ -n kube-system -- nvidia-smi \ --query-gpu=index,power.draw,power.limit,temperature.gpu,utilization.gpu,utilization.memory,memory.free,memory.used \ --format=csv -l 5
  • 如果utilization.memory接近 100%,则您的代码很可能受限于内存。这意味着 GPU(内存)已得到充分利用,但可能表明应研究进一步的性能优化。

  • 如果接近 100%,这并不一定意味着 GPU 已得到充分利用。utilization.gpu更好的衡量标准是与的power.draw比率power.limit。如果此比率为 100% 或更高,则说明您的代码充分利用了 GPU 的计算容量。

  • -l 5标志显示每 5 秒输出一次指标。对于单个 GPU 实例类型,则不需要索引查询标志。

要了解更多信息,请参阅 AWS 文档中的 GPU 实例

通过时间切片、MIG 和分数 GPU 分配优化 GPU 资源分配

Kubernetes 中的静态资源限制(例如 CPU、内存、GPU 数量)可能会导致过度配置或利用不足,对于推理等动态工作负载尤其如此。 AI/ML 选择正确的 GPU 很重要。对于低容量或尖峰工作负载,时间分片允许多个工作负载通过共享其计算资源共享单个 GPU,从而有可能提高效率并减少浪费。GPU 共享可以通过不同的选项实现:

  • 利用节点选择器/节点亲和力来影响调度:确保已配置的节点和 Pod 的调度符合工作负载(例如) GPUs karpenter.k8s.aws/instance-gpu-name: "a100"

  • 时间切片:调度工作负载以随着时间的推移共享 GPU 的计算资源,从而允许在不进行物理分区的情况下并发执行。这非常适合具有可变计算需求但可能缺乏内存隔离的工作负载。

  • 多实例 GPU (MIG):MIG 允许将单个 NVIDIA GPU 分成多个隔离实例,并支持 NVIDIA Ampere(例如 A100 GPU)、NVIDIA Hopper(例如 H100 GPU)和 NVIDIA Blackwell(例如 Blackwell)。 GPUs GPUs每个 MIG 实例都会获得专用的计算和内存资源,从而在多租户环境或需要资源保障的工作负载中实现资源共享,这使您可以优化 GPU 资源利用率,包括通过时间分段为具有不同批次大小的多个模型提供服务等场景。

  • GPU 分配:使用基于软件的调度将 GPU 的部分计算或内存分配给工作负载,从而为动态工作负载提供灵活性。作为 run: AI 平台的一部分,NVIDIA KAI 调度器通过允许 pod 请求一部分 GPU 资源来实现这一点。

要在 EKS 中启用这些功能,您可以部署 NVIDIA 设备插件,该插件 GPUs 作为可调度资源公开,并支持分时和 MIG。要了解更多信息,请参阅 Kubernetes GPUs 中的时间切片以及使用 N VIDIA 时间切片和加速实例在 Amazon EKS 上共享 GPU。 EC2

示例

例如,要使用 NVIDIA 设备插件启用时间切片,请执行以下操作:

apiVersion: v1 kind: ConfigMap metadata: name: nvidia-device-plugin-config namespace: kube-system data: config.yaml: | version: v1 sharing: timeSlicing: resources: - name: nvidia.com/gpu replicas: 4 # Allow 4 pods to share each GPU

示例

例如,要使用 KAI 调度器进行小部分 GPU 分配,请将其与 NVIDIA GPU 操作员一起部署,然后在 pod 规范中指定小部分 GPU 资源:

apiVersion: v1 kind: Pod metadata: name: fractional-gpu-pod-example annotations: gpu-fraction: "0.5" # Annotation for 50% GPU labels: runai/queue: "default" # Required queue assignment spec: containers: - name: ml-workload image: nvcr.io/nvidia/pytorch:25.04-py3 resources: limits: nvidia.com/gpu: 1 nodeSelector: nvidia.com/gpu: "true" schedulerName: kai-scheduler

节点弹性和训练 Job 管理

通过自动恢复实现节点运行状况检查

对于 Amazon EKS 上需要频繁进行节点间通信的分布式训练作业(例如跨多个节点的多 GPU 模型训练),GPU 或 EFA 故障等硬件问题可能会导致训练作业中断。这些中断可能导致训练进度损失和成本增加,对于依赖稳定硬件的长时间运行 AI/ML 的工作负载尤其如此。

为了帮助增强针对硬件故障(例如运行 GPU 工作负载的 EKS 集群中的 GPU 故障)的弹性,我们建议使用带自动修复功能的 EKS 节点监控代理Amazon SageMaker HyperPod。而带自动修复功能的 EKS 节点监控代理提供节点运行状况监控和使用标准 Kubernetes 机制进行自动修复等功能,同时 SageMaker HyperPod 还提供了针对性的弹性和专为大规模机器学习训练设计的其他功能,例如深度运行状况检查和自动任务恢复。

  • 带有节点自动修复功能的 EKS 节点监控代理通过读取日志和应用 NodeConditions(包括加速硬件的标准条件和特定条件)来持续监控节点的运行状况,以识别 GPU 或网络故障等问题。Ready当节点被认为不健康时,Node Auto Repair 会封锁该节点并将其替换为新节点。Pod 的重新调度和作业的重启依赖于标准的 Kubernetes 机制和作业的重启策略。

  • SageMaker HyperPod深度运行状况检查和运行状况监控代理会持续监控 GPU 和 Trainium 实例的运行状况。它专为 AI/ML 工作负载量身定制,使用标签(例如 node-health-status)来管理节点运行状况。当节点被认为运行状况不佳时, HyperPod 会触发自动更换故障硬件,例如。 GPUs默认情况下,它通过基本的运行状况检查来检测 EFA 的网络相关故障,并支持对中断的训练作业自动恢复,允许作业从最后一个检查点继续进行,从而最大限度地减少大规模机器学习任务的中断。

对于具有自动修复功能的 EKS 节点监控代理和使用 EFA 的 SageMaker HyperPod 集群,要监控特定于 EFA 的指标,例如远程直接内存访问 (RDMA) 错误和数据包丢失,请确保已安装 AW S EF A 驱动程序。此外,我们建议部署CloudWatch 可观察性插件或使用带有 Prometheus 的 DCGM Exporter 和 Grafana 等工具来监控 EFA、GPU 以及与其功能相关的特定指标。 SageMaker HyperPod

对中断敏感型工作负载禁用 Karpenter 整合

对于对中断敏感的工作负载,例如处理、大规模 AI/ML 预测任务或训练,我们建议调整 Karpenter 整合策略,以防止作业执行期间出现中断。Karpenter 的整合功能通过终止未充分利用的节点或用价格较低的替代节点来自动优化集群成本。但是,即使工作负载完全利用了 GPU,Karpenter 也可能会整合节点,前提是它发现了价格较低且大小适中、符合容器要求的实例类型,从而导致作业中断。

WhenEmptyOrUnderutilized整合策略可能会过早终止节点,从而导致更长的执行时间。例如,由于容器重新调度、数据重新加载,中断可能会延迟任务的恢复,这对于长时间运行的批量推理作业来说可能会付出高昂的代价。为了缓解这种情况,您可以将设置consolidationPolicyWhenEmpty并配置consolidateAfter持续时间(例如 1 小时),以便在工作负载高峰期间保留节点。例如:

disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m

这种方法可以改善尖峰批量推理工作负载和其他对中断敏感的作业(例如实时在线推理数据处理或模型训练)的 pod 启动延迟,在这些作业中,中断成本大于节省的计算成本。Karpenter NodePool 干扰预算是管理 Karpenter 中断的另一项功能。有了预算,您可以确保在所选时间点中断的节点不超过一定数量 NodePool 的节点。您还可以使用中断预算来防止所有节点在特定时间(例如高峰时段)中断。要了解更多信息,请参阅 Karpenter 整合文档

使用 ttlSecondsAfter已完成自动清理 Kubernetes 作业

我们建议在 Amazon EKS 中将 Kubernetes 任务设置ttlSecondsAfterFinished为自动删除已完成的任务对象。延迟的任务对象会消耗群集资源,例如 API 服务器内存,并且仪表板混乱(例如 Grafana、Amazon),从而使监控复杂化。 CloudWatch例如,将 TTL 设置为 1 小时可确保任务在完成后不久即被删除,从而保持集群整洁。有关更多详细信息,请参阅自动清理已完成的作业

为优先级较高的作业/工作负载配置低优先级作业抢占权

对于 Amazon EKS 上的混合优先级 AI/ML 工作负载,您可以配置低优先级任务抢占以确保优先级较高的任务(例如实时推断)及时获得资源。如果没有抢占权,低优先级工作负载(例如批处理推理、数据处理)、非批处理服务(例如后台任务、cron 作业)或 CPU/内存密集型作业(例如 Web 服务)可能会通过占用节点来延迟关键 pod。抢占允许 Kubernetes 在高优先级 Pod 需要资源时驱逐低优先级 Pod,从而确保在带有、或内存的节点上高效分配资源。 GPUs CPUs我们建议使用 Kubernetes PriorityClass 来分配优先级并控制驱PodDisruptionBudget逐行为。

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 100 --- spec: priorityClassName: low-priority

有关更多信息,请参阅 Kubernetes 优先级和抢占文档

应用程序扩展和性能

使用 Karpenter 或静态节点为机器学习工作负载量身定制计算容量

为了确保在 Amazon EKS 上为机器学习 (ML) 工作流程提供经济高效且响应迅速的计算容量,我们建议根据工作负载的特征和成本承诺量身定制节点配置策略。以下是两种需要考虑的方法:使用 Karpenter just-in-time 扩展和静态节点组以获得预留容量。

  • Just-in-time 像 Karpenter 这样的数据平面缩放器:对于具有可变计算需求的动态机器学习工作流程(例如,基于 GPU 的推理和基于 CPU 的绘图),我们建议使用像 Karpenter 这样的数据平面缩放器。 just-in-time

  • 将@@ 静态节点组用于可预测的工作负载:对于可预测的稳定状态机器学习工作负载或使用预留实例时,EKS 托管节点组可以帮助确保预留容量得到充分配置和利用,从而最大限度地节省开支。这种方法非常适合通过 RIs 或提交的特定实例类型 ODCRs。

示例

这是一个多元化 Karpenter 的示例 NodePool,它允许在 EC2 实例生成大于三的情况下启动 g Amazon 实例。

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inference spec: template: spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["g"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] - key: kubernetes.io/arch operator: In values: ["amd64"] taints: - key: nvidia.com/gpu effect: NoSchedule limits: cpu: "1000" memory: "4000Gi" nvidia.com/gpu: "10" *# Limit the total number of GPUs to 10 for the NodePool* disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m expireAfter: 720h

示例

使用静态节点组处理训练工作负载的示例:

apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ml-cluster region: us-west-2 managedNodeGroups: - name: gpu-node-group instanceType: p4d.24xlarge minSize: 2 maxSize: 2 desiredCapacity: 2 taints: - key: nvidia.com/gpu effect: NoSchedule

使用污点和容忍度来防止在加速实例上调度非加速工作负载

在 GPU 资源上调度非加速工作负载的计算效率不高,我们建议使用污点和容忍度来确保非加速工作负载 pod 不会调度到不合适的节点上。有关更多信息,请参阅 Kubernetes 文档

基于模型性能进行缩放

对于推理工作负载,我们建议使用 Kubernetes 事件驱动的自动扩展 (KEDA) 根据模型性能指标(例如推理请求或令牌吞吐量)进行扩展,并设置适当的冷却时间。静态扩展策略可能会过度配置资源或资源不足,从而影响成本和延迟。要了解更多信息,请参阅 KEDA 文档

用于高级 GPU 管理的动态资源分配

动态资源分配 (DRA) 代表了 Kubernetes GPU 资源管理的一项根本进步。DRA 超越了传统的设备插件限制,实现了复杂的 GPU 共享、拓扑感知和跨节点资源协调。DRA 在 Amazon EKS 1.33 版本中可用,它通过提供以下内容来解决 AI/ML 工作负载中的关键挑战:

  • 精细的 GPU 分配

  • 高级共享机制,例如多进程服务 (MPS) 和多实例 GPU (MIG)

  • 支持下一代硬件架构,包括 NVIDIA GB2 00 UltraClusters

传统的 GPU 分配 GPUs 被视为不透明的整数资源,导致严重的利用率不足(在生产集群中通常为 30-40%)。之所以发生这种情况,是因为 GPUs 即使只需要一小部分资源,工作负载也能获得对整体的独占访问权限。DRA 通过引入结构化的声明式分配来改变这种模型,让 Kubernetes 调度器能够完全了解硬件特征和工作负载要求。这可以实现明智的放置决策和高效的资源共享。

使用 DRA 代替 NVIDIA 设备插件的优势

NVIDIA 设备插件(从版本开始0.12.0)支持 GPU 共享机制,包括时间切片、MPS 和 MIG。但是,DRA 可以解决一些架构限制。

NVIDIA 设备插件限制

  • 静态配置:GPU 共享配置(时间切片副本和 MPS 设置)需要在整个集群范围内进行预配置。ConfigMaps这使得为不同的工作负载提供不同的共享策略变得困难。

  • 精细选择有限:虽然设备插件通过节点标签公开 GPU 特性,但工作负载无法在调度决策中动态请求特定的 GPU 配置(内存大小和计算能力)。

  • 没有跨节点资源协调:无法管理跨多个节点的分布式 GPU 资源,也无法表达复杂的拓扑要求,例如像 NVIDIA GB2 00 这样的系统的 NVLink 域名。

  • 调度器限制:Kubernetes 调度器将 GPU 资源视为不透明的整数,从而限制了其做出拓扑感知决策或处理复杂资源依赖关系的能力。

  • 配置复杂性:设置不同的共享策略需要多重ConfigMaps且谨慎的节点标记,这增加了操作的复杂性。

采用 DRA 的解决方案

  • 动态资源选择:DRA 允许工作负载在请求时指定详细要求(GPU 内存、驱动程序版本和特定属性)resourceclaims。这样可以实现更灵活的资源匹配。

  • 拓扑感知:通过结构化参数和设备选择器,DRA 可以处理复杂的需求,例如跨节点 GPU 通信和内存一致性互连。

  • 跨节点资源管理:computeDomains支持跨多个节点协调分布式 GPU 资源,这对于像 GB2 00 这样具有 IMEX 通道的系统至关重要。

  • 特定于工作负载的配置:每种配置都ResourceClaim指定了不同的共享策略和配置,允许对每个工作负载进行精细控制,而不是集群范围的设置。

  • 增强的调度程序集成:DRA 为调度器提供详细的设备信息,并允许根据硬件拓扑和资源特征做出更明智的放置决策。

重要提示:DRA 并不能完全取代 NVIDIA 设备插件。NVIDIA DRA 驱动程序与设备插件配合使用,可提供增强功能。设备插件继续处理基本的 GPU 发现和管理,而 DRA 则增加了高级分配和调度功能。

DRA 支持的实例及其功能

DRA 支持因 Amazon EC2 实例系列和 GPU 架构而异,如下表所示。

实例系列 GPU 类型 时间切片 MIG 支持 MPS 支持 IMEX 支持 使用案例

G5

NVIDIA A10G

推理和图形工作负载

G6

NVIDIA L4

AI 推理和视频处理

G6e

英伟达 L40S

训练、推理和图形

p4d/p4de

NVIDIA A100

支持

大规模培训和 HPC

P5

NVIDIA H100

支持

基础模型训练

P6

英伟达 B200

支持

十亿或万亿参数模型、分布式训练和推理

p6e

英伟达 GB2 00

支持

十亿或万亿参数模型、分布式训练和推理

以下是对表中每项功能的描述:

  • 时间分片:允许多个工作负载随着时间的推移共享 GPU 计算资源。

  • 多实例 GPU (MIG):用于创建隔离的 GPU 实例的硬件级分区。

  • 多进程服务 (MPS):允许在单个 GPU 上同时执行多个 CUDA 进程。

  • 节点间内存交换 (IMEX):00的跨节点内存一致性通信。 GB2 UltraClusters

其他资源

有关 Kubernetes DRA 和 NVIDIA DRA 驱动程序的更多信息,请参阅以下资源: GitHub

为高级 GPU 管理设置动态资源分配

以下主题介绍如何为高级 GPU 管理设置动态资源分配 (DRA)。

先决条件

在 Amazon EKS 上实施 DRA 之前,请确保您的环境满足以下要求。

集群配置
必需的组件
  • NVIDIA 设备插件版本0.17.1或更高版本

  • 英伟达 DRA 驱动程序版本25.3.0或更高版本

步骤 1:使用 eksctl 创建启用了 DRA 的节点组的集群

  1. 创建名为dra-eks-cluster.yaml:的集群配置文件

    --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-west-2 version: '1.33' managedNodeGroups: - name: gpu-dra-nodes amiFamily: AmazonLinux2023 instanceType: g6.12xlarge desiredCapacity: 2 minSize: 1 maxSize: 3 labels: node-type: "gpu-dra" nvidia.com/gpu.present: "true" taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule
  2. 创建集群:

    eksctl create cluster -f dra-eks-cluster.yaml

第 2 步:部署 NVIDIA 设备插件

部署 NVIDIA 设备插件以启用基本的 GPU 发现:

  1. 添加 NVIDIA 设备插件 Helm 存储库:

    helm repo add nvidia https://nvidia.github.io/k8s-device-plugin helm repo update
  2. 为设备插件创建自定义值:

    cat <<EOF > nvidia-device-plugin-values.yaml gfd: enabled: true nfd: enabled: true tolerations: - key: [nvidia.com/gpu](http://nvidia.com/gpu) operator: Exists effect: NoSchedule EOF
  3. 安装 NVIDIA 设备插件:

    helm install nvidia-device-plugin nvidia/nvidia-device-plugin \ --namespace nvidia-device-plugin \ --create-namespace \ --version v0.17.1 \ --values nvidia-device-plugin-values.yaml

第 3 步:部署 NVIDIA DRA 驱动程序 Helm

  1. 为 DRA 驱动程序创建dra-driver-values.yaml值文件:

    --- nvidiaDriverRoot: / gpuResourcesEnabledOverride: true resources: gpus: enabled: true computeDomains: enabled: true # Enable for GB200 IMEX support controller: tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule kubeletPlugin: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "nvidia.com/gpu.present" operator: In values: ["true"] tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  2. 添加 NVIDIA NGC Helm 存储库:

    helm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update
  3. 安装 NVIDIA DRA 驱动程序:

    helm install nvidia-dra-driver nvidia/nvidia-dra-driver-gpu \ --version="25.3.0-rc.2" \ --namespace nvidia-dra-driver \ --create-namespace \ --values dra-driver-values.yaml

步骤 4:验证 DRA 的安装

  1. 验证 DRA API 资源是否可用:

    kubectl api-resources | grep [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1)

    以下是预期的输出:

    deviceclasses [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false DeviceClass resourceclaims [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaim resourceclaimtemplates [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaimTemplate resourceslices [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false ResourceSlice
  2. 检查可用的设备类别:

    kubectl get deviceclasses

    以下是预期输出的示例:

    NAME AGE compute-domain-daemon.nvidia.com 4h39m compute-domain-default-channel.nvidia.com 4h39m gpu.nvidia.com 4h39m mig.nvidia.com 4h39m

    当新创建的 G6 GPU 实例加入启用了 DRA 的 Amazon EKS 集群时,会发生以下操作:

    • NVIDIA DRA 驱动程序会自动发现 A10G GPU 并在该节点resourceslices上创建两个 GPU。

    • gpu.nvidia.com切片注册物理 A10G GPU 设备及其规格(内存、计算能力等)。

    • 由于 A10G 不支持 MIG 分区,因此该compute-domain.nvidia.com切片会创建一个代表 GPU 整个计算环境的单个计算域。

    • 然后将resourceslices它们发布到 Kubernetes API 服务器,从而使 GPU 资源可用于调度。resourceclaims

      DRA 调度器现在可以智能地将此 GPU 分配给通过其请求 GPU 资源的 Podresourceclaimtemplates,与传统的设备插件方法相比,资源管理更加灵活。这会自动发生,无需人工干预。DRA 驱动程序完成资源发现和注册过程后,该节点即可用于 GPU 工作负载。

      当你运行以下命令时:

      kubectl get resourceslices

      以下是预期输出的示例:

      NAME NODE DRIVER POOL AGE ip-100-64-129-47.ec2.internal-compute-domain.nvidia.com-rwsts ip-100-64-129-47.ec2.internal compute-domain.nvidia.com ip-100-64-129-47.ec2.internal 35m ip-100-64-129-47.ec2.internal-gpu.nvidia.com-6kndg ip-100-64-129-47.ec2.internal gpu.nvidia.com ip-100-64-129-47.ec2.internal 35m

继续使用动态资源分配安排简单的 GPU 工作负载

使用动态资源分配安排简单的 GPU 工作负载

要使用动态资源分配 (DRA) 安排简单的 GPU 工作负载,请执行以下步骤。在继续操作之前,请确保你已经遵循了为高级 GPU 管理设置动态资源分配

  1. 使用名ResourceClaimTemplate为以下文件创建 GPU 分配的基本配置basic-gpu-claim-template.yaml

    --- apiVersion: v1 kind: Namespace metadata: name: gpu-test1 --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: namespace: gpu-test1 name: single-gpu spec: spec: devices: requests: - name: gpu deviceClassName: gpu.nvidia.com
  2. 应用模板:

    kubectl apply -f basic-gpu-claim-template.yaml
  3. 验证状态:

    kubectl get resourceclaimtemplates -n gpu-test1

    下面是示例输出:

    NAME AGE single-gpu 9m16s
  4. 创建一个使用的 PodResourceClaimTemplate,其文件名为basic-gpu-pod.yaml

    --- apiVersion: v1 kind: Pod metadata: namespace: gpu-test1 name: gpu-pod labels: app: pod spec: containers: - name: ctr0 image: ubuntu:22.04 command: ["bash", "-c"] args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"] resources: claims: - name: gpu0 resourceClaims: - name: gpu0 resourceClaimTemplateName: single-gpu nodeSelector: NodeGroupType: gpu-dra nvidia.com/gpu.present: "true" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"
  5. 应用并监控 Pod:

    kubectl apply -f basic-gpu-pod.yaml
  6. 检查 Pod 状态:

    kubectl get pod -n gpu-test1

    以下是预期输出示例:

    NAME READY STATUS RESTARTS AGE gpu-pod 1/1 Running 0 13m
  7. 检查ResourceClaim状态:

    kubectl get resourceclaims -n gpu-test1

    以下是预期输出示例:

    NAME STATE AGE gpu-pod-gpu0-l76cg allocated,reserved 9m6s
  8. 查看 Pod 日志以查看 GPU 信息:

    kubectl logs gpu-pod -n gpu-test1

    以下是预期输出示例:

    GPU 0: NVIDIA L4 (UUID: GPU-da7c24d7-c7e3-ed3b-418c-bcecc32af7c5)

继续阅读采用动态资源分配的 GPU 优化技术以了解使用 DRA 的更高级的 GPU 优化技术。

采用动态资源分配的 GPU 优化技术

现代 GPU 工作负载需要复杂的资源管理才能实现最佳利用率和成本效益。DRA 支持多种高级优化技术,可解决不同的用例和硬件功能:

这些技术可以显著提高资源利用率。Organizations报告说,通过优化的共享策略,GPU利用率从传统分配的30-40%提高到80-90%。技术的选择取决于工作负载特性、隔离要求和硬件功能。

通过时间切片优化 GPU 工作负载

时间分片使多个工作负载能够通过调度它们在同一个物理 GPU 上按顺序运行来共享 GPU 计算资源。它非常适合偶尔使用 GPU 的推理工作负载。

请执行以下步骤。

  1. 使用名为:的文件ResourceClaimTemplate为时间切片定义一个 timeslicing-claim-template.yaml

    --- apiVersion: v1 kind: Namespace metadata: name: timeslicing-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: timeslicing-gpu-template namespace: timeslicing-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: TimeSlicing
  2. 使用时间切片定义一个 Pod,文件名为:timeslicing-pod.yaml

    --- # Pod 1 - Inference workload apiVersion: v1 kind: Pod metadata: name: inference-pod-1 namespace: timeslicing-gpu labels: app: gpu-inference spec: restartPolicy: Never containers: - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 1 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate inference workload for i in range(20): x = torch.randn(1000, 1000).cuda() y = torch.mm(x, x.t()) print(f"Pod 1 - Iteration {i+1} completed at {time.strftime('%H:%M:%S')}") time.sleep(60) else: print("No GPU available!") time.sleep(5) resources: claims: - name: shared-gpu-claim resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule --- # Pod 2 - Training workload apiVersion: v1 kind: Pod metadata: name: training-pod-2 namespace: timeslicing-gpu labels: app: gpu-training spec: restartPolicy: Never containers: - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 2 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate training workload with heavier compute for i in range(15): x = torch.randn(2000, 2000).cuda() y = torch.mm(x, x.t()) loss = torch.sum(y) print(f"Pod 2 - Training step {i+1}, Loss: {loss.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(5) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim-2 resourceClaims: - name: shared-gpu-claim-2 resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. 应用模板和 Pod:

    kubectl apply -f timeslicing-claim-template.yaml kubectl apply -f timeslicing-pod.yaml
  4. 监控资源声明:

    kubectl get resourceclaims -n timeslicing-gpu -w

    下面是示例输出:

    NAME STATE AGE inference-pod-1-shared-gpu-claim-9p97x allocated,reserved 21s training-pod-2-shared-gpu-claim-2-qghnb pending 21s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb pending 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb allocated,reserved 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s

First Pod (inference-pod-1)

  • allocated,reserved

  • 含义:DRA 找到了可用的 GPU 并将其保留给这个 Pod

  • Pod 状态:立即开始运行

第二个吊舱 (training-pod-2)

  • pending

  • 含义:等待 DRA 在同一 GPU 上配置时间切片

  • Pod 状态:等待调度

  • 该州将从pendingallocated,reservedrunning

使用 MPS 优化 GPU 工作负

多进程服务 (MPS) 支持在单个 GPU 上并发执行多个 CUDA 上下文,隔离效果比时间切片更好。

请执行以下步骤。

  1. 使用名为mps-claim-template.yaml:的文件为 MPS 定义 ResourceClaimTemplate

    --- apiVersion: v1 kind: Namespace metadata: name: mps-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mps-gpu-template namespace: mps-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: MPS
  2. 使用 MPS 定义一个 Pod,文件名为mps-pod.yaml

    --- # Single Pod with Multiple Containers sharing GPU via MPS apiVersion: v1 kind: Pod metadata: name: mps-multi-container-pod namespace: mps-gpu labels: app: mps-demo spec: restartPolicy: Never containers: # Container 1 - Inference workload - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== INFERENCE CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create inference model model = nn.Sequential( nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 100) ).cuda() # Run inference for i in range(1, 999999): with torch.no_grad(): x = torch.randn(128, 1000).cuda() output = model(x) result = torch.sum(output) print(f"Inference Container PID {os.getpid()}: Batch {i}, Result: {result.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(2) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu # Container 2 - Training workload - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== TRAINING CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create training model model = nn.Sequential( nn.Linear(2000, 1000), nn.ReLU(), nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 10) ).cuda() criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Run training for epoch in range(1, 999999): x = torch.randn(64, 2000).cuda() target = torch.randn(64, 10).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, target) loss.backward() optimizer.step() print(f"Training Container PID {os.getpid()}: Epoch {epoch}, Loss: {loss.item():.4f} at {time.strftime('%H:%M:%S')}") time.sleep(3) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: mps-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. 应用模板并创建多个 MPS 容器:

    kubectl apply -f mps-claim-template.yaml kubectl apply -f mps-pod.yaml
  4. 监控资源声明:

    kubectl get resourceclaims -n mps-gpu -w

    下面是示例输出:

    NAME STATE AGE mps-multi-container-pod-shared-gpu-claim-2p9kx allocated,reserved 86s

此配置演示了使用 NVIDIA 多进程服务 (MPS) 通过动态资源分配 (DRA) 实现真正的 GPU 共享。与按顺序轮流使用 GPU 的工作负载的时间切片不同,MPS 允许两个容器在同一个物理 GPU 上同时运行。关键见解是,DRA MPS 共享需要在单个 Pod 中使用多个容器,而不是多个单独的 Pod。部署后,DRA 驱动程序会向 Pod 分配一个ResourceClaim,并自动配置 MPS 以允许推理容器和训练容器同时执行。

每个容器都有自己独立的 GPU 内存空间和计算资源,MPS 守护程序会协调对底层硬件的访问。您可以通过执行以下操作来验证它是否有效:

  • 勾选nvidia-smi,这会将两个容器显示为 M+C (MPS + Compute) 进程共享同一 GPU 设备。

  • 监控来自两个容器的日志,这将显示交错的时间戳,证明可以同时执行。

这种方法允许互补的工作负载高效共享昂贵的 GPU 硬件,而不是让单个进程充分利用它,从而最大限度地提高 GPU 利用率。

容器 1:inference-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:09:30 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.9 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA L4 On | 00000000:35:00.0 Off | 0 | | N/A 48C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 246MiB | +-----------------------------------------------------------------------------------------+
容器 2:training-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:16:00 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.9 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA L4 On | 00000000:35:00.0 Off | 0 | | N/A 51C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 314MiB | +-----------------------------------------------------------------------------------------+

使用多实例 GPU 优化 GPU 工作负载

多实例 GPU (MIG) 提供硬件级分区,使用专用的计算和内存资源创建隔离的 GPU 实例。

使用具有各种配置文件的动态 MIG 分区需要 NVIDIA GPU 操作员。NVIDIA GPU 操作员使用 MIG Manager 创建 MIG 配置文件并重新启动 P4D、P4De、P5、P6 等 GPU 实例以应用配置更改。GPU 操作员通过 MIG Manager 组件包括全面的 MIG 管理功能,该组件可监视节点标签的变化并自动应用相应的 MIG 配置。当请求更改 MIG 配置文件时,操作员会优雅地关闭所有 GPU 客户端,应用新的分区几何结构,然后重新启动受影响的服务。此过程需要重启 GPU 实例的节点,以确保干净的 GPU 状态转换。这就是为什么WITH–0—REBOOT=true在 MIG 管理器配置中启用对于成功部署 MIG 至关重要。

你需要 NVIDIA DRA 驱动程序和 NVIDIA GPU 操作员才能在 Amazon EKS 中使用 MIG。除此之外,你不需要 NVIDIA 设备插件和 DCGM Exporter,因为它们是 NVIDIA GPU 操作员的一部分。由于 EKS NVIDIA 预装 AMIs了 NVIDIA 驱动程序,因此我们禁用了 GPU 操作员部署驱动程序,以避免冲突并利用实例上已经存在的优化驱动程序。NVIDIA DRA 驱动程序处理 MIG 实例的动态资源分配,而 GPU 操作员则管理整个 GPU 生命周期。这包括 MIG 配置、设备插件功能、通过 DCGM 进行监控以及节点功能发现。这种集成方法为企业 GPU 管理提供了完整的解决方案,具有硬件级隔离和动态资源分配功能。

步骤 1:部署 NVIDIA GPU 操作员
  1. 添加 NVIDIA GPU 操作员存储库:

    helm repo add nvidia https://nvidia.github.io/gpu-operator helm repo update
  2. 创建gpu-operator-values.yaml文件:

    driver: enabled: false mig: strategy: mixed migManager: enabled: true env: - name: WITH_REBOOT value: "true" config: create: true name: custom-mig-parted-configs default: "all-disabled" data: config.yaml: |- version: v1 mig-configs: all-disabled: - devices: all mig-enabled: false # P4D profiles (A100 40GB) p4d-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.5gb": 2 "2g.10gb": 1 "3g.20gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false # P4DE profiles (A100 80GB) p4de-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.10gb": 2 "2g.20gb": 1 "3g.40gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false devicePlugin: enabled: true config: name: "" create: false default: "" toolkit: enabled: true nfd: enabled: true gfd: enabled: true dcgmExporter: enabled: true serviceMonitor: enabled: true interval: 15s honorLabels: false additionalLabels: release: kube-prometheus-stack nodeStatusExporter: enabled: false operator: defaultRuntime: containerd runtimeClass: nvidia resources: limits: cpu: 500m memory: 350Mi requests: cpu: 200m memory: 100Mi daemonsets: tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: accelerator: nvidia priorityClassName: system-node-critical
  3. 使用以下gpu-operator-values.yaml文件安装 GPU 操作员:

    helm install gpu-operator nvidia/gpu-operator \ --namespace gpu-operator \ --create-namespace \ --version v25.3.1 \ --values gpu-operator-values.yaml

    此 Helm 图表部署了以下组件和多个 MIG 配置文件:

    • 设备插件(GPU 资源调度)

    • DCGM 导出器(GPU 指标和监控)

    • 节点功能发现(NFD-硬件标签)

    • GPU 功能发现(GFD-GPU 专用标签)

    • MIG 管理器(多实例 GPU 分区)

    • 容器工具包(GPU 容器运行时)

    • 操作员控制器(生命周期管理)

  4. 验证部署 Pod:

    kubectl get pods -n gpu-operator

    下面是示例输出:

    NAME READY STATUS RESTARTS AGE gpu-feature-discovery-27rdq 1/1 Running 0 3h31m gpu-operator-555774698d-48brn 1/1 Running 0 4h8m nvidia-container-toolkit-daemonset-sxmh9 1/1 Running 1 (3h32m ago) 4h1m nvidia-cuda-validator-qb77g 0/1 Completed 0 3h31m nvidia-dcgm-exporter-cvzd7 1/1 Running 0 3h31m nvidia-device-plugin-daemonset-5ljm5 1/1 Running 0 3h31m nvidia-gpu-operator-node-feature-discovery-gc-67f66fc557-q5wkt 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-master-5d8ffddcsl6s6 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-6t4w7 1/1 Running 1 (3h32m ago) 4h1m nvidia-gpu-operator-node-feature-discovery-worker-9w7g8 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-k5fgs 1/1 Running 0 4h8m nvidia-mig-manager-zvf54 1/1 Running 1 (3h32m ago) 3h35m
  5. 创建带有 P4de 托管节点组的 Amazon EKS 集群,用于测试 MIG 示例:

    apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-east-1 version: '1.33' managedNodeGroups: # P4DE MIG Node Group with Capacity Block Reservation - name: p4de-mig-nodes amiFamily: AmazonLinux2023 instanceType: p4de.24xlarge # Capacity settings desiredCapacity: 0 minSize: 0 maxSize: 1 # Use specific subnet in us-east-1b for capacity reservation subnets: - us-east-1b # AL2023 NodeConfig for RAID0 local storage only nodeadmConfig: apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: instance: localStorage: strategy: RAID0 # Node labels for MIG configuration labels: nvidia.com/gpu.present: "true" nvidia.com/gpu.product: "A100-SXM4-80GB" nvidia.com/mig.config: "p4de-half-balanced" node-type: "p4de" vpc.amazonaws.com/efa.present: "true" accelerator: "nvidia" # Node taints taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule # EFA support efaEnabled: true # Placement group for high-performance networking placementGroup: groupName: p4de-placement-group strategy: cluster # Capacity Block Reservation (CBR) # Ensure CBR ID matches the subnet AZ with the Nodegroup subnet spot: false capacityReservation: capacityReservationTarget: capacityReservationId: "cr-abcdefghij" # Replace with your capacity reservation ID

    NVIDIA GPU Operator 使用添加到节点的标签,nvidia.com/mig.config: "p4de-half-balanced"并使用给定的配置文件对 GPU 进行分区。

  6. 登录实p4de例。

  7. 运行以下命令:

    nvidia-smi -L

    您应该会看到以下示例输出:

    [root@ip-100-64-173-145 bin]# nvidia-smi -L GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-ab52e33c-be48-38f2-119e-b62b9935925a) MIG 3g.40gb Device 0: (UUID: MIG-da972af8-a20a-5f51-849f-bc0439f7970e) MIG 2g.20gb Device 1: (UUID: MIG-7f9768b7-11a6-5de9-a8aa-e9c424400da4) MIG 1g.10gb Device 2: (UUID: MIG-498adad6-6cf7-53af-9d1a-10cfd1fa53b2) MIG 1g.10gb Device 3: (UUID: MIG-3f55ef65-1991-571a-ac50-0dbf50d80c5a) GPU 1: NVIDIA A100-SXM4-80GB (UUID: GPU-0eabeccc-7498-c282-0ac7-d3c09f6af0c8) MIG 3g.40gb Device 0: (UUID: MIG-80543849-ea3b-595b-b162-847568fe6e0e) MIG 2g.20gb Device 1: (UUID: MIG-3af1958f-fac4-59f1-8477-9f8d08c55029) MIG 1g.10gb Device 2: (UUID: MIG-401088d2-716f-527b-a970-b1fc7a4ac6b2) MIG 1g.10gb Device 3: (UUID: MIG-8c56c75e-5141-501c-8f43-8cf22f422569) GPU 2: NVIDIA A100-SXM4-80GB (UUID: GPU-1c7a1289-243f-7872-a35c-1d2d8af22dd0) MIG 3g.40gb Device 0: (UUID: MIG-e9b44486-09fc-591a-b904-0d378caf2276) MIG 2g.20gb Device 1: (UUID: MIG-ded93941-9f64-56a3-a9b1-a129c6edf6e4) MIG 1g.10gb Device 2: (UUID: MIG-6c317d83-a078-5c25-9fa3-c8308b379aa1) MIG 1g.10gb Device 3: (UUID: MIG-2b070d39-d4e9-5b11-bda6-e903372e3d08) GPU 3: NVIDIA A100-SXM4-80GB (UUID: GPU-9a6250e2-5c59-10b7-2da8-b61d8a937233) MIG 3g.40gb Device 0: (UUID: MIG-20e3cd87-7a57-5f1b-82e7-97b14ab1a5aa) MIG 2g.20gb Device 1: (UUID: MIG-04430354-1575-5b42-95f4-bda6901f1ace) MIG 1g.10gb Device 2: (UUID: MIG-d62ec8b6-e097-5e99-a60c-abf8eb906f91) MIG 1g.10gb Device 3: (UUID: MIG-fce20069-2baa-5dd4-988a-cead08348ada) GPU 4: NVIDIA A100-SXM4-80GB (UUID: GPU-5d09daf0-c2eb-75fd-3919-7ad8fafa5f86) GPU 5: NVIDIA A100-SXM4-80GB (UUID: GPU-99194e04-ab2a-b519-4793-81cb2e8e9179) GPU 6: NVIDIA A100-SXM4-80GB (UUID: GPU-c1a1910f-465a-e16f-5af1-c6aafe499cd6) GPU 7: NVIDIA A100-SXM4-80GB (UUID: GPU-c2cfafbc-fd6e-2679-e955-2a9e09377f78)

NVIDIA GPU Operator 已成功p4de-half-balanced将 MIG 配置文件应用于你的 P4DE 实例,按照配置创建了硬件级 GPU 分区。以下是分区的工作原理:

GPU 操作员应用了您的嵌入式 MIG 配置文件中的以下配置:

p4de-half-balanced: - devices: [0, 1, 2, 3] # First 4 GPUs: MIG enabled mig-enabled: true mig-devices: "1g.10gb": 2 # 2x small instances (10GB each) "2g.20gb": 1 # 1x medium instance (20GB) "3g.40gb": 1 # 1x large instance (40GB) - devices: [4, 5, 6, 7] # Last 4 GPUs: Full GPUs mig-enabled: false

根据你的nvidia-smi -L输出,GPU 操作员创建的内容如下:

  • 支持 Mig GPUs (0-3):硬件已分区

    • GPU 0:NVIDIA A100-SXM4 80GB

      • MIG 3g.40GB 设备 0 — 大型工作负载(40GB 内存,42) SMs

      • MIG 2g.20GB 设备 1 — 中型工作负载(20GB 内存,28) SMs

      • MIG 1g.10GB 设备 2 — 小型工作负载(10GB 内存,14) SMs

      • MIG 1g.10GB 设备 3 — 小型工作负载(10GB 内存,14) SMs

    • GPU 1:NVIDIA A100-SXM4 80GB

      • MIG 3g.40GB 设备 0 — 相同的分区布局

      • MIG 2g.20GB 设备 1

      • MIG 1g.10GB 设备 2

      • MIG 1g.10GB 设备 3

    • GPU 2 和 GPU 3 — 与 GPU 0 和 GPU 1 的模式相同

  • 完整 GPUs (4-7):没有 MIG 分区

    • GPU 4:NVIDIA A100 SXM4 -80GB — 全 80GB GPU

    • GPU 5:NVIDIA A100 SXM4 -80GB — 全 80GB GPU

    • GPU 6:NVIDIA A100 SXM4 -80GB — 全 80GB GPU

    • GPU 7:NVIDIA A100 SXM4 -80GB — 全 80GB GPU

NVIDIA GPU 操作员创建 MIG 分区后,NVIDIA DRA 驱动程序会自动检测这些硬件隔离的实例,并使其可用于在 Kubernetes 中进行动态资源分配。DRA 驱动程序会发现每个 MIG 实例及其特定配置文件(1g.10GB、2g.20GB、3g.40GB),并通过设备类别将其作为可调度资源公开。mig.nvidia.com

DRA 驱动程序持续监控 MIG 拓扑并维护所有 GPUs可用实例的清单。当 Pod 通过请求特定的 MIG 配置文件时ResourceClaimTemplate,DRA 驱动程序会智能地从任何可用的 GPU 中选择适当的 MIG 实例,从而实现真正的硬件级多租户。这种动态分配允许多个独立的工作负载在同一个物理 GPU 上同时运行,同时保持严格的资源边界和性能保证。

步骤 2:测试 MIG 资源分配

现在,让我们举一些示例,演示 DRA 如何动态地将 MIG 实例分配给不同的工作负载。部署resourceclaimtemplates并测试 pod,查看 DRA 驱动程序如何在可用的 MIG 分区中放置工作负载,从而允许多个容器在硬件级隔离下共享 GPU 资源。

  1. 创建mig-claim-template.yaml以包含 MIG:resourceclaimtemplates

    apiVersion: v1 kind: Namespace metadata: name: mig-gpu --- # Template for 3g.40gb MIG instance (Large training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-large-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-large deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '3g.40gb' --- # Template for 2g.20gb MIG instance (Medium training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-medium-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-medium deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '2g.20gb' --- # Template for 1g.10gb MIG instance (Small inference) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-small-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-small deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '1g.10gb'
  2. 应用三个模板:

    kubectl apply -f mig-claim-template.yaml
  3. 运行以下命令:

    kubectl get resourceclaimtemplates -n mig-gpu

    下面是示例输出:

    NAME AGE mig-large-template 71m mig-medium-template 71m mig-small-template 71m
  4. 创建mig-pod.yaml以安排多个作业以利用这一点resourceclaimtemplates

    --- # ConfigMap containing Python scripts for MIG pods apiVersion: v1 kind: ConfigMap metadata: name: mig-scripts-configmap namespace: mig-gpu data: large-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== LARGE TRAINING POD (3g.40gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Large model for 3g.40gb instance model = nn.Sequential( nn.Linear(2048, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Large batch for 3g.40gb x = torch.randn(256, 2048).cuda() y = torch.randint(0, 10, (256,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Large Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(3) print("Large training completed on 3g.40gb MIG instance") medium-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== MEDIUM TRAINING POD (2g.20gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Medium model for 2g.20gb instance model = nn.Sequential( nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Medium batch for 2g.20gb x = torch.randn(128, 1024).cuda() y = torch.randint(0, 10, (128,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Medium Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(4) print("Medium training completed on 2g.20gb MIG instance") small-inference-script.py: | import torch import torch.nn as nn import time import os print(f"=== SMALL INFERENCE POD (1g.10gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Small model for 1g.10gb instance model = nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Inference loop for i in range(200): with torch.no_grad(): # Small batch for 1g.10gb x = torch.randn(32, 512).cuda() output = model(x) prediction = torch.argmax(output, dim=1) if i % 20 == 0: print(f"Small Inference - Batch {i}, Predictions: {prediction[:5].tolist()}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(2) print("Small inference completed on 1g.10gb MIG instance") --- # Pod 1: Large training workload (3g.40gb) apiVersion: v1 kind: Pod metadata: name: mig-large-training-pod namespace: mig-gpu labels: app: mig-large-training workload-type: training spec: restartPolicy: Never containers: - name: large-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/large-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-large-claim resourceClaims: - name: mig-large-claim resourceClaimTemplateName: mig-large-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 2: Medium training workload (2g.20gb) - can run on SAME GPU as Pod 1 apiVersion: v1 kind: Pod metadata: name: mig-medium-training-pod namespace: mig-gpu labels: app: mig-medium-training workload-type: training spec: restartPolicy: Never containers: - name: medium-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/medium-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-medium-claim resourceClaims: - name: mig-medium-claim resourceClaimTemplateName: mig-medium-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 3: Small inference workload (1g.10gb) - can run on SAME GPU as Pod 1 & 2 apiVersion: v1 kind: Pod metadata: name: mig-small-inference-pod namespace: mig-gpu labels: app: mig-small-inference workload-type: inference spec: restartPolicy: Never containers: - name: small-inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/small-inference-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-small-claim resourceClaims: - name: mig-small-claim resourceClaimTemplateName: mig-small-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755
  5. 应用这个规范,它应该部署三个 Pod:

    kubctl apply -f mig-pod.yaml

    这些 Pod 应由 DRA 驱动程序调度。

  6. 查看 DRA 驱动程序 Pod 日志,您将看到类似于以下内容的输出:

    I0717 21:50:22.925811 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:22.932499 1 driver.go:129] Returning newly prepared devices for claim '933e9c72-6fd6-49c5-933c-a896407dc6d1': [&Device{RequestNames:[mig-large],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-0-mig-9-4-4,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-0-mig-9-4-4**],}] I0717 21:50:23.186472 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.191226 1 driver.go:129] Returning newly prepared devices for claim '61e5ddd2-8c2e-4c19-93ae-d317fecb44a4': [&Device{RequestNames:[mig-medium],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-2-mig-14-0-2,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-2-mig-14-0-2**],}] I0717 21:50:23.450024 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.455991 1 driver.go:129] Returning newly prepared devices for claim '1eda9b2c-2ea6-401e-96d0-90e9b3c111b5': [&Device{RequestNames:[mig-small],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-1-mig-19-2-1,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-1-mig-19-2-1**],}]
  7. 验证resourceclaims以查看 Pod 状态:

    kubectl get resourceclaims -n mig-gpu -w

    下面是示例输出:

    NAME STATE AGE mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 allocated,reserved 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 allocated,reserved 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 allocated,reserved 0s

    如你所见,所有 Pod 都从 “待处理” 移至 allocated,reserved DRA 驱动程序。

  8. nvidia-smi从节点运行。你会注意到有三个 Python 处理器正在运行:

    root@ip-100-64-173-145 bin]# nvidia-smi +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.8 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA A100-SXM4-80GB On | 00000000:10:1C.0 Off | On | | N/A 63C P0 127W / 400W | 569MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 1 NVIDIA A100-SXM4-80GB On | 00000000:10:1D.0 Off | On | | N/A 56C P0 121W / 400W | 374MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 2 NVIDIA A100-SXM4-80GB On | 00000000:20:1C.0 Off | On | | N/A 63C P0 128W / 400W | 467MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 3 NVIDIA A100-SXM4-80GB On | 00000000:20:1D.0 Off | On | | N/A 57C P0 118W / 400W | 249MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 4 NVIDIA A100-SXM4-80GB On | 00000000:90:1C.0 Off | 0 | | N/A 51C P0 77W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 5 NVIDIA A100-SXM4-80GB On | 00000000:90:1D.0 Off | 0 | | N/A 46C P0 69W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 6 NVIDIA A100-SXM4-80GB On | 00000000:A0:1C.0 Off | 0 | | N/A 52C P0 74W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 7 NVIDIA A100-SXM4-80GB On | 00000000:A0:1D.0 Off | 0 | | N/A 47C P0 72W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | MIG devices: | +------------------+----------------------------------+-----------+-----------------------+ | GPU GI CI MIG | Memory-Usage | Vol| Shared | | ID ID Dev | BAR1-Usage | SM Unc| CE ENC DEC OFA JPG | | | | ECC| | |==================+==================================+===========+=======================| | 0 2 0 0 | 428MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 2MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 3 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 9 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 10 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 13 0 2 | 161MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 2MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 5 0 1 | 289MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 2MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| **| 0 2 0 64080 C python 312MiB | | 1 13 0 64085 C python 118MiB | | 2 5 0 64073 C python 210MiB |** +-----------------------------------------------------------------------------------------+

使用 GB2 00 个 P6e 实例通过 IMEX 优化 GPU 工作负载

IMEX(节点间内存交换)支持在 NVIDIA 00 上进行分布式训练的节点间内存一致性通信。 GB2 UltraClusters

请执行以下步骤。

  1. 使用名ComputeDomainimex-compute-domain.yaml为以下的文件为多节点训练定义一个

    apiVersion: resource.nvidia.com/v1beta1 kind: ComputeDomain metadata: name: distributed-training-domain namespace: default spec: numNodes: 2 channel: resourceClaimTemplate: name: imex-channel-template
  2. 使用 IMEX 通道定义一个 Pod,文件名为:imex-pod.yaml

    apiVersion: v1 kind: Pod metadata: name: imex-distributed-training namespace: default labels: app: imex-training spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: nvidia.com/gpu.clique operator: Exists containers: - name: distributed-training image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["bash", "-c"] args: - | echo "=== IMEX Channel Verification ===" ls -la /dev/nvidia-caps-imex-channels/ echo "" echo "=== GPU Information ===" nvidia-smi echo "" echo "=== NCCL Test (if available) ===" python -c " import torch import torch.distributed as dist import os print(f'CUDA available: {torch.cuda.is_available()}') print(f'CUDA device count: {torch.cuda.device_count()}') if torch.cuda.is_available(): for i in range(torch.cuda.device_count()): print(f'GPU {i}: {torch.cuda.get_device_name(i)}') # Check for IMEX environment variables imex_vars = [k for k in os.environ.keys() if 'IMEX' in k or 'NVLINK' in k] if imex_vars: print('IMEX Environment Variables:') for var in imex_vars: print(f' {var}={os.environ[var]}') print('IMEX channel verification completed') " # Keep container running for inspection sleep 3600 resources: claims: - name: imex-channel-0 - name: imex-channel-1 resourceClaims: - name: imex-channel-0 resourceClaimTemplateName: imex-channel-template - name: imex-channel-1 resourceClaimTemplateName: imex-channel-template tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
    注意

    这需要 P6e GB2 00 实例。

  3. 通过应用ComputeDomain和模板部署 IMEX:

    kubectl apply -f imex-claim-template.yaml kubectl apply -f imex-compute-domain.yaml kubectl apply -f imex-pod.yaml
  4. 检查ComputeDomain状态。

    kubectl get computedomain distributed-training-domain
  5. 监控 IMEX 守护程序的部署。

    kubectl get pods -n nvidia-dra-driver -l [resource.nvidia.com/computeDomain](http://resource.nvidia.com/computeDomain)
  6. 在 Pod 中查看 IMEX 频道:

    kubectl exec imex-distributed-training -- ls -la /dev/nvidia-caps-imex-channels/
  7. 查看 Pod 日志:

    kubectl logs imex-distributed-training

    以下是预期输出的示例:

    === IMEX Channel Verification === total 0 drwxr-xr-x. 2 root root 80 Jul 8 10:45 . drwxr-xr-x. 6 root root 380 Jul 8 10:45 .. crw-rw-rw-. 1 root root 241, 0 Jul 8 10:45 channel0 crw-rw-rw-. 1 root root 241, 1 Jul 8 10:45 channel1

有关更多信息,请参阅上的 NVIDIA 示例 GitHub。