计算和自动缩放 - 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会自动选择相应的 ClusterConfig AMI 并安装 NVIDIA Kubernetes 设备插件。要了解更多信息,请参阅 eksctl 文档中的 GPU 支持

如果你决定改用 EKS Accelerated AMIs 和 NVIDIA GPU 操作员来管理诸如 NVIDIA Kubernetes 设备插件之类的组件,请注意根据预安装的 NVIDIA GPU 驱动程序和 NVIDIA 容器工具包 NVIDIA 文档禁用 NVIDIA GPU 驱动程序和 NVIDIA 容器工具包的管理。

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

kubectl describe node | grep nvidia.com/gpu

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

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

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

加速计算实例以不同的购买模式提供,以适应短期、中期和稳定状态的工作负载。对于短期、灵活且容错的工作负载,如果您希望避免进行预留,请查看竞价型实例。容量块、按需实例和节省计划允许您为中长期工作负载持续时间预置加速计算实例。为了增加在首选购买选项中成功访问所需容量的机会,建议使用不同的实例类型和可用区列表。或者,如果您遇到特 ICEs 定的购买模式,请重试使用其他型号。

示例以下示例说明如何让 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

有关使用竞价型实例的详细信息 GPUs,请参阅下面的 “考虑在 Karpenter 上使用亚马逊 EC2 竞价型实例”。 GPUs

考虑将 Amazon EC2 竞价型实例 GPUs 与 Karpenter 配合使用

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

使用案例

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

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

  • 超参数调整:使用 Spot 实例机会主义地运行探索性调整作业,因为可以容忍中断而不会造成重大损失,尤其是对于持续时间较短的实验。

  • 数据增强:使用 Spot 实例执行数据预处理和增强任务,如果中断,这些任务可以从检查点重新启动,因此非常适合 Spot 的可变可用性。

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

  • B@@ atch In ference:使用 Spot 实例处理大批量的离线推理请求,可以暂停和恢复作业,从而与 Spot 节省的成本保持最佳一致,并通过重试或多样化来处理潜在的中断。 non-real-time

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

注意事项

要在 Amazon EKS 上使用竞价型实例处理加速工作负载,需要考虑一些关键因素(排名不分先后):

  • 使用 Karpenter 管理启用了高级整合的竞价型实例。通过在 Karpenter 中将 karpenter.sh/cacitype 指定为 “spot”,Karpenter 将在默认情况下预配置 Spot NodePool 实例,无需任何其他配置。但是,要启用高级 Spot-to-Spot整合(用价格较低的竞价替代品取代未充分利用的竞价节点),您需要通过在 Karpenter 控制器参数中设置--fe at ure-gates SpotToSpotConsolidation =true 或通过 FEATURE_GATES 环境变量来启用功能门。 SpotToSpotConsolidation Karpenter 使用price-capacity-optimized分配策略来配置 EC2 实例。根据 NodePool 要求和容器限制,Karpenter 对不可调度的 pod 进行装箱,并将一组不同的实例类型发送到 Amazon Fleet API。 EC2 您可以使用EC2 实例类型浏览器工具生成符合您的特定计算要求的实例类型列表。

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

  • 避免过度限制实例类型选择。应尽量避免限制实例类型。通过不限制实例类型,以更低的成本获得竞价型实例中断频率的大规模竞价型容量的几率更高。例如,避免仅限于特定类型(例如 g5.xlarge)。考虑使用诸如 karpenter.k8s 之类的密钥指定一组不同的实例类别和生成。 aws/instance-category and karpenter.k8s.aws/instance-generation. Karpenter enables easier diversification of on-demand and Spot instance capacity across multiple instance types and Availability Zones (AZs). Moreover, if your AI/ML工作负载需要特定或有限数量的加速器,但在区域之间具有灵活性,您可以在启动前使用竞价放置分数动态确定部署工作负载的最佳区域。

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

  • 利用一个区域中的所有可用区。可用容量因可用区 (AZ) 而异,特定实例类型可能在一个可用区不可用,但在另一个可用区却很多。实例类型和可用区的每种唯一组合构成一个单独的竞价容量池。通过请求符合您的 Karpenter NodePool 要求的某个区域的所有 AZs 容量,您实际上是在一次搜索更多资源池。这样可以最大限度地提高竞价容量池的数量,从而增加获取竞价容量的可能性。为此,在您的 NodePool 配置中,要么完全省略 topology.kubernetes.io/zone 密钥以允许 Karpenter 从该区域的所有可用密钥 AZs 中进行选择,要么 AZs 使用运算符:in 明确列出并提供值(例如 us-west-2a)。

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

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

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

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

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

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

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

对于竞价型实例的实时在线推理工作负载,您可以将 Karpenter NodePool 配置为在兼容的 GPU 实例系列和世代之间实现多样化。这种方法通过从多个 Spot 池中提取资源来确保高可用性,同时通过 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 Spot 实例中断造成的中断的影响。

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

使用案例

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

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

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

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

  • 具有高资源需求的大型模型:在 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 上 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 Capacity Blocks 不同,它 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 UltraServers

传统的 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 UltraServers

其他资源

有关 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 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

    以下是预期的输出:

    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)

继续阅读采用动态资源分配的 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 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 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
  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。