一. 簡介
PVC 和 PV 的設(shè)計,其實跟“面向?qū)ο蟆钡乃枷胪耆恢隆VC 可以理解為持久化存儲的“接口”,它提供了對某種持久化存儲的描述,但不提供具體的實現(xiàn),而 PV 負(fù)責(zé)完成持久化存儲的實現(xiàn)。而StorageClass 對象的作用,其實就是創(chuàng)建 PV 的模板,減少手動操作的重復(fù)工作以及錯誤情況。
關(guān)于本文的項目的代碼,都放于鏈接:GitHub資源
二. PV
2.1 Volume
容器的 Volume,其實就是將一個宿主機(jī)上的目錄,跟一個容器里的目錄綁定掛載在了一起。
而所謂的“持久化 Volume”,指的就是這個宿主機(jī)上的目錄,具備“持久性”。即:這個目錄里面的內(nèi)容,既不會因為容器的刪除而被清理掉,也不會跟當(dāng)前的宿主機(jī)綁定。
而我們常見的 hostPath 和 emptyDir 類型的 Volume 并不具備這個特征:它們既有可能被 kubelet 清理掉,也不能被“遷移”到其他節(jié)點上。這也是我們需要使用PV與PVC的原因。
2.2 PV持久化
PV的持久化可以分為2階段流程:
- Attach階段
這一步為虛擬機(jī)掛載遠(yuǎn)程磁盤的操作。Kubernetes 提供的可用參數(shù)是nodeName,即宿主機(jī)的名字。在 Kubernetes 中,我們把這個階段稱為Attach。 - Mount
這個將磁盤設(shè)備格式化并掛載到 Volume 宿主機(jī)目錄。Kubernetes 提供的可用參數(shù)是dir,即 Volume 的宿主機(jī)目錄。在 Kubernetes 中,我們把這個階段稱為Mount。
經(jīng)過了“兩階段處理”,我們就得到了一個“持久化”的 Volume 宿主機(jī)目錄。kubelet 只要把這個 Volume 目錄通過 CRI 里的 Mounts 參數(shù),傳遞給 Docker,然后就可以為 Pod 里的容器掛載這個“持久化”的 Volume 了。
2.3 PV與PVC綁定控制器
PersistentVolumeController 會不斷地查看當(dāng)前每一個 PVC,是不是已經(jīng)處于 Bound(已綁定)狀態(tài)。如果不是,那它就會遍歷所有的、可用的 PV,并嘗試將其與這個“單獨(dú)”的 PVC 進(jìn)行綁定。這樣,Kubernetes 就可以保證用戶提交的每一個 PVC,只要有合適的 PV 出現(xiàn),它就能夠很快進(jìn)入綁定狀態(tài)。
而所謂將一個 PV 與 PVC 進(jìn)行“綁定”,其實就是將這個 PV 對象的名字,填在了 PVC 對象的 spec.volumeName 字段上。所以,接下來 Kubernetes 只要獲取到這個 PVC 對象,就一定能夠找到它所綁定的 PV。
2.4 小結(jié)
關(guān)于 PV 的“兩階段處理”流程,是靠獨(dú)立于 kubelet 主控制循環(huán)(Kubelet Sync Loop)之外的兩個控制循環(huán)來實現(xiàn)的。
第一階段的 Attach(以及 Dettach)操作,是由
Volume Controller負(fù)責(zé)維護(hù)的,這個控制循環(huán)的名字叫作:AttachDetachController。而它的作用,就是不斷地檢查每一個 Pod 對應(yīng)的 PV,和這個 Pod 所在宿主機(jī)之間掛載情況。從而決定,是否需要對這個 PV 進(jìn)行Attach(或者 Dettach)操作。第二階段的
Mount(以及 Unmount)操作,必須發(fā)生在 Pod 對應(yīng)的宿主機(jī)上,所以它必須是 kubelet 組件的一部分。這個控制循環(huán)的名字,叫作:VolumeManagerReconciler,它運(yùn)行起來之后,是一個獨(dú)立于 kubelet 主循環(huán)的Goroutine。
通過這樣將 Volume 的處理同 kubelet 的主循環(huán)解耦,Kubernetes 就避免了這些耗時的遠(yuǎn)程掛載操作拖慢 kubelet 的主控制循環(huán),進(jìn)而導(dǎo)致 Pod 的創(chuàng)建效率大幅下降的問題。
三. StorageClass
StorageClass 對象的作用,其實就是創(chuàng)建 PV 的模板。
3.1 PV創(chuàng)建方式
- Static Provisioning
人工管理 PV 的方式就叫作 Static Provisioning,通過手動創(chuàng)建PV方式具有極高的重復(fù)性。 - Dynamic Provisioning
Dynamic Provisioning 機(jī)制工作的核心,在于一個名叫StorageClass的 API 對象。
3.2 StorageClass范圍
StorageClass 對象會定義如下兩個部分內(nèi)容:
- PV 的屬性。比如,存儲類型、Volume 的大小等等。
- 創(chuàng)建這種 PV 需要用到的存儲插件。比如,Ceph 等等。
有了這樣兩個信息之后,Kubernetes 就能夠根據(jù)用戶提交的 PVC,找到一個對應(yīng)的 StorageClass 了。然后,Kubernetes 就會調(diào)用該 StorageClass 聲明的存儲插件,創(chuàng)建出需要的 PV。
3.3 案例
3.3.1 StorageClass定義
“demo-storageclass.yaml”文件內(nèi)容如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: demo-storageclass
provisioner: docker.io/hostpath
parameters:
type: pd-ssd
在這個 YAML 文件里,我們定義了一個名叫 demo-storageclass 的 StorageClass。這個 StorageClass 的 provisioner 字段的值是:docker.io/hostpath,因為我使用的是mac docker desktop 。而這個 StorageClass 的 parameters 字段,就是 PV 的參數(shù)。比如:上面例子里的 type=pd-ssd。
3.3.2 PVC定義
“demo-pvc.yaml”的定義如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: demo-storageclass
resources:
requests:
storage: 1Gi
可以看到,我們在這個 PVC 里添加了一個叫作 storageClassName 的字段,用于指定該 PVC 所要使用的 StorageClass 的名字是:demo-storageclass。
3.3.3 驗證
通過按序創(chuàng)建StorageClass與PVC,指令如下:
kubectl apply -f demo-storageclass.yaml
kubectl apply -f demo-pvc.yaml
我們再通過如下的指令查看volume情況:
kubectl describe pvc demo-pvc
# result
Name: demo-pvc
Namespace: default
StorageClass: demo-storageclass
Status: Bound
Volume: pvc-cd36fd9f-d15a-49f7-836b-9b6605a75f87
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: docker.io/hostpath
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWO
VolumeMode: Filesystem
Mounted By: <none>
Events:
執(zhí)行如下指令,再查看對應(yīng)的PV內(nèi)容:
kubectl describe pv pvc-cd36fd9f-d15a-49f7-836b-9b6605a75f87
# result
Name: pvc-cd36fd9f-d15a-49f7-836b-9b6605a75f87
Labels: <none>
Annotations: docker.io/hostpath: /var/lib/k8s-pvs/demo-pvc/pvc-cd36fd9f-d15a-49f7-836b-9b6605a75f87
pv.kubernetes.io/provisioned-by: docker.io/hostpath
Finalizers: [kubernetes.io/pv-protection]
StorageClass: demo-storageclass
Status: Bound
Claim: default/demo-pvc
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
這個自動創(chuàng)建出來的 PV 的 StorageClass 字段的值,也是 demo-storageclass,Kubernetes 只會將 StorageClass 相同的 PVC 和 PV 綁定起來。
實操結(jié)果如下圖:

3.4. 小結(jié)
有了 Dynamic Provisioning 機(jī)制,運(yùn)維人員只需要在 Kubernetes 集群里創(chuàng)建出數(shù)量有限的 StorageClass 對象。當(dāng)開發(fā)人員提交了包含 StorageClass 字段的 PVC 之后,Kubernetes 就會根據(jù)這個 StorageClass 創(chuàng)建出對應(yīng)的 PV。
四. 總結(jié)
基于Pod,PVC,PV與StorageClass之間的關(guān)系,我總結(jié)出如下的架構(gòu)圖:

從圖中我們可以總結(jié)為:
- PVC 描述了 Pod 想要使用的持久化存儲的屬性,比如存儲的大小、讀寫權(quán)限等。
- PV 描述了一個具體的 Volume 的屬性,比如 Volume 的類型、掛載目錄、遠(yuǎn)程存儲服務(wù)器地址等。
- StorageClass 的作用,則是充當(dāng) PV 的模板。并且,只有同屬于一個
StorageClass的 PV 和 PVC,才可以綁定在一起。同時,可以指定 PV 的 Provisioner(存儲插件),當(dāng)存儲插件支持Dynamic Provisioning的話,Kubernetes 就可以自動創(chuàng)建 PV 了。
歡迎收藏個人博客: Wyatt's Blog ,非常感謝~
Reference
https://kubernetes.io/docs/concepts/storage/storage-classes/
https://kubernetes.io/zh/docs/concepts/storage/dynamic-provisioning/
https://time.geekbang.org/column/article/42698?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title