View a markdown version of this page

计算和自动缩放 - Amazon EKS

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

计算和自动缩放

提示

注册参加即将举行的亚马逊 EKS AI/ML 研讨会。

GPU 资源优化和成本管理

使用 Well-Known 标签安排具有 GPU 要求的工作负载

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

示例

例如,在使用 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 设备插件公开 GPU

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

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

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

如果您决定改用 EKS Accelerated AMI 和 NVIDIA GPU 运营商来管理 NVIDIA Kubernetes 设备插件等组件,请注意按照英伟达 GPU 驱动程序和 NVIDIA 容器工具包 NVIDIA 文档禁用对 NVIDIA GPU 驱动程序和 Pre-Installed NVIDIA 容器工具包的管理。

要验证 NVIDIA 设备插件是否处于活动状态以及 GPU 已正确暴露,请运行:

kubectl describe node | grep nvidia.com/gpu

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

使用许多不同的 EC2 实例类型

如本Kubernetes 数据平面节所述,尽可能多地使用不同的 EC2 实例类型是 Amazon EKS 可扩展性的重要最佳实践。此建议也适用于具有加速硬件(例如 GPU)的实例。如果您创建的集群仅使用一种实例类型,并尝试将节点数量扩展到该区域的容量以外,则可能会收到容量不足错误 (ICE),表明没有可用实例。在任意分散 AI/ML 工作负载之前,了解工作负载的独特特征非常重要。使用 EC2 Instance Type Explorer 工具查看可用实例类型,生成符合您特定计算要求的实例类型列表,避免任意限制可在集群中使用的实例类型。

加速计算实例以不同的购买模式提供,以适应短期、中期和稳态工作负载。对于短期、灵活且容错的工作负载,如果您希望避免进行预留,请考虑竞价型实例。容量区块、 On-Demand 实例和储蓄计划允许您为中长期工作负载预置加速计算实例。为了增加成功访问首选购买选项中所需容量的几率,建议使用不同的实例类型和可用区域列表。或者,如果您遇到特定购买模式的 ICE,请重试使用其他型号。

示例以下示例显示如何使 Karpenter NodePool 能够预置大于第 3 代(例如 p3)的 G 和 P 实例。要了解更多信息,请参阅EKS 可扩展性最佳实践一节。

- key: karpenter.k8s.aws/instance-category operator: In values: ["g", "p"] # Diversifies across G-series and P-series - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] # Selects instance generations greater than 3

有关将竞价型实例用于 GPU 的详细信息,请参阅下文 “考虑将亚马逊 EC2 竞价型实例用于 GPU 和 Karpenter”。

考虑将适用于 GPU 的 Amazon EC2 竞价型实例与 Karpenter 配合使用

Amazon EC2 竞价型实例使您可以利用 AWS 云中未使用的 EC2 容量,与 On-Demand 价格相比,可享受高达 90% 的折扣。当 EC2 需要恢复容量时,Amazon EC2 竞价型实例可以在通知两分钟后中断。有关更多信息,请参阅 Amazon EC2 用户指南中的竞价型实例。Amazon EC2 Spot 是容错、无状态和灵活(时间和实例类型)工作负载的绝佳选择。要详细了解何时使用竞价型实例,请参阅 EC2 竞价型实例最佳实践。如果是,您也可以将竞价型实例用于 AI/ML 工作负载 Spot-friendly。

使用案例

Spot-friendly 工作负载可以是大数据、容器化工作负载 CI/CD、无状态 Web 服务器、高性能计算 (HPC) 和渲染工作负载。竞价型实例不适用于实例节点之间不灵活、有状态、不容错或紧密耦合的工作负载(例如,具有并行进程在计算上高度依赖的工作负载,需要持续的节点间通信,例如计算流体动力学等 MPI-based 高性能计算应用程序或具有复杂相互依赖关系的分布式数据库)。以下是我们推荐的具体用例(排名不分先后):

  • Real-time 在线推理:使用竞价型实例对实时推理工作负载进行成本优化的扩展,前提是您的工作负载适合现场使用。换句话说,推理时间要么少于两分钟,应用程序可以容错中断,并且可以在不同的实例类型上运行。通过实例多样性(例如,跨多个实例类型和可用区)或预留来确保高可用性,同时实施应用程序级容错以处理潜在的 Spot 中断。

  • Hyper-parameter 优化:使用竞价型实例有机会地运行探索性调整任务,因为可以容忍中断而不会造成重大损失,尤其是在短期实验中。

  • 数据增强:使用竞价型实例执行数据预处理和增强任务,如果中断,这些任务可以从检查点重新启动,这使其成为实现竞价变量可用性的理想选择。

  • Fine-tuning 模型:使用竞价型实例通过强大的检查点机制进行微调,从上次保存的状态恢复,从而最大限度地减少实例中断的影响。

  • 批量推理:使用竞价型实例以非实时方式处理大批量离线推理请求,任务可以暂停和恢复,从而与 Spot 节省的成本保持最佳一致,并通过重试或分散来处理潜在的中断。

  • 机会主义训练子集:将竞价型实例用于边缘或实验性训练工作负载(例如,低于 1000 万个参数的较小模型),在这些工作负载中,中断是可以接受的,并且可以应用效率优化,例如跨实例类型或区域的多样化——但由于潜在的中断,不建议将其用于生产规模的训练。

注意事项

要在 Amazon EKS 上使用竞价型实例处理加速工作负载,有许多关键注意事项(排名不分先后):

  • 使用 Karpenter 管理启用了高级整合的竞价型实例。通过指定 karpenter。sh/capacity-在您的 Karpenter 中键入 “竞价” NodePool,Karpenter 将在默认情况下预置竞价型实例,无需任何额外配置。但是,要启用高级 Spot-to-Spot 整合,将未充分利用的竞价节点替换为价格较低的竞价节点,您需要通过在 Karpenter 控制器参数中设置--feature-gates SpotToSpotConsolidation =true 或通过 FEATURE_GATES 环境变量来启用功能门。 SpotToSpotConsolidation Karpenter 使用价格容量优化分配策略来配置 EC2 实例。根据 NodePool 要求和容器组限制,Karpenter 将不可调度的容器打包起来,并将各种不同的实例类型发送到亚马逊 EC2 队列 API。https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-fleet-request-type.html您可以使用 EC2 实例类型浏览器工具生成符合您的特定计算要求的实例类型列表。

  • 确保工作负载是无状态的、容错的和灵活的。工作负载必须是无状态的、容错的,并且在大小方面 instance/GPU 必须灵活。这样可以在竞价中断后无缝恢复,而实例灵活性使您有可能在竞价上停留更长时间。通过将 settings.InterruptionQueue Helm 值配置为 AWS SQS 队列的名称,在 Karpenter 中启用竞价中断处理,以捕获竞价中断事件。例如,通过 Helm 安装时,使用--set “settings.inruptionQueue=$ {CLUSTER_NAME}”。要查看示例,请参阅 Karpenter https://karpenter.sh/docs/getting-started/getting-started-with-karpenter/ 入门指南。当 Karpenter 注意到 Spot 中断事件时,它会在中断事件发生之前自动封锁、污染、排空和终止节点,以最大限度地延长 Pod 的终止宽限期。同时,Karpenter 将立即启动一个新节点,以便尽快准备就绪。

  • 避免过度限制实例类型选择。您应尽可能避免限制实例类型。通过不限制实例类型,更有可能以更低的成本以更低的竞价型实例中断频率大规模获取竞价容量。例如,避免仅限于特定类型(例如 g5.xlarge)。考虑使用诸如 karpenter.k8s 之类的密钥指定一组不同的实例类别和代。aws/instance-category 和 karpenter.k8s。aws/instance-世代。Karpenter 可以更轻松地在多种实例类型和可用区 (AZ) 之间实现按需和竞价型实例容量的多样化。此外,如果您的 AI/ML 工作负载需要特定或有限数量的加速器,但在不同区域之间可以灵活使用 Spot Placement Score,在启动前动态确定部署工作负载的最佳区域。

  • 扩大 NodePool 要求,以包括更多类似的 EC2 实例系列。每个竞价型实例池均由特定可用区 (AZ) 中特定实例类型的未使用的 EC2 实例容量组成。当 Karpenter 尝试配置新节点时,它会选择符合要求 NodePool的实例类型。如果在任何 AZ 中都没有兼容的实例类型具有竞价容量,则配置将失败。为避免此问题,允许 NVIDIA 提供跨大小和可用区 (AZ) 的更广泛的 g 系列实例(第 4 代或更高版本),同时考虑 GPU 内存或光线追踪等硬件需求。由于实例可以是不同的类型,因此您需要确保您的工作负载能够在每种类型上运行,并且获得的性能满足您的需求。

  • 利用一个区域中的所有可用区。可用容量因可用区 (AZ) 而异,特定实例类型可能在一个可用区不可用,但在另一个可用区充足。实例类型和可用区的每种独特组合构成一个单独的竞价容量池。通过在 Karpenter NodePool 要求范围内请求区域内所有可用区的容量,您可以有效地同时搜索更多池。这最大化了竞价容量池的数量,因此增加了获取 Spot 容量的可能性。要实现这一点,在你的 NodePool 配置中,要么省略 topology.kubernetes。io/zone 密钥完全允许 Karpenter 从该地区的所有可用区域中进行选择,或者使用运算符:In 明确列出可用区并提供值(例如 us-west-2a)。

  • 考虑使用竞价放置分数 (SPS) 来了解使用竞价型实例成功访问所需容量的可能性竞价投放分数 (SPS) 是一种提供分数的工具,可帮助您评估竞价请求成功的可能性。使用 SPS 时,您首先指定竞价型实例的计算要求,然后 Amazon EC2 返回竞价请求可能成功的前 10 个区域或可用区 (AZ)。区域和可用区的评分范围为 1 至 10。分数为 10 表示您的竞价请求极有可能,但不能保证成功。分数为 1 则表示您的竞价请求几乎没有成功的可能。即使是不同的区域或可用区,也可能会返回相同的分数。要了解更多信息,请参阅在 AWS 上构建竞价排名分数跟踪仪表板的指南。由于竞价容量一直在波动,SPS 将帮助您确定哪种实例类型、可用区和区域组合最适合您的工作负载限制(即灵活性、性能、大小等)。如果您的 AI/ML 工作负载需要特定或有限数量的加速器,但在不同区域之间可以灵活使用 Spot placement score,在启动前动态确定部署工作负载的最佳区域。为了帮助您自动找出获取 Spot 容量的可能性,我们为构建 SPS 跟踪仪表板提供了指导。该解决方案使用 YAML 配置监控一段时间内的 SPS 分数,以实现多样化设置(例如,包括 GPU 在内的实例要求),存储指标 CloudWatch,并提供仪表板来比较配置。为每个工作负载定义仪表板以评估 vCPU、内存和 GPU 需求,确保 EKS 集群的最佳设置,包括考虑使用其他 AWS 区域。要了解更多信息,请参阅 Spot 投放分数的工作原理

  • 优雅地处理 Spot 中断和测试。对于终止时间超过两分钟的 Pod,旧节点将在重新调度 Pod 之前中断,这可能会影响工作负载的可用性。在设计应用程序时考虑两分钟的 Spot 中断通知,在长时间运行的应用程序(例如,将进度保存到 Amazon S3 等永久存储)中断后恢复运行,延长 Pod 规格中的终止时间GracePeriodSeconds (默认为 30 秒)以留出更多时间进行正常关闭,并在应用程序中使用 PreStop 生命周期挂钩 and/or SIGTERM 信号处理中断,以实现清理、状态保存和连接关闭等平稳关闭活动。对于实时工作负载,在这些工作负载中,扩展时间很重要,而工作负载需要超过两分钟才能为流量做好准备,请考虑通过审查和应用程序扩展和性能最佳实践来优化容器启动仓储服务和机器学习模型加载时间。要测试替代节点,请使用 AWS 故障注入服务 (FIS) 来模拟 Spot 中断。

除了这些核心竞价最佳实践外,在亚马逊 EKS 上管理 GPU 工作负载时,还要考虑这些因素。与 CPU-based 工作负载不同,GPU 工作负载对硬件细节特别敏感,例如 GPU 功能和可用 GPU 内存。GPU 工作负载可能会受到它们可以使用的实例类型的限制,与 CPU 相比,可用的选项更少。首先,评估您的工作负载是否具有实例灵活性。如果您不知道工作负载可以使用多少种实例类型,请分别对其进行测试以确保兼容性和功能。确定您可以灵活地实现多元化,同时确认多样化可以保持工作负载正常运行,并了解任何性能影响(例如,对吞吐量或完成时间)。作为分散工作负载的一部分,请考虑以下几点:

  • 查看 CUDA 和框架兼容性。您的 GPU 工作负载可能会针对特定的硬件、GPU 类型(例如,p3 中的 V100 与 p4 中的 A100)进行优化,或者针对特定 CUDA 版本的库编写 TensorFlow,因此请务必查看工作负载的兼容性。这种兼容性对于防止运行时错误、崩溃、GPU 加速故障(例如,CUDA 版本与类似 PyTorch 或 TensorFlow 可能阻止执行的框架不匹配)或利用精度等 FP16/INT8 硬件功能的能力至关重要。

  • GPU 内存。请务必使用 DCGM Exporter 等工具评估模型的内存需求并分析模型在运行期间的内存使用情况,并在众所周知的标签(如 karpenter.k8s)中设置实例类型所需的最低 GPU 内存。aws/instance-gpu-内存。GPU VRAM 因实例类型而异(例如,NVIDIA T4 有 16GB,A10G 有 24GB,V100 有 16-32GB),机器学习模型(例如大型语言模型)可能会超过可用内存,从而导致内存不足 (OOM) 错误或崩溃。对于 EKS 中的竞价型实例,这可能会限制分散投资。例如,如果您的模型不合适,则不能包括低vRAM类型,这可能会限制对容量池的访问并增加中断风险。请注意,对于单 GPU、单节点推断(例如,调度在同一个节点上的多个 Pod 以利用其 GPU 资源),这可能会限制多样化,因为您只能在竞价配置中包含具有充足 VRAM 的实例类型。

  • Floating-point 精度和性能。并非所有 Nvidia GPU 架构都具有相同的浮点精度(例如, FP16/INT8)。评估工作负载所需的核心类型 (CUDA/Tensor/RT) 性能和浮点精度。在价格更低、性能更差的 GPU 上运行并不意味着它更好,因此可以考虑根据在特定时间范围内完成的工作来评估性能,以了解多元化的影响。

场景:实时推理工作负载的多样化

对于竞价型实例上的实时在线推理工作负载,您可以配置 Karpenter NodePool ,使其在兼容的 GPU 实例系列和代之间实现多样化。这种方法通过从多个竞价池中提取资源来确保高可用性,同时通过 GPU 功能、内存和架构的限制来保持性能。它支持在实例容量受限时使用替代方案,从而最大限度地减少中断并优化推理延迟。此示例 NodePool 指出,使用大于 3 的 g 和 p 系列实例,它们的 GPU 内存超过 20GB。

示例

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inference-spot spec: template: metadata: labels: role: gpu-spot-worker spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot"] # Use Spot Instances - key: karpenter.k8s.aws/instance-category operator: In values: ["g", "p"] # Diversifies across G-series and P-series - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] # Selects instance generations greater than 3 - key: kubernetes.io/arch operator: In values: ["amd64"] # Specifies AMD64 architecture, compatible with NVIDIA GPUs - key: karpenter.k8s.aws/instance-gpu-memory operator: Gt values: ["20480"] # Ensures more than 20GB (20480 MiB) total GPU memory taints: - key: nvidia.com/gpu effect: NoSchedule nodeClassRef: name: gpu-inference-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: 100 memory: 100Gi disruption: consolidationPolicy: WhenEmptyOrUnderutilized consolidateAfter: 5m # Enables consolidation of underutilized nodes after 5 minutes

为长时间运行的训练作业实施检查点

Checkpointing 是一种容错技术,它涉及定期保存进程的状态,允许它从上次保存的点恢复,以防出现中断。在机器学习中,它通常与训练相关,在这种训练中,长时间运行的任务可以保存模型权重和优化器状态,以便在出现故障(例如硬件问题或竞价型实例中断)后恢复训练。

在训练期间,您可以使用检查点保存机器学习 (ML) 模型的状态。检查点是模型的快照,可以通过机器学习框架的回调函数进行配置。您可以使用保存的检查点,根据上次保存的检查点重新启动训练作业。使用检查点,由于训练作业或实例意外中断,您可以将模型快照保存在训练中。这使您将来可以从检查点恢复模型的训练。除了实施节点弹性系统外,我们还建议实施检查点以减轻中断的影响,包括由硬件故障或 Amazon EC2 竞价型实例中断造成的中断的影响。

如果没有检查点,中断会导致计算时间浪费和进度损失,这对于长时间运行的训练任务来说代价高昂。Checkpointing 允许作业定期保存其状态(例如模型权重和优化器状态),并在中断后从最后一个检查点(上次处理的批次)恢复。要实现检查点,请将您的应用程序设计为大批量处理数据,并在训练任务进行期间通过适用于 Amazon S3 CSI 驱动程序的 Mountpoint 将中间结果保存到永久存储空间中,例如 Amazon S3 存储桶。

使用案例

Checkpointing 在特定场景中特别有用,可以平衡容错能力和性能开销。在以下情况下考虑使用检查点:

  • 作业持续时间超过几个小时:对于长时间运行的训练作业(例如,小型模型大于1-2小时,或 days/weeks 具有数十亿参数的大型基础模型),中断造成的进度损失代价高昂。较短的工作时间可能不足以证明 I/O 开销是合理的。

  • 对于竞价型实例或硬件故障:在容易出现中断的环境中,例如 EC2 Spot(通知 2 分钟)或硬件故障(例如 GPU 内存错误),检查点支持快速恢复,这使得 Spot 在容错工作负载中节省成本是可行的。

  • 大规模分布式训练:对于带有加速器(例如 >100 GPU) hundreds/thousands 的设置,其平均故障间隔时间随规模线性缩短。用于 model/data 并行处理以处理并发检查点访问并避免完全重启。

  • Large-scale 资源需求高的模型:在 PB 级的 LLM 训练中,由于集群规模,故障是不可避免的;分层方法(瞬态每 5-30 分钟快速本地化一次,对于重大故障,每小时持续一次)可以优化恢复时间和效率。

使用 ML 容量块来保证 P 和 Trainium 实例的容量

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

要为 Amazon EKS 上的 AI/ML 工作负载预留 GPU 以获得可预测的容量保障,我们建议将机器学习容量块用于短期或利用On-Demand 容量预留 (ODCR) 实现通用容量保障。

  • ODCR 允许您在特定可用区预留 EC2 实例容量(例如 G5 或 p5 等 GPU 实例)一段时间,从而确保可用性,即使在需求旺盛时也是如此。ODCR 没有长期承诺,但您需要为预留容量支付 On-Demand 费率,无论是已用容量还是空闲容量。在 EKS 中,Karpenter 托管节点组等节点类型支持 ODCR。要在 Karpenter 中优先考虑 ODCR,请将配置为使用该字段 NodeClass 。capacityReservationSelectorTerms请参阅 Karpenter 文档。 NodePools

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

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

考虑一下 On-Demand G Amazon EC2 实例的 Amazon EC2 竞价或 On-Demand 容量预留 (ODCR)

对于 G Amazon EC2 实例 On-Demand,请考虑与亚马逊 EC2 竞价型实例和 On-Demand 容量预留不同的购买选项。ODCR 允许您在特定可用区预留 EC2 实例容量一段时间,即使在需求旺盛时也能确保可用性。与仅适用于 P 和 Trainium 实例的 ML 容量块不同,ODCR 可用于更广泛的实例类型,包括 G 实例,使其适用于需要不同 GPU 功能(例如推理或图形)的工作负载。使用 Amazon EC2 竞价型实例时,能够在不同的实例类型、大小和可用区之间进行多样化是延长在竞价型上停留时间的关键。

ODCR 没有长期承诺,但您需要为预留容量支付 On-Demand 费率,无论是已用容量还是空闲容量。可以创建 ODCR 以供立即使用或计划在将来使用,从而为容量规划提供了灵活性。在亚马逊 EKS 中,Karpenter 托管节点组等节点类型支持 ODCR。要在 Karpenter 中优先考虑 ODCR,请将配置为使用该字段 NodeClass 。capacityReservationSelectorTerms请参阅 Karpenter 文档。 NodePools 有关创建 ODCR 的更多信息,包括 CLI 命令,请参阅On-Demand 容量预留入门。

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

选择适当的加速实例和大小对于在 Amazon EKS 上优化机器学习工作负载的性能和成本至关重要。例如,不同的 GPU 实例系列具有不同的性能和功能,例如 GPU 内存。为了帮助您选择性价比最高的选项,请在 EC2 实例类型页面的加速计算下查看可用的 GPU 实例。评估多种实例类型和大小,找到最适合您的特定工作负载要求的实例类型和大小。考虑 GPU 数量、内存和网络性能等因素。通过仔细选择正确的 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 实例。

使用 Time-Slicing、MIG 和部分 GPU 分配优化 GPU 资源分配

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

  • 利用节点选择器/节点亲和力来影响调度:确保为工作负载配置的节点和 Pod 调度在相应的 GPU 上(例如,)karpenter.k8s.aws/instance-gpu-name: "a100"

  • Time-Slicing:安排工作负载以在一段时间内共享 GPU 的计算资源,允许在不进行物理分区的情况下并行执行。这对于具有可变计算需求但可能缺乏内存隔离的工作负载非常理想。

  • Multi-Instance GPU (MIG):MIG 允许将单个 NVIDIA GPU 分区为多个独立实例,并支持 NVIDIA Ampere(例如 A100 GPU)、NVIDIA Hopper(例如 H100 GPU)和 NVIDIA Blackwell(例如 Blackwell GPU)GPU。每个 MIG 实例均接收专用的计算和内存资源,从而在多租户环境或需要资源保障的工作负载中实现资源共享,这使您可以优化 GPU 资源利用率,包括通过时间切片为不同批量大小的多个模型提供服务等方案。

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

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

示例

例如,要使用 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

节点弹性和训练任务管理

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

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

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

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

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

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

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

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

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

disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m

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

使用 ttl 自动执行 Clean-Up Kubernetes SecondsAfterFinished 作业

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

为配置 Low-Priority 任务抢占 Higher-Priority Jobs/workloads

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

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

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

应用程序扩展和性能

使用 Karpenter 或静态节点为 ML 工作负载定制计算容量

为了确保 Amazon EKS 上的机器学习 (ML) 工作流程具有成本效益和响应式计算能力,我们建议根据工作负载的特性和成本承诺量身定制节点配置策略。以下是两种可供考虑的方法:使用 Karpenter 进行即时扩展,以及预留容量的静态节点组。

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

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

示例

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

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 Event-Driven Autoscaling (KEDA) 根据推理请求或代币吞吐量等模型性能指标进行扩展,并设置适当的冷却时间。静态扩展策略可能会过度或不足预置资源,从而影响成本和延迟。在 KEDA 文档中了解更多信息。

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

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

  • Fine-grained GPU 分配

  • 高级共享机制,例如 Multi-Process 服务 (MPS) 和 Multi-Instance GPU (MIG)

  • 支持下一代硬件架构,包括 NVIDIA GB200 UltraServers

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

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

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

NVIDIA 设备插件限制

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

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

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

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

  • 配置复杂性:设置不同的共享策略需要仔细标记多个ConfigMaps节点,从而造成操作复杂性。

使用 DRA 的解决方案

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

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

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

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

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

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

DRA 支持的实例及其功能

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

实例系列 GPU 类型 Time-slicing MIG 支持 MPS 支持 IMEX 支持 使用案例

G5

NVIDIA A10G

推理和图形工作负载

G6

NVIDIA L4

AI 推理和视频处理

G6e

NVIDIA L40S

训练、推理和绘图

P4d/P4de

NVIDIA A100

支持

Large-scale 培训和 HPC

P5

NVIDIA H100

支持

基础模型训练

P6

NVIDIA B200

支持

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

P6e

英伟达 GB200

支持

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

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

  • Time-slicing:允许多个工作负载在一段时间内共享 GPU 计算资源。

  • Multi-Instance GPU (MIG):用于创建隔离 GPU 实例的 Hardware-level 分区。

  • Multi-Process 服务 (MPS):支持在单个 GPU 上并行执行多个 CUDA 进程。

  • 节点间内存交换 (IMEX):GB200 的节点间 Memory-coherent 通信。 UltraServers

其他资源

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

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

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

先决条件

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

集群配置
  • Amazon EKS 集群运行版本1.33或更高版本

  • 亚马逊 EKS 托管节点组(目前只有采用 AL2023 和 Bottlerocket NVIDIA 优化 AMI 的托管节点组支持 DRA,Karpenter 不支持)

  • 具有适当实例类型的 NVIDIA GPU-enabled 工作节点

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

  • NVIDIA DRA 驱动程序版本25.3.0或更

第 1 步:使用 eksctl 创建带有 DRA-enabled 节点组的集群

  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 operator: Exists effect: NoSchedule EOF
  3. 安装 NVIDIA 设备插件:

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

第 3 步:部署 NVIDIA DRA 驱动程序头盔图

  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

    预期的输出如下所示:

    deviceclasses resource.k8s.io/v1beta1 false DeviceClass resourceclaims resource.k8s.io/v1beta1 true ResourceClaim resourceclaimtemplates resource.k8s.io/v1beta1 true ResourceClaimTemplate resourceslices 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)

继续阅读使用 DRA 具有动态资源分配的 GPU 优化技术 获取更高级的 GPU 优化技术。

具有动态资源分配的 GPU 优化技术

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

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

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

Time-slicing 通过安排多个工作负载在同一个物理 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

第一个 Pod (inference-pod-1)

  • allocated,reserved

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

  • Pod 状态:立即开始运行

第二个 Pod (training-pod-2)

  • pending

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

  • Pod 状态:等待调度

  • 该州将从pendingallocated,reservedrunning

使用 MPS 优化 GPU 工作负载

Multi-Process 服务 (MPS) 支持在单个 GPU 上并行执行多个 CUDA 上下文,比时间切片具有更好的隔离性。

执行以下步骤。

  1. ResourceClaimTemplate为 MPS 定义一个,文件名为mps-claim-template.yaml

    --- 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 Pod:

    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

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

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

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

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

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

容器 1:推理容器
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 | +-----------------------------------------------------------------------------------------+
Container2:训练容器
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 优化 GP Multi-Instance U 负载

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

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

你需要 NVIDIA DRA 驱动程序和 NVIDIA GPU 操作员才能在亚马逊 EKS 中使用 MIG。除此之外,你不需要 NVIDIA 设备插件和 DCGM 导出器,因为它们是 NVIDIA GPU 运营商的一部分。由于 EKS NVIDIA AMI 预装了 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 Operator:

    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-specific 标签)

    • MIG 管理器(Multi-instance 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 托管节点组创建亚马逊 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 运营商使用添加至节点的标签,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 运营商已成功将 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-enabled GPU (0-3):硬件分区

    • GPU 0:英伟达 A100-SXM4-80GB

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

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

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

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

    • GPU 1:英伟达 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 相同的模式

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

第 2 步:测试 MIG 资源分配

现在,让我们运行一些示例来演示 DRA 如何动态地将 MIG 实例分配给不同的工作负载。部署resourceclaimtemplates和测试容器以查看 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 |** +-----------------------------------------------------------------------------------------+

使用 GB200 P6e 实例通过 IMEX 优化 GPU 工作负载

IMEX(节点间内存交换)支持节点间的内存一致性通信,用于在 NVIDIA GB200 上进行分布式训练。 UltraServers

执行以下步骤。

  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 GB200 实例。

  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
  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。