欢迎光临
我们一直在努力

如何利用Kubernetes Device Plugin管理异构计算设备(FPGA/NPU)?

在现代AI基础设施中,除了标准的GPU,FPGA(现场可编程门阵列)和NPU(神经网络处理器)等异构计算设备扮演着越来越重要的角色。这些设备提供了更高的能效比和定制化的计算能力。然而,Kubernetes原生只支持基本的CPU和内存调度。要让Kubelet识别、分配和管理这些非标准设备,我们必须依赖Kubernetes Device Plugin机制。

本文将深入讲解Device Plugin的工作原理,并提供一个基于Device Plugin API管理异构资源的实操框架。

1. Device Plugin 架构概述

Kubernetes Device Plugin 是一种 Sidecar 模式的扩展机制,允许第三方供应商(如FPGA/NPU厂商)通过实现特定的gRPC服务,向Kubelet通告和管理其设备。

核心工作流程:

  1. 发现 (Discovery): Device Plugin 启动后,在主机文件系统(通常是
    1
    /var/lib/kubelet/device-plugins/

    )上创建一个 Unix Socket。

  2. 注册 (Registration): Plugin 通过该 Socket 连接到 Kubelet,并向 Kubelet 注册自己提供的资源类型(例如:
    1
    company.com/fpga_accel

    )。

  3. 监控 (ListAndWatch): Plugin 持续监控设备的健康状态,并通过 gRPC 流实时报告给 Kubelet。
  4. 分配 (Allocation): 当 Pod 请求该资源时,Kubelet 调用 Plugin 的
    1
    Allocate

    接口。Plugin 负责准备设备(例如,进行必要的驱动初始化,设置cgroup规则),并将分配信息返回给 Kubelet,Kubelet随后将这些信息注入到Pod的运行时环境中。

2. Device Plugin gRPC 接口实现要点

一个 Device Plugin 必须实现 Kubernetes 定义的

1
device.proto

中的 gRPC 服务。以下是实现一个假定 NPU Plugin 的关键步骤。

2.1 定义 gRPC 服务(device.proto 简化)

Device Plugin 至少需要实现

1
Registration

服务和

1
DevicePlugin

服务。


1
2
3
4
5
6
7
8
9
10
// Registration.proto
service Registration {
    rpc Register(RegisterRequest) returns (Empty) {}
}

// DevicePlugin.proto
service DevicePlugin {
    rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
}

2.2 插件核心逻辑 (Python 伪代码)

为了演示注册和报告设备的过程,我们使用 Python 结构来展示关键逻辑。一个实际的 NPU 插件需要通过特定的 vendor SDK 来获取设备信息和健康状态。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import grpc
import time
import os

# 假设我们已经生成了 device_plugin_pb2.py 和 device_plugin_pb2_grpc.py

SOCKET_PATH = '/var/lib/kubelet/device-plugins/my-npu.sock'
RESOURCE_NAME = 'mycompany.com/npu_accelerator'

class NPUDevicePlugin(device_plugin_pb2_grpc.DevicePluginServicer):
    def __init__(self):
        # 模拟两个 NPU 设备 ID
        self.devices = {
            'npu-0': device_plugin_pb2.Device.HEALTHY,
            'npu-1': device_plugin_pb2.Device.HEALTHY
        }

    def ListAndWatch(self, request, context):
        # 持续发送设备列表和健康状态
        while True:
            devices_list = []
            for id, health in self.devices.items():
                devices_list.append(
                    device_plugin_pb2.Device(id=id, health=health)
                )

            response = device_plugin_pb2.ListAndWatchResponse(devices=devices_list)
            yield response
            time.sleep(10) # 每10秒报告一次状态

    def Allocate(self, request, context):
        # Kubelet 请求分配设备时调用
        response = device_plugin_pb2.AllocateResponse()
        container_response = device_plugin_pb2.ContainerAllocateResponse()

        # 实际操作:获取设备驱动信息、挂载路径、设置cgroup
        for id in request.container_requests[0].device_ids:
            print(f"Allocating device: {id}")

            # 假设 NPU 需要一个特殊的设备文件挂载
            container_response.devices.append(
                device_plugin_pb2.DeviceSpec(
                    host_path=f'/dev/{id}',
                    container_path=f'/dev/npu_accel/{id}',
                    permissions='rwm'
                )
            )

        response.container_responses.append(container_response)
        return response

# 注册逻辑
def register_to_kubelet():
    # Kubelet gRPC Server 监听在 /var/lib/kubelet/device-plugins/kubelet.sock
    channel = grpc.insecure_channel(
        'unix:///var/lib/kubelet/device-plugins/kubelet.sock'
    )
    # ... (连接和发送 RegisterRequest,包含 RESOURCE_NAME 和 SOCKET_PATH)
    print(f"Plugin registered for {RESOURCE_NAME}")

3. 部署 Device Plugin (使用 DaemonSet)

Device Plugin 必须作为 DaemonSet 部署,确保它运行在集群中的每个节点上(或通过 Node Selector 运行在拥有异构设备的节点上)。

关键配置点:

  1. HostPath Volume: 必须挂载 Kubelet 的 Device Plugin 目录 (
    1
    /var/lib/kubelet/device-plugins

    ),用于通信。

  2. Privileged Mode: 通常需要以特权模式运行,以便访问和配置底层设备驱动和
    1
    /dev

    目录。

Device Plugin DaemonSet YAML 示例


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: npu-device-plugin
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: npu-plugin
  template:
    metadata:
      labels:
        name: npu-plugin
    spec:
      hostNetwork: true
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: npu-plugin-container
        image: your-registry.com/npu-plugin:v1.0
        securityContext:
          privileged: true  # 允许访问底层设备
        volumeMounts:
        - name: device-plugin-socket
          mountPath: /var/lib/kubelet/device-plugins
        - name: dev-dir
          mountPath: /dev  # 如果需要在Allocate中修改设备文件
      volumes:
      - name: device-plugin-socket
        hostPath:
          path: /var/lib/kubelet/device-plugins
      - name: dev-dir
        hostPath:
          path: /dev

4. 验证和调度异构资源

部署 DaemonSet 成功后,Kubelet 会识别新的资源类型。您可以通过查看节点状态来验证资源是否被注册。

4.1 检查 Node Status


1
kubectl describe node <node-name>

您应该在

1
Capacity

1
Allocatable

部分看到您的自定义资源:


1
2
3
4
5
6
7
8
Capacity:
  cpu:                16
  memory:             64Gi
  mycompany.com/npu_accelerator: 2  # 成功注册了2个NPU
Allocatable:
  cpu:                16
  memory:             64Gi
  mycompany.com/npu_accelerator: 2

4.2 Pod 请求资源

现在,AI 推理或训练工作负载可以通过标准的

1
resources.limits

字段请求这些 NPU 设备。


1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
  name: npu-worker-pod
spec:
  containers:
  - name: ai-inference-app
    image: deeplearning/npu-image:latest
    resources:
      limits:
        mycompany.com/npu_accelerator: 1

当 Pod 调度到具有 NPU 的节点上时,Kubelet 会调用 Device Plugin 的

1
Allocate

方法,确保所需的设备文件和挂载点正确配置到容器中,从而实现对异构计算资源的精细化管理和使用。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 如何利用Kubernetes Device Plugin管理异构计算设备(FPGA/NPU)?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址