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

具備儲存容量追蹤功能的臨時性磁碟區:更強大的 EmptyDir

有些應用程式需要額外的儲存空間,但不關心資料是否在重新啟動後持續保存。例如,快取服務通常受限於記憶體大小,並且可以將不常使用的資料移至速度較慢於記憶體的儲存空間中,而對整體效能影響很小。其他應用程式則期望某些唯讀輸入資料以檔案形式存在,例如組態資料或密鑰。

Kubernetes 已經支援幾種這類的臨時卷,但這些功能僅限於 Kubernetes 內部實作的功能。

CSI 臨時卷使使用 CSI 驅動程式擴展 Kubernetes 成為可能,這些驅動程式提供輕量級的本機卷。這些卷注入任意狀態,例如組態、密鑰、身分、變數或類似資訊。CSI 驅動程式必須修改以支援此 Kubernetes 功能,也就是說,一般的、符合標準的 CSI 驅動程式將無法運作,並且根據設計,此類卷應該可以在為 Pod 選擇的任何節點上使用。

對於在節點上消耗大量資源的卷或僅在某些節點上可用的特殊儲存空間而言,這是個問題。因此,Kubernetes 1.19 針對概念上更像 EmptyDir 卷的卷引入了兩個新的 Alpha 功能

新方法的優點是

  • 儲存空間可以是本機或網路連接的。
  • 卷可以具有固定的容量大小,應用程式永遠無法超過。
  • 適用於任何支援持久卷佈建的 CSI 驅動程式,以及(對於容量追蹤)實作 CSI GetCapacity 呼叫的驅動程式。
  • 卷可能具有一些初始資料,具體取決於驅動程式和參數。
  • 支援所有典型的卷操作(快照、調整大小、未來的儲存容量追蹤等)。
  • 這些卷可與任何接受 Pod 或卷規範的應用程式控制器一起使用。
  • Kubernetes 排程器本身會選擇合適的節點,也就是說,不再需要實作和配置排程器擴充器和變更 Webhook。

這使得通用臨時卷成為多種使用案例的合適解決方案

使用案例

持久記憶體作為 memcached 的 DRAM 替代品

memcached 的最新版本新增了支援使用持久記憶體 (PMEM) 而不是標準 DRAM。當透過其中一個應用程式控制器部署 memcached 時,通用臨時卷可以使用 CSI 驅動程式 (如 PMEM-CSI) 請求特定大小的 PMEM 卷。

本機 LVM 儲存空間作為暫存空間

處理超過 RAM 大小的資料集的應用程式可以請求具有效能特性或大小的本機儲存空間,而這不是一般的 Kubernetes EmptyDir 卷所能滿足的。例如,TopoLVM 就是為此目的而編寫的。

對包含資料的卷進行唯讀存取

佈建卷可能會產生非空卷

此類卷可以唯讀方式掛載。

運作方式

通用臨時卷

通用臨時卷背後的關鍵概念是一個新的卷來源,所謂的 EphemeralVolumeSource 包含建立卷聲明 (歷史上稱為持久卷聲明,PVC) 所需的所有欄位。kube-controller-manager 中的一個新控制器等待嵌入此類卷來源的 Pod,然後為該 Pod 建立 PVC。對於 CSI 驅動程式部署,該 PVC 看起來與任何其他 PVC 相同,因此不需要特殊支援。

只要這些 PVC 存在,它們就可以像任何其他卷聲明一樣使用。特別是,它們可以作為卷克隆或快照中的資料來源被引用。PVC 物件還保存卷的當前狀態。

自動建立的 PVC 的命名是確定性的:名稱是 Pod 名稱和卷名稱的組合,中間用連字號 (-) 分隔。這種確定性命名使得與 PVC 互動更加容易,因為一旦 Pod 名稱和卷名稱已知,就不必搜尋它。缺點是名稱可能已被使用。Kubernetes 會偵測到這一點,然後阻止 Pod 啟動。

為了確保卷與 Pod 一起刪除,控制器使 Pod 成為卷聲明的擁有者。當 Pod 被刪除時,正常的垃圾收集機制也會移除聲明,從而移除卷。

聲明透過正常的儲存類別機制選擇儲存驅動程式。雖然同時具有立即和延遲綁定(又名 WaitForFirstConsumer)的儲存類別都受支援,但對於臨時卷,使用 WaitForFirstConsumer 更有意義:然後 Pod 排程可以在選擇節點時同時考慮節點利用率和儲存空間的可用性。這就是另一個新功能發揮作用的地方。

儲存容量追蹤

通常,Kubernetes 排程器沒有關於 CSI 驅動程式可能在何處建立卷的資訊。它也沒有辦法直接與 CSI 驅動程式對話以檢索該資訊。因此,它會嘗試不同的節點,直到找到一個可以使所有卷都可用的節點(延遲綁定),或完全由驅動程式選擇位置(立即綁定)。

新的 CSIStorageCapacity Alpha API 允許將必要的資訊儲存在 etcd 中,排程器可以在其中使用它。與對通用臨時卷的支援相反,儲存容量追蹤必須在部署 CSI 驅動程式時啟用:必須告知 external-provisioner 發布容量資訊,然後它透過正常的 GetCapacity 呼叫從 CSI 驅動程式檢索該資訊。

當 Kubernetes 排程器需要為具有使用延遲綁定的未綁定卷的 Pod 選擇節點,並且 CSI 驅動程式部署已透過設定 CSIDriver.storageCapacity 標誌 標誌選擇使用該功能時,排程器會自動篩選掉無法存取足夠儲存容量的節點。這適用於通用臨時卷和持久卷,但不適用於 CSI 臨時卷,因為這些卷的參數對於 Kubernetes 是不透明的。

與往常一樣,具有立即綁定的卷會在排程 Pod 之前建立,其位置由儲存驅動程式選擇。因此,external-provisioner 的預設組態會跳過具有立即綁定的儲存類別,因為無論如何都不會使用該資訊。

由於 Kubernetes 排程器必須根據可能過時的資訊採取行動,因此無法確保在要建立卷時容量仍然可用。儘管如此,無需重試即可建立卷的可能性應該更高。

安全性

CSIStorageCapacity

CSIStorageCapacity 物件是命名空間化的。當在自己的命名空間中部署每個 CSI 驅動程式,並按照建議將 CSIStorageCapacity 的 RBAC 權限限制在該命名空間時,資料的來源始終是顯而易見的。但是,Kubernetes 不會檢查這一點,並且通常驅動程式會安裝在同一個命名空間中,因此最終驅動程式預期會正常運作,並且不會發布不正確的資料。

通用臨時卷

如果使用者有權限建立 Pod(直接或間接),那麼即使他們沒有權限建立卷聲明,他們也可以建立通用臨時卷。這是因為 RBAC 權限檢查應用於建立 PVC 的控制器,而不是原始使用者。這是一個根本性的改變,在啟用不應有權限建立卷的非信任使用者叢集中的功能之前,必須加以考慮

範例

PMEM-CSI 中的一個特殊分支包含在 QEMU VM 內部啟動 Kubernetes 1.19 叢集並啟用兩個 Alpha 功能所需的所有變更。PMEM-CSI 驅動程式程式碼保持不變,僅更新了部署。

在合適的機器上(Linux,非 root 使用者可以使用 Docker - 請參閱 PMEM-CSI 文件中的 QEMU 和 Kubernetes 章節),以下命令啟動叢集並安裝 PMEM-CSI 驅動程式

git clone --branch=kubernetes-1-19-blog-post https://github.com/intel/pmem-csi.git
cd pmem-csi
export TEST_KUBERNETES_VERSION=1.19 TEST_FEATURE_GATES=CSIStorageCapacity=true,GenericEphemeralVolume=true TEST_PMEM_REGISTRY=intel
make start && echo && test/setup-deployment.sh

如果一切順利,輸出將包含以下使用說明

The test cluster is ready. Log in with [...]/pmem-csi/_work/pmem-govm/ssh.0, run
kubectl once logged in.  Alternatively, use kubectl directly with the
following env variable:
   KUBECONFIG=[...]/pmem-csi/_work/pmem-govm/kube.config

secret/pmem-csi-registry-secrets created
secret/pmem-csi-node-secrets created
serviceaccount/pmem-csi-controller created
...
To try out the pmem-csi driver ephemeral volumes:
   cat deploy/kubernetes-1.19/pmem-app-ephemeral.yaml |
   [...]/pmem-csi/_work/pmem-govm/ssh.0 kubectl create -f -

CSIStorageCapacity 物件不適合人類閱讀,因此需要一些後處理。以下 Golang 範本會依範例使用的儲存類別篩選所有物件,並印出名稱、拓撲和容量

kubectl get \
        -o go-template='{{range .items}}{{if eq .storageClassName "pmem-csi-sc-late-binding"}}{{.metadata.name}} {{.nodeTopology.matchLabels}} {{.capacity}}
{{end}}{{end}}' \
        csistoragecapacities
csisc-2js6n map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker2] 30716Mi
csisc-sqdnt map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker1] 30716Mi
csisc-ws4bv map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker3] 30716Mi

一個個別物件具有以下內容

kubectl describe csistoragecapacities/csisc-6cw8j
Name:         csisc-sqdnt
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  storage.k8s.io/v1alpha1
Capacity:     30716Mi
Kind:         CSIStorageCapacity
Metadata:
  Creation Timestamp:  2020-08-11T15:41:03Z
  Generate Name:       csisc-
  Managed Fields:
    ...
  Owner References:
    API Version:     apps/v1
    Controller:      true
    Kind:            StatefulSet
    Name:            pmem-csi-controller
    UID:             590237f9-1eb4-4208-b37b-5f7eab4597d1
  Resource Version:  2994
  Self Link:         /apis/storage.k8s.io/v1alpha1/namespaces/default/csistoragecapacities/csisc-sqdnt
  UID:               da36215b-3b9d-404a-a4c7-3f1c3502ab13
Node Topology:
  Match Labels:
    pmem-csi.intel.com/node:  pmem-csi-pmem-govm-worker1
Storage Class Name:           pmem-csi-sc-late-binding
Events:                       <none>

現在讓我們建立包含一個通用臨時卷的範例應用程式。pmem-app-ephemeral.yaml 檔案包含

# This example Pod definition demonstrates
# how to use generic ephemeral inline volumes
# with a PMEM-CSI storage class.
kind: Pod
apiVersion: v1
metadata:
  name: my-csi-app-inline-volume
spec:
  containers:
    - name: my-frontend
      image: intel/pmem-csi-driver-test:v0.7.14
      command: [ "sleep", "100000" ]
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-volume
  volumes:
  - name: my-csi-volume
    ephemeral:
      volumeClaimTemplate:
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 4Gi
          storageClassName: pmem-csi-sc-late-binding

如上述使用說明所示建立該檔案後,我們有一個額外的 Pod 和 PVC

kubectl get pods/my-csi-app-inline-volume -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP          NODE                         NOMINATED NODE   READINESS GATES
my-csi-app-inline-volume   1/1     Running   0          6m58s   10.36.0.2   pmem-csi-pmem-govm-worker1   <none>           <none>
kubectl get pvc/my-csi-app-inline-volume-my-csi-volume
NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS               AGE
my-csi-app-inline-volume-my-csi-volume   Bound    pvc-c11eb7ab-a4fa-46fe-b515-b366be908823   4Gi        RWO            pmem-csi-sc-late-binding   9m21s

該 PVC 歸 Pod 所有

kubectl get -o yaml pvc/my-csi-app-inline-volume-my-csi-volume
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    pv.kubernetes.io/bind-completed: "yes"
    pv.kubernetes.io/bound-by-controller: "yes"
    volume.beta.kubernetes.io/storage-provisioner: pmem-csi.intel.com
    volume.kubernetes.io/selected-node: pmem-csi-pmem-govm-worker1
  creationTimestamp: "2020-08-11T15:44:57Z"
  finalizers:
  - kubernetes.io/pvc-protection
  managedFields:
    ...
  name: my-csi-app-inline-volume-my-csi-volume
  namespace: default
  ownerReferences:
  - apiVersion: v1
    blockOwnerDeletion: true
    controller: true
    kind: Pod
    name: my-csi-app-inline-volume
    uid: 75c925bf-ca8e-441a-ac67-f190b7a2265f
...

最終,pmem-csi-pmem-govm-worker1 的儲存容量資訊也會更新

csisc-2js6n map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker2] 30716Mi
csisc-sqdnt map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker1] 26620Mi
csisc-ws4bv map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker3] 30716Mi

如果另一個應用程式需要超過 26620Mi,Kubernetes 排程器將不再選擇 pmem-csi-pmem-govm-worker1

後續步驟

這兩個功能都在開發中。在 Alpha 審查過程中已經提出了幾個未解決的問題。這兩個增強提案記錄了遷移到 Beta 所需的工作,以及已經考慮和拒絕的替代方案

您的意見回饋對於推動該開發至關重要。SIG-Storage 定期舉行會議,並且可以透過 Slack 和郵件列表聯繫。