在 Kubernetes 中集成 NVIDIA 多进程服务 (MPS) 以在工作负载之间共享 GPU,从而最大限度地提高利用率并降低基础设施成本
大多数工作负载不需要每个 GPU 的全部内存和计算资源。因此,在多个进程之间共享 GPU 对于提高 GPU 利用率和降低基础架构成本至关重要。
在 Kubernetes 中,这可以通过将单个 GPU 公开为特定内存和计算大小的多个资源(即切片)来实现,这些资源可以由各个容器请求。通过创建每个容器严格需要大小的 GPU 切片,您可以释放集群中的资源。这些资源可以用来调度额外的 Pod,或者可以让你减少集群的节点数量。在任何一种情况下,在进程之间共享 GPU 都可以降低基础设施成本。
Kubernetes 中的 GPU 支持由NVIDIA Kubernetes Device Plugin提供,目前仅支持两种共享策略:时间切片和多实例 GPU (MIG)。然而,还有第三种 GPU 共享策略平衡了时间分片和 MIG 的优缺点:多进程服务(MPS)。尽管 NVIDIA Device Plugin 不支持 MPS,但有一种方法可以在 Kubernetes 中使用它。
在本文中,我们将首先研究所有三种 GPU 共享技术的优缺点,然后提供有关如何在 Kubernetes 中使用 MPS 的分步指南。此外,我们还提供了一种用于自动管理 MPS 资源以优化利用率和降低运营成本的解决方案:动态 MPS 分区。
GPU共享技术概述
共有三种共享 GPU 的方法:
- 时间分片
- 多实例 GPU (MIG)
- 多进程服务 (MPS)
在深入了解动态 MPS 分区的演示之前,让我们先来概述一下这些技术。
时间片
时间片是一种机制,它允许落在超额订阅 GPU 上的工作负载相互交错。时间片利用 GPU 时间片调度程序,它通过时间共享并发执行多个 CUDA 进程。
当时间片被激活时,GPU 通过以固定时间间隔在进程之间切换,以公平共享的方式在不同进程之间共享其计算资源。这会产生与连续上下文切换相关的计算时间开销,这会转化为抖动和更高的延迟。
基本上每个 GPU 架构都支持时间分片,并且是在 Kubernetes 集群中共享 GPU 的最简单的解决方案。然而,进程之间的不断切换会产生计算时间开销。此外,时间分片不会在共享 GPU 的进程之间提供任何级别的内存隔离,也不会提供任何内存分配限制,这可能导致频繁的内存不足 (OOM) 错误。
如果你想在 Kubernetes 中使用时间切片,你所要做的就是编辑 NVIDIA Device Plugin 配置。例如,您可以将以下配置应用于具有 2 个 GPU 的节点。在该节点上运行的设备插件将向nvidia.com/gpuKubernetes 发布 8 个资源,而不是 2 个。这允许每个 GPU 最多由 4 个容器共享。
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4
多实例 GPU (MIG)
多实例 GPU (MIG) 是 NVIDIA Ampere和Hopper架构上可用的一项技术,它允许将 GPU 安全地划分为最多七个独立的 GPU 实例,每个实例都完全隔离,具有自己的高带宽内存、缓存和计算核心。
孤立的 GPU 切片称为 MIG 设备,它们的命名采用指示设备的计算和内存资源的格式。例如,2g.20gb 对应具有 20 GB 内存的 GPU 切片。
MIG 不允许创建自定义大小和数量的 GPU 切片,因为每个 GPU 模型仅支持一组特定的 MIG 配置文件。这会降低您可以对 GPU 进行分区的粒度。此外,MIG 设备的创建必须遵守特定的放置规则,这进一步限制了使用的灵活性。
MIG 是一种 GPU 共享方法,可在进程之间提供最高级别的隔离。但是,它缺乏灵活性,并且仅与少数 GPU 架构(Ampere 和 Hopper)兼容。
您可以使用nvidia-smi CLI手动创建和删除 MIG 设备,也可以使用NVML以编程方式创建和删除 MIG 设备。然后,NVIDIA 设备插件使用不同的命名策略将这些设备公开为 Kubernetes 资源。例如,使用该mixed策略,设备1g.10gb公开为nvidia.com/mig-1g.10gb. 相反,该策略single将设备公开为通用nvidia.com/gpu资源。
使用 nvidia-smi CLI 或 NVML 手动管理 MIG 设备是相当不切实际的:在 Kubernetes 中,NVIDIA GPU Operator 提供了一种使用 MIG 的更简单方法,但仍然存在限制。操作员使用ConfigMap定义一组允许的 MIG 配置,您可以通过使用标签对其进行标记来将其应用于每个节点。
您可以编辑此 ConfigMap 以定义您自己的自定义 MIG 配置,如下例所示。在这个例子中,一个节点被标记为nvidia.com/mig.config=all-1g.5gb。因此,GPU Operator 会将该节点的每个 GPU 划分为 7 个 1g.5gb MIG 设备,然后作为nvidia.com/mig-1g.5gb资源暴露给 Kubernetes。
apiVersion: v1
kind: ConfigMap
metadata:
name: default-mig-parted-config
data:
config.yaml: |
version: v1
mig-configs:
all-1g.5gb:
- devices: all
mig-enabled: true
mig-devices:
"1g.5gb": 7
all-2g.10gb:
- devices: all
mig-enabled: true
mig-devices:
"2g.10gb": 3
为了通过 NVIDIA GPU Operator 有效利用集群中的资源,集群管理员必须不断修改 ConfigMap 以使 MIG 大小适应不断变化的工作负载计算要求。
这是非常不切实际的。虽然这种方法肯定比通过 SSH 连接到节点并手动创建/删除 MIG 设备要好,但对于集群管理员来说,这非常耗费人力和时间。因此,通常情况下 MIG 设备的配置很少随时间更改或根本不应用,在这两种情况下,这都会导致 GPU 利用率低下,从而导致基础设施成本更高。
这个挑战可以通过动态 GPU 分区来克服。在本文的后面,我们将看到如何使用开源模块使用 MPS 对 GPU 进行动态分区nos,遵循的方法也适用于 MIG。
多进程服务 (MPS)
多进程服务 (MPS) 是 CUDA 应用程序编程接口 (API) 的客户端-服务器实现,用于在同一 GPU 上同时运行多个进程。
服务器管理 GPU 访问,提供客户端之间的并发性。客户端通过客户端运行时连接到它,它内置在 CUDA 驱动程序库中,可以被任何 CUDA 应用程序透明地使用。
MPS 基本上与每个现代 GPU 兼容,并提供最高的灵活性,允许创建对可分配内存量和可用计算量具有任意限制的 GPU 切片。但是,它不强制进程之间的完全内存隔离。在大多数情况下,MPS 代表了 MIG 和时间切片之间的良好折衷。
与时间片相比,MPS 通过空间共享并行运行进程,消除了上下文切换的开销,从而带来更好的计算性能。此外,MPS 为每个进程提供了自己的 GPU 内存地址空间。这允许对进程实施内存限制,克服时间片共享的限制。
然而,在 MPS 中,客户端进程并不是完全相互隔离的。事实上,尽管 MPS 允许限制客户端的计算和内存资源,但它不提供错误隔离和内存保护。这意味着客户端进程可能会崩溃并导致整个 GPU 重置,从而影响在 GPU 上运行的所有其他进程。
NVIDIA Kubernetes 设备插件不提供对 MPS 分区的支持,因此在 Kubernetes 中使用它并不简单。在下一节中,我们将探索通过利用nos不同的 Kubernetes 设备插件来利用 MPS 进行 GPU 共享的替代方法。
Kubernetes 中的多进程服务 (MPS)
您可以通过使用 Helm 安装 NVIDIA 设备插件的这个分支来在 Kubernetes 集群中启用 MPS 分区:
helm install oci://ghcr.io/nebuly-ai/helm-charts/nvidia-device-plugin \
--version 0.13.0 \
--generate-name \
-n nebuly-nvidia \
--create-namespace
默认情况下,Helm chart 在所有标记为 的节点上部署启用 MPS 模式的设备插件nos.nebuly.com/gpu-partitioning=mps。要在特定节点的 GPU 上启用 MPS 分区,您只需将标签应用nos.nebuly.com/gpu-partitioning=mps到它即可。
您的集群上可能已经安装了某个版本的 NVIDIA 设备插件。如果您不想删除它,您可以选择将这个分叉插件与原始 NVIDIA 设备插件一起安装,并仅在特定节点上运行它。为此,确保一次只有两个插件中的一个在一个节点上运行很重要。如安装指南中所述,这可以通过编辑原始NVIDIA 设备插件的规范并在其中添加反关联规则来实现spec.template.spec,这样它就不会在分叉插件所针对的相同节点上运行:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nos.nebuly.com/gpu-partitioning
operator: NotIn
values:
- mps
安装设备插件后,您可以通过编辑sharing.mps其配置部分将其配置为将 GPU 公开为多个 MPS 资源。例如,下面的配置告诉插件向 Kubernetes 公开带有索引的 GPU0作为两个 GPU 资源(名为nvidia.com/gpu-4gb),每个 4GB 内存:
version: v1
sharing:
mps:
resources:
- name: nvidia.com/gpu
rename: nvidia.com/gpu-4gb
memoryGB: 4
replicas: 2
devices: ["0"]
发布给 Kubernetes 的资源名称、分区大小和副本数可以根据需要配置。回到上面给出的示例,容器可以请求 4 GB GPU 内存的一小部分,如下所示:
apiVersion: v1
kind: Pod
metadata:
name: mps-partitioning-example
spec:
hostIPC: true #
securityContext:
runAsUser: 1000 #
containers:
- name: sleepy
image: "busybox:latest"
command: ["sleep", "120"]
resources:
limits:
nvidia.com/gpu-4gb: 1 #
请注意,带有请求 MPS 资源的容器的 Pod 有一些限制:
- 容器必须使用与使用设备插件部署的 MPS 服务器相同的用户 ID 运行,默认情况下为 1000。mps.userID您可以通过编辑设备插件安装图表的值来更改它。
- Pod 规范必须包括hostIPC: true. 由于 MPS 要求客户端和服务器共享相同的内存空间,因此我们需要允许 Pod 访问主机节点的 IPC 命名空间,以便它可以与运行在其上的 MPS 服务器进行通信。
在此示例中,容器最多只能在共享 GPU 上分配 2 GB 的内存。如果它尝试分配更多内存,它将因内存不足 (OOM) 错误而崩溃,而不会影响其他 Pod。
但是,需要指出的是nvidia-smi绕过 MPS 客户端运行时访问 NVIDIA 驱动程序。因此,nvidia-smi 在容器内运行将在其输出中显示整个 GPU 资源:
image.png动态 MPS 分区
总体而言,通过 Device Plugin 配置来管理 MPS 资源既复杂又耗时。相反,最好只创建请求 MPS 资源的 Pod,然后让其他人自动配置和管理它们。
动态 MPS 分区正是这样做的:它根据集群中工作负载的实时要求自动创建和删除 MPS 资源,确保始终将最佳共享配置应用于可用的 GPU。
要应用动态分区,我们需要使用nos一个开源模块来在 Kubernetes 上高效运行 GPU 工作负载。
我已经介绍了如何使用nos基于多实例 GPU (MIG) 的动态 GPU 分区。因此,我们不会在这里深入研究细节,因为nos以相同的方式管理 MPS 分区。更多信息可以参考文章Dynamic MIG Partitioning in Kubernetes,或者查看nos 文档。
MPS 和 MIG 动态分区之间的唯一区别是用于告知应为哪些节点管理 GPU 分区的标签的值nos。如果是 MPS,则需要按如下方式标记节点:
kubectl label nodes <node-names> "nos.nebuly.com/gpu-partitioning=mps"
结论
请求 GPU 切片的可能性对于提高 GPU 利用率和降低基础设施成本至关重要。
可以通过三种方式实现:时间切片、多实例 GPU (MIG) 和多进程服务器 (MPS)。时间分片是共享 GPU 的最简单技术,但它缺乏内存隔离并引入会降低工作负载性能的开销。另一方面,MIG 提供了最高级别的隔离,但其受支持的配置和“切片”大小的集合有限,使其不灵活。
MPS 是 MIG 和时间片之间的有效折衷。与 MIG 不同,它允许创建任意大小的 GPU 切片。与时间切片不同,它允许强制执行内存分配限制并减少多个容器竞争共享 GPU 资源时可能发生的内存不足 (OOM) 错误。
目前,NVIDIA Device Plugin 不支持 MPS。尽管如此,只需安装另一个支持它的设备插件即可启用 MPS。
然而,MPS 静态配置不会自动调整以适应工作负载不断变化的需求,因此不足以为每个 Pod 提供所需的 GPU 资源,尤其是在工作负载需要在内存和计算方面随时间变化的各种切片的场景中。
nos通过动态 GPU 分区克服 MPS 静态配置限制,这提高了 GPU 利用率并减少了在集群节点上运行的设备插件实例上手动定义和应用 MPS 配置的操作负担。
总之,我们必须指出,有些情况下 MPS 的灵活性是不必要的,而 MIG 提供的完全隔离是至关重要的。然而,在这些情况下,仍然可以通过 来利用动态 GPU 分区nos,因为它支持两种分区模式。
网友评论