Kubernetes容器-使用Kubernetes设备插件框架

作者: K8SStack

Kubernetes 提供了一个 [设备插件框架],你可以用它来将系统硬件资源发布到 。

供应商可以实现设备插件,由你手动部署或作为

来部署,而不必定制 Kubernetes 本身的代码。目标设备包括 GPU、高性能 NIC、FPGA、 InfiniBand 适配器以及其他类似的、可能需要特定于供应商的初始化和设置的计算资源。

注册设备插件

kubelet 提供了一个 Registration 的 gRPC 服务:

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

设备插件可以通过此 gRPC 服务在 kubelet 进行注册。在注册期间,设备插件需要发送下面几样内容:

  • 设备插件的 Unix 套接字。
  • 设备插件的 API 版本。
  • ResourceName 是需要公布的。这里 ResourceName 需要遵循 [扩展资源命名方案], 类似于 vendor-domain/resourcetype。(比如 NVIDIA GPU 就被公布为 nvidia.com/gpu。)

成功注册后,设备插件就向 kubelet 发送它所管理的设备列表,然后 kubelet

负责将这些资源发布到 API 服务器,作为 kubelet 节点状态更新的一部分。

比如,设备插件在 kubelet 中注册了 hardware-vendor.example/foo 并报告了

节点上的两个运行状况良好的设备后,节点状态将更新以通告该节点已安装 2 个 “Foo” 设备并且是可用的。

然后,用户可以请求设备作为 Pod 规范的一部分,

参见 [Container]。

请求扩展资源类似于管理请求和限制的方式,

其他资源,有以下区别:

  • 扩展资源仅可作为整数资源使用,并且不能被过量使用
  • 设备不能在容器之间共享

示例

假设 Kubernetes 集群正在运行一个设备插件,该插件在一些节点上公布的资源为 hardware-vendor.example/foo

下面就是一个 Pod 示例,请求此资源以运行一个工作负载的示例:

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: registry.k8s.io/pause:2.0
      resources:
        limits:
          hardware-vendor.example/foo: 2

##

## 这个 pod 需要两个 hardware-vendor.example/foo 设备

## 而且只能够调度到满足需求的节点上

##

## 如果该节点中有 2 个以上的设备可用,其余的可供其他 Pod 使用

设备插件的实现

设备插件的常规工作流程包括以下几个步骤:

  • 初始化。在这个阶段,设备插件将执行供应商特定的初始化和设置, 以确保设备处于就绪状态。
  • 插件使用主机路径 /var/lib/kubelet/device-plugins/ 下的 Unix 套接字启动 一个 gRPC 服务,该服务实现以下接口:
    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) {}
    }
    
    插件并非必须为 GetPreferredAllocation()PreStartContainer() 提供有用 的实现逻辑,调用 GetDevicePluginOptions() 时所返回的 DevicePluginOptions 消息中应该设置这些调用是否可用。kubelet 在真正调用这些函数之前,总会调用 GetDevicePluginOptions() 来查看是否存在这些可选的函数。
  • 插件通过 Unix socket 在主机路径 /var/lib/kubelet/device-plugins/kubelet.sock 处向 kubelet 注册自身。
  • 成功注册自身后,设备插件将以服务模式运行,在此期间,它将持续监控设备运行状况, 并在设备状态发生任何变化时向 kubelet 报告。它还负责响应 Allocate gRPC 请求。 在 Allocate 期间,设备插件可能还会做一些设备特定的准备;例如 GPU 清理或 QRNG 初始化。 如果操作成功,则设备插件将返回 AllocateResponse,其中包含用于访问被分配的设备容器运行时的配置。 kubelet 将此信息传递到容器运行时。

Handling kubelet restarts

A device plugin is expected to detect kubelet restarts and re-register itself with the new kubelet instance. In the current implementation, a new kubelet instance deletes all the existing Unix sockets under /var/lib/kubelet/device-plugins when it starts. A device plugin can monitor the deletion of its Unix socket and re-register itself upon such an event. –>

处理 kubelet 重启

设备插件应能监测到 kubelet 重启,并且向新的 kubelet 实例来重新注册自己。

在当前实现中,当 kubelet 重启的时候,新的 kubelet 实例会删除 /var/lib/kubelet/device-plugins

下所有已经存在的 Unix 套接字。

设备插件需要能够监控到它的 Unix 套接字被删除,并且当发生此类事件时重新注册自己。

设备插件部署

你可以将你的设备插件作为节点操作系统的软件包来部署、作为 DaemonSet 来部署或者手动部署。

规范目录 /var/lib/kubelet/device-plugins 是需要特权访问的,所以设备插件

必须要在被授权的安全的上下文中运行。

如果你将设备插件部署为 DaemonSet,/var/lib/kubelet/device-plugins 目录必须要在插件的 [PodSpec] version 中声明作为 volume 被挂载到插件中。

如果你选择 DaemonSet 方法,你可以通过 Kubernetes 进行以下操作:

将设备插件的 Pod 放置在节点上,在出现故障后重新启动守护进程 Pod,来进行自动升级。

API 兼容性

Kubernetes 设备插件支持还处于 beta 版本。所以在稳定版本出来之前 API 会以不兼容的方式进行更改。

作为一个项目,Kubernetes 建议设备插件开发者:

  • 注意未来版本的更改
  • 支持多个版本的设备插件 API,以实现向后/向前兼容性。

如果你启用 DevicePlugins 功能,并在需要升级到 Kubernetes 版本来获得较新的设备插件 API

版本的节点上运行设备插件,请在升级这些节点之前先升级设备插件以支持这两个版本。

采用该方法将确保升级期间设备分配的连续运行。

监控设备插件资源

为了监控设备插件提供的资源,监控代理程序需要能够发现节点上正在使用的设备,

并获取元数据来描述哪个指标与容器相关联。

设备监控代理暴露给 Prometheus 的指标应该遵循 [Kubernetes Instrumentation Guidelines],

使用 podnamespacecontainer 标签来标识容器。

kubelet 提供了 gRPC 服务来使得正在使用中的设备被发现,并且还未这些设备提供了元数据:

// PodResourcesLister 是一个由 kubelet 提供的服务,用来提供供节点上 
// Pod 和容器使用的节点资源的信息
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
}

List gRPC 端点

这一 List 端点提供运行中 Pod 的资源信息,包括类似独占式分配的 CPU ID、设备插件所报告的设备 ID 以及这些设备分配所处的 NUMA 节点 ID。

此外,对于基于 NUMA 的机器,它还会包含为容器保留的内存和大页的信息。

// ListPodResourcesResponse 是 List 函数的响应
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}
// PodResources 包含关于分配给 Pod 的节点资源的信息
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}
// ContainerResources 包含分配给容器的资源的信息
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
}
// ContainerMemory 包含分配给容器的内存和大页信息
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}
// Topology 描述资源的硬件拓扑结构
message TopologyInfo {
        repeated NUMANode nodes = 1;
}
// NUMA 代表的是 NUMA 节点
message NUMANode {
        int64 ID = 1;
}
// ContainerDevices 包含分配给容器的设备信息
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

List 端点中的 ContainerResources 中的 cpu_ids 对应于分配给某个容器的专属 CPU。

如果要统计共享池中的 CPU,List 端点需要与 GetAllocatableResources 端点一起使用,如下所述:

  1. 调用 GetAllocatableResources 获取所有可用的 CPU。
  2. 在系统中所有的 ContainerResources 上调用 GetCpuIds
  3. GetAllocatableResources 获取的 CPU 数减去 GetCpuIds 获取的 CPU 数。

GetAllocatableResources gRPC 端点

端点 GetAllocatableResources 提供工作节点上原始可用的资源信息。

此端点所提供的信息比导出给 API 服务器的信息更丰富。

GetAllocatableResources 应该仅被用于评估一个节点上的[可分配的]资源。

如果目标是评估空闲/未分配的资源,此调用应该与 List() 端点一起使用。

除非暴露给 kubelet 的底层资源发生变化,否则 GetAllocatableResources 得到的结果将保持不变。

这种情况很少发生,但当发生时(例如:热插拔,设备健康状况改变),客户端应该调用 GetAlloctableResources 端点。

然而,调用 GetAllocatableResources 端点在 cpu、内存被更新的情况下是不够的, Kubelet 需要重新启动以获取正确的资源容量和可分配的资源。

// AllocatableResourcesResponses 包含 kubelet 所了解到的所有设备的信息
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

从 Kubernetes v1.23 开始,GetAllocatableResources 被默认启用。

你可以通过关闭 KubeletPodResourcesGetAllocatable [特性门控]来禁用。

在 Kubernetes v1.23 之前,要启用这一功能,kubelet 必须用以下标志启动: --feature-gates=KubeletPodResourcesGetAllocatable=true

ContainerDevices 会向外提供各个设备所隶属的 NUMA 单元这类拓扑信息。 NUMA 单元通过一个整数 ID 来标识,其取值与设备插件所报告的一致。 [设备插件注册到 kubelet 时]

会报告这类信息。

gRPC 服务通过 /var/lib/kubelet/pod-resources/kubelet.sock 的 UNIX 套接字来提供服务。

设备插件资源的监控代理程序可以部署为守护进程或者 DaemonSet。

规范的路径 /var/lib/kubelet/pod-resources 需要特权来进入,

所以监控代理程序必须要在获得授权的安全的上下文中运行。

如果设备监控代理以 DaemonSet 形式运行,必须要在插件的 [PodSpec] version ##podspec-v1-core)

中声明将 /var/lib/kubelet/pod-resources 目录以

卷 的形式被挂载到设备监控代理中。

对 “PodResourcesLister 服务”的支持要求启用 KubeletPodResources [特性门控]。

从 Kubernetes 1.15 开始默认启用,自从 Kubernetes 1.20 开始为 v1。

设备插件与拓扑管理器的集成

拓扑管理器是 Kubelet 的一个组件,它允许以拓扑对齐方式来调度资源。

为了做到这一点,设备插件 API 进行了扩展来包括一个 TopologyInfo 结构体。

message TopologyInfo {
 repeated NUMANode nodes = 1;
}
message NUMANode {
    int64 ID = 1;
}

设备插件希望拓扑管理器可以将填充的 TopologyInfo 结构体作为设备注册的一部分以及设备 ID

和设备的运行状况发送回去。然后设备管理器将使用此信息来咨询拓扑管理器并做出资源分配决策。 TopologyInfo 支持定义 nodes 字段,允许为 nil(默认)或者是一个 NUMA 节点的列表。

这样就可以使设备插件可以跨越 NUMA 节点去发布。

下面是一个由设备插件为设备填充 TopologyInfo 结构体的示例:

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

设备插件示例

下面是一些设备插件实现的示例:

文章列表

更多推荐

更多
  • AWS自动化机器学习-十一、MLSDLC 的持续集成、部署和训练 技术要求,编纂持续集成阶段,管理持续部署阶段,管理持续训练,延伸,构建集成工件,构建测试工件,构建生产工件,自动化持续集成流程,回顾构建阶段,回顾测试阶段,审查部署和维护阶段,回顾应用用户体验,创建新的鲍鱼调查数据,回顾持续训练流程,清
    Apache CN

  • AWS自动化机器学习-六、使用 AWS 步骤函数自动化机器学习过程 技术要求,介绍 AWS 步骤功能,使用 Step 函数 Data Science SDK for CI/CD,建立 CI/CD 渠道资源,创建状态机,解决状态机的复杂性,更新开发环境,创建管道工件库,构建管道应用构件,部署 CI/CD
    Apache CN

  • AWS自动化机器学习-第三部分:优化以源代码为中心的自动化机器学习方法 本节将向您介绍整体 CI/CD 流程的局限性,以及如何将 ML 从业者的角色进一步整合到管道构建流程中。本节还将介绍这种角色集成如何简化自动化过程,并通过向您介绍 AWS Step 函数向您展示一种优化的方法。本节包括以下章节:
    Apache CN

  • AWS自动化机器学习-一、AWS 上的自动化机器学习入门 技术要求,洗钱流程概述,洗钱过程的复杂性,端到端 ML 流程示例,AWS 如何使 ML 开发和部署过程更容易自动化,介绍 ACME 渔业物流,ML 的情况,从数据中获得洞察力,建立正确的模型,训练模型,评估训练好的模型,探索可能的后续步
    Apache CN

  • AWS自动化机器学习-二、使用 SageMaker 自动驾驶器自动化机器学习模型开发 技术要求,介绍 AWS AI 和 ML 前景,SageMaker 自动驾驶器概述,利用 SageMaker 自动驾驶器克服自动化挑战,使用 SageMaker SDK 自动化 ML 实验,SageMaker Studio 入门,准备实验
    Apache CN

  • AWS自动化机器学习-四、机器学习的持续集成和持续交(CI/CD) 四、机器学习的持续集成和持续交CI/CD技术要求,介绍 CI/CD 方法,通过 CI/CD 实现 ML 自动化,在 AWS 上创建 CI/CD 管道,介绍 CI/CD 的 CI 部分,介绍 CI/CD 的 CD 部分,结束循环,采取以部
    Apache CN

  • AWS自动化机器学习-九、使用 Amazon Managed Workflows 为 Apache AirFlow 构建 ML 工作流 技术要求,开发以数据为中心的工作流程,创建合成鲍鱼调查数据,执行以数据为中心的工作流程,构建和单元测试数据 ETL 工件,构建气流 DAG,清理, 在前面的年龄计算器示例中,我们了解了如何通过 ML 从业者和开发人员团队之间的跨职能
    Apache CN

  • AWS自动化机器学习-七、使用 AWS 步骤函数构建 ML 工作流 技术要求,构建状态机工作流,执行集成测试,监控管道进度,设置服务权限,创建 ML 工作流程, 在本章中,我们将从第六章中的 [处继续,使用 AWS 步骤函数自动化机器学习过程。您将从那一章中回忆起,我们正在努力实现的主要目标是简化
    Apache CN

  • AWS自动化机器学习-八、使用 Apache Airflow 实现机器学习过程的自动化 技术要求,介绍阿帕奇气流,介绍亚马逊 MWAA,利用气流处理鲍鱼数据集,配置 MWAA 系统的先决条件,配置 MWAA 环境, 当建立一个 ML 模型时,有一个所有 ML 从业者都知道的基本原则;也就是说,最大似然模型只有在数据被训练时
    Apache CN

  • AWS自动化机器学习-五、自动化 ML 模型的持续部署 技术要求,部署 CI/CD 管道,构建 ML 模型工件,执行自动化 ML 模型部署,整理管道结构,创建 CDK 应用,部署管道应用,查看建模文件,审查申请文件,查看模型服务文件,查看容器构建文件,提交 ML 工件,清理, 在 [第 4
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多