本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
具有儲存容量跟蹤的臨時卷:增強版的 EmptyDir
有些應用程式需要額外的儲存,但並不關心資料是否在重啟後持久儲存。例如,快取服務通常受記憶體大小限制,可以將不常用資料移動到比記憶體慢的儲存中,對整體效能影響很小。其他應用程式則期望某些只讀輸入資料以檔案的形式存在,例如配置資料或金鑰。
Kubernetes 已經支援多種此類臨時卷,但其功能僅限於 Kubernetes 內部實現的功能。
CSI 臨時卷使得透過提供輕量級本地卷的 CSI 驅動程式擴充套件 Kubernetes 成為可能。這些卷注入任意狀態,例如配置、金鑰、身份、變數或類似資訊。CSI 驅動程式必須經過修改才能支援此 Kubernetes 功能,即,正常的、符合標準 CSI 驅動程式將無法工作,並且根據設計,此類卷應可在為 Pod 選擇的任何節點上使用。
這對於在節點上消耗大量資源的卷,或僅在某些節點上可用的特殊儲存來說是存在問題的。因此,Kubernetes 1.19 引入了兩個新的 Alpha 功能,用於在概念上更像 EmptyDir
卷的卷:
新方法的優點是:
- 儲存可以是本地的或網路附加的。
- 卷可以具有固定大小,應用程式永遠無法超出該大小。
- 適用於任何支援持久卷供應和(對於容量跟蹤)實現 CSI
GetCapacity
呼叫的 CSI 驅動程式。 - 根據驅動程式和引數,卷可能具有一些初始資料。
- 支援所有典型的卷操作(快照、調整大小、未來的儲存容量跟蹤等)。
- 這些卷可用於任何接受 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 和郵件列表聯絡。