7. Kubernetes集群 GPU/IPU 混合部署解决方案

7.1. 引言

一个异构集群是由一组拥有不同设备的计算机组成的,这些服务器的 CPU 包括来自多个供应商的 Intel、AMD、ARM 芯片,对于人工智能场景,GPU、FPGA、IPU 等人工智能芯片广泛使用。 如何在集群中管理多种设备,是一个值得考虑的问题。作为容器编排基础架构,Kubernetes 承载了异构硬件的管理,在标准 Kubernetes 之上提供异构硬件的调度和编排,并支持用户的 各种灵活策略控制。我们基于 Kubernetes 的原始架构,考虑统一管理 IPUs 和 GPUs,降低用户使用两种异构设备时的感知度,提高用户使用体验。 我们提供了 IPU 和 GPU 混合资源部署的端到端解决方案,尝试解决以下问题:

  • 支持 IPU 和 GPU 基于设备优先级在 Kubernetes 集群中进行统一的资源调度

  • 提供支持 IPU 和 GPU 两种硬件的混合后端

7.2. 搭建 GPU 和 IPU 的混合资源 Kubernetes 集群

在 Kubernetes 集群中通过 Device Plugin 来对硬件设备进行管理。

7.2.1. 为节点添加标签

  • 为 IPU 节点添加标签
  • 为 GPU 节点添加标签

使 Kubernetes 集群支持对 GPU 资源进行调度

NVIDIA 设备插件是 Kubernetes 的一个 Daemonset ,可以自动完成以下任务:

  • 暴露集群中每个节点上 GPU 数量

  • 跟踪 GPU 的健康状况

  • 在 Kubernetes 集群中运行启用了 GPU 的容器

NVIDIA 发布的 快速入门指南 描述了如何通过 NVIDIA 设备插件使 GPU 与 Kubernetes 协同工作的指导。

可以通过部署以下 Daemonset 来启用 GPU 支持:

如果希望 GPU Device Plugin 仅部署在 GPU 节点上,可以为其添加 nodeSelector

使 Kubernetes 集群支持对 IPU 资源进行调度

Kubernetes IPU Device Plugin 是用于 Kubernetes 的一种 DaemonSet,它具有以下功能:

  • 暴露集群中每个节点的 IPU 数量

  • 跟踪 IPU 的健康状况

  • 在 Kubernetes 集群中运行启用了 IPU 的容器

用户指南 包含如何通过Kubernetes IPU设备插件使C600 IPU与Kubernetes配合工作的完整文档。 通过部署以下 Daemonset 来启用 IPU 支持:

如果希望 IPU Device Plugin 仅部署在 GPU 节点上,可以为其添加 nodeSelector

7.3. 在 IPU 和 GPU 的异构集群中进行统一设备调度

H-Device Scheduler 插件支持基于设备优先级进行 GPU 和 IPU 的统一调度。该插件是基于 scheduler framework 中的 PreFilter 插件实现的。 其实现框架如下图所示,具体详情参见 Github repo

images/heterogeneous_deployment_solution/framework.png

快速入门指南 描述了H-Device Scheduler如何在Kubernetes集群中进行工作。 按照以下方式安装调度程序插件:

当在集群中成功部署后,一个名为 h-device-plugin-scheduler 的新的调度器就可以被使用了。

可以通过在 Kubernetes 资源(如 Pod,Deployment 等)的 annotations 中设置 device.priority/weights 来指定设备的优先级权重。 以下是一个示例:

在该示例中,IPU 和 GPU 的优先级权重为 1:0,根据集群的设备情况:

  • 如果集群中可以分配5个 IPU,则调度程序将在 IPU 节点上运行5个 replica;

  • 如果可用的 IPU 小于5个但大于1个,例如可以使用3个 IPU,则会在 IPU 节点上运行3个 replica,并尝试在 GPU 节点上运行2个 replica;

  • 如果无可用的 IPU,则5个 replica 将尝试在 GPU 节点上运行。

H-Device Scheduler 插件将设置未使用设备的 resources.limitsresources.requests 为0。例如,如果 replica 运行在 IPU 节点,则如下所示:

7.4. 支持 IPU 和 GPU 模型的统一镜像

本示例介绍了如何在 Kubernetes 集群中利用 HDevice-scheduler-plugin 来进行合理分配和调度 GPU 和 IPU 资源。 当安装完 HDevice-scheduler-plugin 后,我们可以正常的利用 Kubernetes 的 Deployment 来创建服务,但是镜像需要 同时支持 IPU 和 GPU,这样才能够在创建服务时,根据服务的资源需求,自动调度到合适的节点上。我们这里以 roberta 为例, 首先,我们准备好 GPU 的模型,这里是利用 TensorRT 转换完成的。然后,我们还要准备好 IPU 的模型,这里是利用 PopRT 工具转换完成的。 详细的转换过程可以参考 NLP 端到端在线搜索解决方案,在此不再赘述。我们直接将转换好的模型分别存到共享网盘中以供使用并且服务框架选用 Triton Server

在上述的 dockerfile 当中, entrypoint 的脚本内容如下:

在上述脚本中我们先将模型从共享网盘中拷贝到本地,然后再启动 Triton Server 服务。

7.5. 应用案例

7.5.1. Kubernetes应用部署

在 Deployment 部署之前,我们先要创建一个 Secret 对象,用来存放我们的模型共享网盘的账号和密码,这样我们在创建服务时就可以直接下载到容器中。

当准备工作已完成之后,进行 Deployment 的部署。以下是一个 Deployment 的示例,我们可以看到, 这里的镜像是 支持 IPU 和 GPU 模型的统一镜像 编译之后的镜像,而且我们在资源需求中指定了 GPUIPU 的硬件数量,并通过 annotation 来指定硬件的优先级。

将上述文件保存成 k8s-mixbackends-deployment.yaml 文件,然后执行如下命令即可创建一个 Deployment 对象。

# 创建一个 Deployment
kubectl apply -f k8s-mixbackends-deployment.yaml
deployment_ready

我们可以用 kubectl describe pod <podname> 来查看我们的 pod 是否正常调度到了合适的节点上。

gpu_pod

我们可以从红色框里面看到这个pod是调度在 GPU 节点上的。

ipu_pod

我们可以从红色框里面看到这个pod是调度在 IPU 节点上的。

为了能够方便的上线服务,我们启用 Kubernetes 的 service 来对外提供服务,这里我们使用的是 NodePort 类型的 service。

将上述文件保存成 k8s-mixbackends-service.yaml 文件,然后执行如下命令即可创建一个 Service 对象。

# 创建一个 Service
kubectl apply -f k8s-mixbackends-service.yaml

我们可以用 kubectl get service 来查看我们的 service 是否正常创建。

service_ready

以上说明我们创建好了 service,我们可以通过 NodeIP:NodePort 来访问我们的服务。

7.5.2. 测试服务

这里我们用我们自己打包好的压力测试镜像来进行压测。

将上述文件保存成 config.json 文件,然后在同级目录下执行如下命令即可启动压测。

我们可以看到,我们的服务正常的返回了结果。

send_request