Kubernetes

K8s Device Plugin机制

2023-01-06  本文已影响0人  王勇1024

Device Plugin介绍

从 Kubernetes 1.8版本开始,提供了 Device Plugin 框架,设备厂商无需修改 Kubernetes 核心代码就可以将自己生产的设备的资源(Kubernetes 可管理的资源包括CPU、内存和存储资源)可以让 kubelet 使用(这一点与操作系统一样,所有设备厂商自己实现驱动)。设备厂商可以自己人工或者以 DaemonSet方式部署,而不是定制 Kubernetes 代码。目标设备包括GPU、高性能NIC(网络接口卡)、FPGA、InfiniBand以及其他类似的需要厂商指定初始化和安装的计算资源。

在 v1.10 中该特性升级为 Beta 版本。

Device Plugin工作原理

Device Plugin实际上是一个 gPRC 接口,需要实现 ListAndWatch() 和 Allocate() 等方法,并监听 gRPC Server 的 Unix Socket 在 /var/lib/kubelet/device-plugins/ 目录中,如 /var/lib/kubelet/device-plugins/nvidiaGPU.sock。在实现 Device 插件时需要注意:

Device Plugin工作流程

下面是 Device Plugin 的工作流程:

Pod 的调度和运行的过程

以GPU为例,我们一起来了解一下Pod在申请扩展资源后的调度过程:

  1. Pod 想使用一个 GPU 的时候,它只需要像之前的例子一样,在 Pod 的 Resource 下 limits 字段中声明 GPU 资源和对应的数量 (比如nvidia.com/gpu: 1)。Kubernetes 会找到满足数量条件的节点,然后将该节点的 GPU 数量减 1,并且完成 Pod 与 Node 的绑定。

  2. 绑定成功后,自然就会被对应节点的 kubelet 拿来创建容器。而当 kubelet 发现这个 Pod 的容器请求的资源是一个 GPU 的时候,kubelet 就会委托自己内部的 Device Plugin Manager 模块,从自己持有的 GPU 的 ID 列表中选择一个可用的 GPU 分配给该容器。此时 kubelet 就会向本机的 DeAvice Plugin 发起一个 Allocate 请求,这个请求所携带的参数,正是即将分配给该容器的设备 ID 列表。

  3. Device Plugin 收到 AllocateRequest 请求之后,它就会根据 kubelet 传过来的设备 ID,去寻找这个设备 ID 对应的设备路径、驱动目录以及环境变量,并且以 AllocateResponse 的形式返还给 kubelet。

  4. AllocateResponse 中所携带的设备路径和驱动目录信息,一旦返回给 kubelet 之后,kubelet 就会根据这些信息执行为容器分配 GPU 的操作,这样 Docker 会根据 kubelet 的指令去创建容器,而这个容器中就会出现 GPU 设备。并且把它所需要的驱动目录给挂载进来,至此 Kubernetes 为 Pod 分配一个 GPU 的流程就结束了。

处理 kubelet 重启

设备插件应能监测到 kubelet 重启,并且向新的 kubelet 实例来重新注册自己。 在当前实现中,当 kubelet 重启的时候,新的 kubelet 实例会删除 /var/lib/kubelet/device-plugins 下所有已经存在的 Unix 套接字。 设备插件需要能够监控到它的 Unix 套接字被删除,并且当发生此类事件时重新注册自己。

Nvidia-Device-Plugin

简介

NVIDIA device plugin 通过k8s daemonset的方式部署到每个k8s的node节点上,实现了Kubernetes device plugin的接口。

提供以下功能:

要注意的是 kubelet 在向 api-server 进行汇报的时候,只会汇报该 GPU 对应的数量。而 kubelet 自身的 Device Plugin Manager 会对这个 GPU 的 ID 列表进行保存,并用来具体的设备分配。而这个对于 Kubernetes 全局调度器来说,它不掌握这个 GPU 的 ID 列表,它只知道 GPU 的数量。这就意味着在现有的 Device Plugin 工作机制下,Kubernetes 的全局调度器无法进行更复杂的调度。比如说想做两个 GPU 的亲和性调度,同一个节点两个 GPU 可能需要进行通过 NVLINK 通讯而不是 PCIe 通讯,才能达到更好的数据传输效果。在这种需求下,目前的 Device Plugin 调度机制中是无法实现的。

要求

使用

安装NVIDIA drivers和nvidia-docker

提供GPU节点的机器,准备工作如下

  1. 安装NVIDIA drivers ~= 384.81

  2. 安装nvidia-docker version > 2.0

注意:需要安装 nvidia-docker2,而不要安装 nvidia-container-toolkit。

# Add the package repositories
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

$ sudo apt-get update && sudo apt-get install -y nvidia-docker2
$ sudo systemctl restart docker
配置docker runtime

配置nvidia runtime作为GPU节点的默认runtime。修改文件/etc/docker/daemon.json,增加以下runtime内容:

{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

部署nvidia-device-plugin

$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.11.0/nvidia-device-plugin.yml
运行GPU任务

创建一个GPU的pod,pod的资源类型指定为nvidia.com/gpu。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: cuda-container
      image: nvcr.io/nvidia/cuda:9.0-devel
      resources:
        limits:
          nvidia.com/gpu: 2 # requesting 2 GPUs
    - name: digits-container
      image: nvcr.io/nvidia/digits:20.12-tensorflow-py3
      resources:
        limits:
          nvidia.com/gpu: 2 # requesting 2 GPUs

警告:当使用

NVIDIA GPU Operator

如果你需要监控集群GPU资源使用情况,你可能还需要安装DCCM exporter结合Prometheus输出GPU资源监控信息。

要安装和管理这么多的组件,对于运维人员来说压力不小。基于此,NVIDIA开源了一款叫NVIDIA GPU Operator的工具,该工具基于Operator Framework实现,用于自动化管理上面我们提到的这些组件。

NVIDIA GPU Operator有以下的组件构成:

实现Device Plugin

实现Registration接口

Registration接口用于插件向kubelet注册,kubelet是服务端,插件是客户端。

service Registration {
    rpc Register(RegisterRequest) returns (Empty) {}
}

message RegisterRequest {
    string version = 1;                 // 版本信息
    string endpoint = 2;                // 插件的endpoint
    string resource_name = 3;           // 资源名称
    DevicePluginOptions options = 4;    // 插件选项
}
// 那插件选项又包含什么呢?
message DevicePluginOptions {
    bool pre_start_required = 1;        // 启动容器前是否调用DevicePlugin.PreStartContainer()
}

实现DevicePlugin接口

service DevicePlugin {
      // GetDevicePluginOptions 返回与设备管理器沟通的选项。
      rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}

      // ListAndWatch 返回 Device 列表构成的数据流。
      // 当 Device 状态发生变化或者 Device 消失时,ListAndWatch
      // 会返回新的列表。
      rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}

      // Allocate 在容器创建期间调用,这样设备插件可以运行一些特定于设备的操作,
      // 并告诉 kubelet 如何令 Device 可在容器中访问的所需执行的具体步骤
      rpc Allocate(AllocateRequest) returns (AllocateResponse) {}

      // GetPreferredAllocation 从一组可用的设备中返回一些优选的设备用来分配,
      // 所返回的优选分配结果不一定会是设备管理器的最终分配方案。
      // 此接口的设计仅是为了让设备管理器能够在可能的情况下做出更有意义的决定。
      rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}

      // PreStartContainer 在设备插件注册阶段根据需要被调用,调用发生在容器启动之前。
      // 在将设备提供给容器使用之前,设备插件可以运行一些诸如重置设备之类的特定于
      // 具体设备的操作,
      rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
}

参考文档

理解GPU管理机制和Device Plugin的工作机制

NVIDIA GPU Operator分析三:NVIDIA Device Plugin安装

NVIDIA device plugin for Kubernetes

上一篇 下一篇

猜你喜欢

热点阅读