本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 1.26:用於動態資源分配的 Alpha API

動態資源分配是用於請求資源的新 API。它是用於通用資源的持久卷 API 的泛化,使得以下操作成為可能:

  • 在不同的 Pod 和容器中訪問相同的資源例項,
  • 為資源請求附加任意約束,以獲得你所需要的確切資源,
  • 根據使用者提供的引數初始化資源。

第三方資源驅動程式負責解釋這些引數,以及在請求進入時跟蹤和分配資源。

動態資源分配是一個 alpha 特性,只有在啟用 DynamicResourceAllocation 特性門控resource.k8s.io/v1alpha1 API 組時才會啟用。有關詳細資訊,請參閱 --feature-gates--runtime-config kube-apiserver 引數。kube-scheduler、kube-controller-manager 和 kubelet 元件也都需要啟用該特性門控。

kube-scheduler 的預設配置當且僅當該特性門控被啟用時才啟用 DynamicResources 外掛。自定義配置可能需要修改以包含它。

一旦啟用了動態資源分配,就可以安裝資源驅動程式來管理某些型別的硬體。Kubernetes 有一個用於端到端測試的測試驅動程式,但也可以手動執行。有關分步說明,請參見下文

API

新的 resource.k8s.io/v1alpha1 API 組提供了四種新型別

ResourceClass
定義哪個資源驅動程式處理某種型別的資源,併為其提供通用引數。ResourceClass 由叢集管理員在安裝資源驅動程式時建立。
ResourceClaim
定義工作負載所需的特定資源例項。由使用者建立(生命週期手動管理,可以在不同 Pod 之間共享),或由控制平面基於 ResourceClaimTemplate 為單個 Pod 建立(自動生命週期,通常僅由一個 Pod 使用)。
ResourceClaimTemplate
定義用於建立 ResourceClaim 的規約和一些元資料。由使用者在部署工作負載時建立。
PodScheduling
當需要為 Pod 分配 ResourceClaim 時,由控制平面和資源驅動程式在內部使用以協調 Pod 排程。

ResourceClass 和 ResourceClaim 的引數儲存在單獨的物件中,通常使用安裝資源驅動程式時建立的 CRD 所定義的型別。

啟用此 alpha 特性後,Pod 的 spec 定義了 Pod 執行所需的 ResourceClaim:此資訊進入一個新的 resourceClaims 欄位。該列表中的條目引用 ResourceClaim 或 ResourceClaimTemplate。當引用 ResourceClaim 時,所有使用此 .spec 的 Pod(例如,在 Deployment 或 StatefulSet 中)共享相同的 ResourceClaim 例項。當引用 ResourceClaimTemplate 時,每個 Pod 都會獲得自己的 ResourceClaim 例項。

對於在 Pod 中定義的容器,resources.claims 列表定義該容器是否可以訪問這些資源例項,這使得在同一 Pod 內的一個或多個容器之間共享資源成為可能。例如,一個 init 容器可以在應用程式使用資源之前對其進行設定。

這是一個虛構的資源驅動程式的示例。將為此 Pod 建立兩個 ResourceClaim 物件,每個容器都可以訪問其中一個。

假設已安裝名為 resource-driver.example.com 的資源驅動程式以及以下資源類

apiVersion: resource.k8s.io/v1alpha1
kind: ResourceClass
name: resource.example.com
driverName: resource-driver.example.com

然後,終端使用者可以如下分配兩個型別為 resource.example.com 的特定資源

---
apiVersion: cats.resource.example.com/v1
kind: ClaimParameters
name: large-black-cats
spec:
  color: black
  size: large
---
apiVersion: resource.k8s.io/v1alpha1
kind: ResourceClaimTemplate
metadata:
  name: large-black-cats
spec:
  spec:
    resourceClassName: resource.example.com
    parametersRef:
      apiGroup: cats.resource.example.com
      kind: ClaimParameters
      name: large-black-cats
–--
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-cats
spec:
  containers: # two example containers; each container claims one cat resource
  - name: first-example
    image: ubuntu:22.04
    command: ["sleep", "9999"]
    resources:
      claims:
      - name: cat-0
  - name: second-example
    image: ubuntu:22.04
    command: ["sleep", "9999"]
    resources:
      claims:
      - name: cat-1
  resourceClaims:
  - name: cat-0
    source:
      resourceClaimTemplateName: large-black-cats
  - name: cat-1
    source:
      resourceClaimTemplateName: large-black-cats

排程

與原生資源(如 CPU 或 RAM)和擴充套件資源(由裝置外掛管理,由 kubelet 公佈)不同,排程器不知道叢集中有哪些動態資源可用,或者如何將它們拆分以滿足特定 ResourceClaim 的要求。資源驅動程式負責這些。驅動程式一旦為 ResourceClaim 保留了資源,就會將其標記為**已分配(allocated)**。這也會告訴排程器所宣告的資源在叢集中的實際可用位置。

ResourceClaim 可以在建立後立即獲得資源分配(**立即分配**),而不考慮哪些 Pod 將使用該資源。預設行為(**等待第一個消費者**)是延遲分配,直到依賴於該 ResourceClaim 的 Pod 符合排程條件。這種具有兩種分配選項的設計類似於 Kubernetes 處理儲存供應的方式,即使用 PersistentVolumes 和 PersistentVolumeClaims。

在“等待第一個消費者”模式下,排程器會檢查 Pod 所需的所有 ResourceClaim。如果 Pod 有任何 ResourceClaim,排程器會建立一個 PodScheduling(一個代表 Pod 請求排程細節的特殊物件)。PodScheduling 與 Pod 具有相同的名稱和名稱空間,並以該 Pod 為其所有者。排程器使用其 PodScheduling 通知負責這些 ResourceClaim 的資源驅動程式,告知其排程器認為適合該 Pod 的節點。資源驅動程式透過排除那些沒有足夠驅動程式資源的節點來響應。

一旦排程器獲得該資源資訊,它就會選擇一個節點並將該選擇儲存在 PodScheduling 物件中。然後,資源驅動程式會根據相關的 ResourceClaim 分配資源,以確保資源在該選定的節點上可用。一旦資源分配完成,排程器就會嘗試將 Pod 排程到合適的節點。此時排程仍然可能失敗;例如,在此期間可能有另一個 Pod 被排程到同一節點。如果發生這種情況,已經分配的 ResourceClaim 可能會被取消分配,以便能夠排程到不同的節點上。

作為此過程的一部分,ResourceClaim 也會為該 Pod 保留。目前,ResourceClaim 要麼只能由單個 Pod 獨佔使用,要麼可以由無限數量的 Pod 使用。

一個關鍵特性是,除非 Pod 的所有資源都已分配和保留,否則不會將其排程到節點。這避免了 Pod 被排程到一個節點然後無法在那裡執行的情況,這是很糟糕的,因為這樣一個處於 pending 狀態的 Pod 也會阻塞為它預留的所有其他資源,如 RAM 或 CPU。

限制

排程器外掛必須參與排程使用 ResourceClaim 的 Pod。透過設定 nodeName 欄位繞過排程器會導致 kubelet 拒絕啟動 Pod,因為 ResourceClaim 未被保留,甚至未被分配。未來可能會消除此限制

編寫資源驅動程式

動態資源分配驅動程式通常由兩個獨立但相互協調的元件組成:一箇中心化的控制器,以及一個由節點本地 kubelet 外掛組成的 DaemonSet。中心化控制器與排程器協調所需的大部分工作可以由樣板程式碼處理。只需要定製實際分配外掛擁有的 ResourceClass 的 ResourceClaim 所需的業務邏輯。因此,Kubernetes 提供了以下包,包括用於呼叫此樣板程式碼的 API 以及一個您可以實現的 Driver 介面,以提供其自定義業務邏輯

同樣,可以使用樣板程式碼將節點本地外掛註冊到 kubelet,並啟動 gRPC 伺服器以實現 kubelet 外掛 API。對於用 Go 編寫的驅動程式,建議使用以下包

由驅動程式開發人員決定這兩個元件如何通訊。KEP 概述了一種使用 CRD 的方法

在 SIG Node 內部,我們還計劃提供一個完整的示例驅動程式,可以作為其他驅動程式的模板。

執行測試驅動程式

以下步驟直接從 Kubernetes 原始碼啟動一個本地的單節點叢集。作為先決條件,你的叢集必須有支援容器裝置介面(CDI)的容器執行時的節點。例如,你可以執行 CRI-O v1.23.2 或更高版本。一旦 containerd v1.7.0 釋出,我們預計你可以執行該版本或任何更高版本。在下面的示例中,我們使用 CRI-O。

首先,克隆 Kubernetes 原始碼。在該目錄中,執行

$ hack/install-etcd.sh
...

$ RUNTIME_CONFIG=resource.k8s.io/v1alpha1 \
  FEATURE_GATES=DynamicResourceAllocation=true \
  DNS_ADDON="coredns" \
  CGROUP_DRIVER=systemd \
  CONTAINER_RUNTIME_ENDPOINT=unix:///var/run/crio/crio.sock \
  LOG_LEVEL=6 \
  ENABLE_CSI_SNAPSHOTTER=false \
  API_SECURE_PORT=6444 \
  ALLOW_PRIVILEGED=1 \
  PATH=$(pwd)/third_party/etcd:$PATH \
  ./hack/local-up-cluster.sh -O
...

要開始使用您的叢集,您可以開啟另一個終端/選項卡並執行

$ export KUBECONFIG=/var/run/kubernetes/admin.kubeconfig

叢集啟動後,在另一個終端執行測試驅動程式控制器。必須為以下所有命令設定 KUBECONFIG

$ go run ./test/e2e/dra/test-driver --feature-gates ContextualLogging=true -v=5 controller

在另一個終端,執行 kubelet 外掛

$ sudo mkdir -p /var/run/cdi && \
  sudo chmod a+rwx /var/run/cdi /var/lib/kubelet/plugins_registry /var/lib/kubelet/plugins/
$ go run ./test/e2e/dra/test-driver --feature-gates ContextualLogging=true -v=6 kubelet-plugin

更改目錄的許可權使得普通使用者可以執行和(當使用 delve 時)除錯 kubelet 外掛,這很方便,因為它使用了已經填充的 Go 快取。完成後,記得用 sudo chmod go-w 恢復許可權。或者,你也可以構建二進位制檔案並以 root 身份執行。

現在叢集已準備好建立物件

$ kubectl create -f test/e2e/dra/test-driver/deploy/example/resourceclass.yaml
resourceclass.resource.k8s.io/example created

$ kubectl create -f test/e2e/dra/test-driver/deploy/example/pod-inline.yaml
configmap/test-inline-claim-parameters created
resourceclaimtemplate.resource.k8s.io/test-inline-claim-template created
pod/test-inline-claim created

$ kubectl get resourceclaims
NAME                         RESOURCECLASSNAME   ALLOCATIONMODE         STATE                AGE
test-inline-claim-resource   example             WaitForFirstConsumer   allocated,reserved   8s

$ kubectl get pods
NAME                READY   STATUS      RESTARTS   AGE
test-inline-claim   0/2     Completed   0          21s

測試驅動程式做的不多,它只設置在 ConfigMap 中定義的環境變數。測試 Pod 會轉儲環境,因此可以檢查日誌以驗證一切是否正常工作

$ kubectl logs test-inline-claim with-resource | grep user_a
user_a='b'

後續步驟

  • 有關設計的更多資訊,請參閱動態資源分配 KEP。
  • 請在 Kubernetes 官方文件中閱讀動態資源分配
  • 你可以參與 SIG Node 和/或 CNCF 容器編排裝置工作組
  • 您可以檢視或評論動態資源分配的專案看板
  • 為了將此功能推向 beta 階段,我們需要硬體供應商的反饋,因此我們在此呼籲:試用此功能,考慮它如何幫助解決您的使用者遇到的問題,並編寫資源驅動程式……