裝置外掛

裝置外掛允許你配置叢集以支援需要供應商特定設定的裝置或資源,例如 GPU、網絡卡 (NIC)、FPGA 或非易失性主記憶體。
功能狀態: Kubernetes v1.26 [stable]

Kubernetes 提供了一個裝置外掛框架,你可以使用它向 Kubelet 宣告系統硬體資源。

供應商可以實現一個裝置外掛,你可以手動部署或作為 DaemonSet 部署,而不是自定義 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 負責將這些資源作為 kubelet 節點狀態更新的一部分,通告給 API 伺服器。例如,當裝置外掛向 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:3.8
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# This Pod needs 2 of the hardware-vendor.example/foo devices
# and can only schedule onto a Node that's able to satisfy
# that need.
#
# If the Node has more than 2 of those devices available, the
# remainder would be available for other Pods to use.

裝置外掛實現

裝置外掛的一般工作流程包括以下步驟:

  1. 初始化。在此階段,裝置外掛執行供應商特定的初始化和設定,以確保裝置處於就緒狀態。

  2. 該外掛啟動一個 gRPC 服務,該服務在主機路徑 /var/lib/kubelet/device-plugins/ 下的 Unix 套接字上,實現了以下介面:

    service DevicePlugin {
          // GetDevicePluginOptions returns options to be communicated with Device Manager.
          rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
          // ListAndWatch returns a stream of List of Devices
          // Whenever a Device state change or a Device disappears, ListAndWatch
          // returns the new list
          rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
          // Allocate is called during container creation so that the Device
          // Plugin can run device specific operations and instruct Kubelet
          // of the steps to make the Device available in the container
          rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
          // GetPreferredAllocation returns a preferred set of devices to allocate
          // from a list of available ones. The resulting preferred allocation is not
          // guaranteed to be the allocation ultimately performed by the
          // devicemanager. It is only designed to help the devicemanager make a more
          // informed allocation decision when possible.
          rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
          // PreStartContainer is called, if indicated by Device Plugin during registration phase,
          // before each container start. Device plugin can run device specific operations
          // such as resetting the device before making devices available to the container.
          rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  3. 外掛透過主機路徑 /var/lib/kubelet/device-plugins/kubelet.sock 上的 Unix 套接字向 kubelet 註冊自己。

  4. 成功註冊後,裝置外掛以服務模式執行,在此期間它會持續監控裝置健康狀況並在任何裝置狀態變化時向 kubelet 報告。它還負責處理 Allocate gRPC 請求。在 Allocate 期間,裝置外掛可能會進行裝置特定的準備工作;例如,GPU 清理或 QRNG 初始化。如果操作成功,裝置外掛會返回一個 AllocateResponse,其中包含用於訪問已分配裝置的容器執行時配置。kubelet 會將此資訊傳遞給容器執行時。

    AllocateResponse 包含零個或多個 ContainerAllocateResponse 物件。在這些物件中,裝置外掛定義了必須對容器定義進行的修改,以提供對裝置的訪問。這些修改包括:

    • 註解
    • 裝置節點
    • 環境變數
    • 掛載點
    • 完全限定的 CDI 裝置名稱

處理 kubelet 重啟

裝置外掛應檢測 kubelet 重啟,並重新向新的 kubelet 例項註冊自己。新的 kubelet 例項在啟動時會刪除 /var/lib/kubelet/device-plugins 下所有現有的 Unix 套接字。裝置外掛可以監控其 Unix 套接字的刪除,並在發生此類事件時重新註冊自己。

裝置外掛和不健康的裝置

有時裝置會發生故障或關閉。在這種情況下,裝置外掛的責任是使用 ListAndWatchResponse API 通知 kubelet 情況。

一旦裝置被標記為不健康,kubelet 將減少節點上該資源的可分配數量,以反映可用於排程新 Pod 的裝置數量。資源的容量數量不會改變。

分配給故障裝置的 Pod 將繼續分配給該裝置。通常情況下,依賴於該裝置的程式碼將開始失敗,如果 Pod 的 restartPolicy 不是 Always,Pod 可能會進入 Failed 階段,否則會進入崩潰迴圈。

在 Kubernetes v1.31 之前,瞭解 Pod 是否與故障裝置關聯的方法是使用 PodResources API

特性狀態: Kubernetes v1.31 [alpha] (預設啟用:false)

透過啟用功能門控 ResourceHealthStatus.status 中每個 Pod 的每個容器狀態都會新增 allocatedResourcesStatus 欄位。allocatedResourcesStatus 欄位報告分配給容器的每個裝置的健康資訊。

對於失敗的 Pod,或者你懷疑有故障的 Pod,你可以使用此狀態來了解 Pod 行為是否可能與裝置故障有關。例如,如果加速器報告過溫事件,allocatedResourcesStatus 欄位可能能夠報告此情況。

裝置外掛部署

你可以將裝置外掛部署為 DaemonSet、作為節點作業系統的包,或者手動部署。

規範目錄 /var/lib/kubelet/device-plugins 需要特權訪問,因此裝置外掛必須在特權安全上下文中執行。如果你將裝置外掛部署為 DaemonSet,/var/lib/kubelet/device-plugins 必須作為 掛載到外掛的 PodSpec 中。

如果你選擇 DaemonSet 方式,你可以依賴 Kubernetes 來:將裝置外掛的 Pod 放置到節點上,在故障後重新啟動守護程序 Pod,並幫助自動化升級。

API 相容性

以前,版本控制方案要求裝置外掛的 API 版本與 Kubelet 的版本完全匹配。自此功能在 v1.12 升級到 Beta 版後,這不再是硬性要求。該 API 已版本化,並且自此功能升級到 Beta 版後一直保持穩定。因此,kubelet 升級應該無縫進行,但在穩定之前 API 可能仍然會發生變化,導致升級無法保證不中斷。

作為專案,Kubernetes 建議裝置外掛開發人員:

  • 關注未來版本中裝置外掛 API 的變化。
  • 支援多個裝置外掛 API 版本以實現向後/向前相容性。

要在需要升級到具有更新裝置外掛 API 版本的 Kubernetes 版本的節點上執行裝置外掛,請在升級這些節點之前升級你的裝置外掛以支援這兩個版本。採用這種方法將確保在升級期間裝置分配的持續執行。

監控裝置外掛資源

特性狀態: Kubernetes v1.28 [stable]

為了監控裝置外掛提供的資源,監控代理需要能夠發現節點上正在使用的裝置集合,並獲取元資料以描述指標應與哪個容器關聯。Prometheus 監控代理暴露的指標應遵循 Kubernetes 觀測指導原則,使用 podnamespacecontainer prometheus 標籤來識別容器。

kubelet 提供一個 gRPC 服務,用於發現正在使用的裝置並提供這些裝置的元資料。

// PodResourcesLister is a service provided by the kubelet that provides information about the
// node resources consumed by pods and containers on the node
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
    rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse) {}
}

List gRPC 端點

List 端點提供執行中的 Pod 資源的詳細資訊,例如獨佔分配的 CPU ID、裝置外掛報告的裝置 ID 以及這些裝置分配到的 NUMA 節點 ID。此外,對於基於 NUMA 的機器,它還包含為容器預留的記憶體和 Hugepages 資訊。

從 Kubernetes v1.27 開始,List 端點可以提供由 DynamicResourceAllocation API 在 ResourceClaims 中分配的執行中 Pod 資源的資訊。從 Kubernetes v1.34 開始,此功能預設啟用。要停用此功能,必須使用以下標誌啟動 kubelet

--feature-gates=KubeletPodResourcesDynamicResources=false
// ListPodResourcesResponse is the response returned by List function
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources contains information about the node resources assigned to a pod
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources contains information about the resources assigned to a container
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
    repeated DynamicResource dynamic_resources = 5;
}

// ContainerMemory contains information about memory and hugepages assigned to a container
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// Topology describes hardware topology of the resource
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA representation of NUMA node
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices contains information about the devices assigned to a container
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

// DynamicResource contains information about the devices assigned to a container by Dynamic Resource Allocation
message DynamicResource {
    string class_name = 1;
    string claim_name = 2;
    string claim_namespace = 3;
    repeated ClaimResource claim_resources = 4;
}

// ClaimResource contains per-plugin resource information
message ClaimResource {
    repeated CDIDevice cdi_devices = 1 [(gogoproto.customname) = "CDIDevices"];
}

// CDIDevice specifies a CDI device information
message CDIDevice {
    // Fully qualified CDI device name
    // for example: vendor.com/gpu=gpudevice1
    // see more details in the CDI specification:
    // https://github.com/container-orchestrated-devices/container-device-interface/blob/main/SPEC.md
    string name = 1;
}

GetAllocatableResources gRPC 端點

特性狀態: Kubernetes v1.28 [stable]

GetAllocatableResources 提供工作節點上最初可用資源的資訊。它比 kubelet 匯出到 APIServer 的資訊更多。

// AllocatableResourcesResponses contains information about all the devices known by the kubelet
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices 暴露拓撲資訊,宣告裝置與哪些 NUMA 單元相關聯。NUMA 單元使用不透明的整數 ID 進行標識,該值與裝置外掛向 kubelet 註冊時報告的值一致。

gRPC 服務透過 Unix 套接字 /var/lib/kubelet/pod-resources/kubelet.sock 提供。裝置外掛資源的監控代理可以作為守護程序部署,也可以作為 DaemonSet 部署。規範目錄 /var/lib/kubelet/pod-resources 需要特權訪問,因此監控代理必須在特權安全上下文中執行。如果裝置監控代理作為 DaemonSet 執行,/var/lib/kubelet/pod-resources 必須作為 掛載到裝置監控代理的 PodSpec 中。

Get gRPC 端點

特性狀態: Kubernetes v1.34 [beta]

Get 端點提供有關正在執行的 Pod 資源的資訊。它暴露的資訊與 List 端點中描述的資訊類似。Get 端點需要正在執行的 Pod 的 PodNamePodNamespace

// GetPodResourcesRequest contains information about the pod
message GetPodResourcesRequest {
    string pod_name = 1;
    string pod_namespace = 2;
}

要停用此功能,你必須使用以下標誌啟動 kubelet 服務:

--feature-gates=KubeletPodResourcesGet=false

Get 端點可以提供與動態資源分配 API 分配的動態資源相關的 Pod 資訊。從 Kubernetes v1.34 開始,此功能預設啟用。要停用此功能,kubelet 必須使用以下標誌啟動:

--feature-gates=KubeletPodResourcesDynamicResources=false

裝置外掛與拓撲管理器整合

特性狀態: Kubernetes v1.27 [穩定]

拓撲管理器是 Kubelet 的一個元件,它允許以拓撲對齊的方式協調資源。為此,裝置外掛 API 已擴充套件為包含 TopologyInfo 結構。

message TopologyInfo {
    repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

希望利用拓撲管理器的裝置外掛可以在設備註冊時,連同裝置 ID 和裝置健康狀況一起,返回填充好的 TopologyInfo 結構。然後,裝置管理器將使用此資訊與拓撲管理器協商並做出資源分配決策。

TopologyInfo 支援將 nodes 欄位設定為 nil 或 NUMA 節點列表。這允許裝置外掛通告跨多個 NUMA 節點的裝置。

TopologyInfo 設定為 nil 或為給定裝置提供空的 NUMA 節點列表,表示裝置外掛對該裝置沒有 NUMA 親和性偏好。

裝置外掛為裝置填充的 TopologyInfo 結構示例

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

裝置外掛示例

以下是一些裝置外掛實現的示例:

下一步

此頁面上的專案是指提供 Kubernetes 所需功能的第三方產品或專案。Kubernetes 專案的作者不對這些第三方產品或專案負責。有關更多詳細資訊,請參閱 CNCF 網站指南

在提議新增額外第三方連結的更改之前,你應該閱讀內容指南

上次修改時間:2025 年 8 月 30 日上午 11:36 PST:修正裝置外掛中的拼寫錯誤 (ac4a40a60f)