大白話說(shuō)明白K8S的PV/PVC/StorageClass
先來(lái)個(gè)一句話總結(jié):PV、PVC是K8S用來(lái)做存儲(chǔ)管理的資源對(duì)象,它們讓存儲(chǔ)資源的使用變得可控,從而保障系統(tǒng)的穩(wěn)定性、可靠性。StorageClass則是為了減少人工的工作量而去自動(dòng)化創(chuàng)建PV的組件。所有Pod使用存儲(chǔ)只有一個(gè)原則:先規(guī)劃 → 后申請(qǐng) → 再使用。
一、理論
1、PV概念
PV是對(duì)K8S存儲(chǔ)資源的抽象,PV一般由運(yùn)維人員創(chuàng)建和配置,供容器申請(qǐng)使用。
沒(méi)有PV之前,服務(wù)器的磁盤沒(méi)有分區(qū)的概念,有了PV之后,相當(dāng)于通過(guò)PV對(duì)服務(wù)器的磁盤進(jìn)行分區(qū)。
2、PVC概念
PVC 是Pod對(duì)存儲(chǔ)資源的一個(gè)申請(qǐng),主要包括存儲(chǔ)空間申請(qǐng)、訪問(wèn)模式等。創(chuàng)建PV后,Pod就可以通過(guò)PVC向PV申請(qǐng)磁盤空間了。類似于某個(gè)應(yīng)用程序向操作系統(tǒng)的D盤申請(qǐng)1G的使用空間。
PVC 創(chuàng)建成功之后,Pod 就可以以存儲(chǔ)卷(Volume)的方式使用 PVC 的存儲(chǔ)資源了。Pod 在使用 PVC 時(shí)必須與PVC在同一個(gè)Namespace下。
3、PV / PVC的關(guān)系
PV相當(dāng)于對(duì)磁盤的分區(qū),PVC相當(dāng)于APP(應(yīng)用程序)向某個(gè)分區(qū)申請(qǐng)多少空間。比如說(shuō)安裝WPS程序時(shí),一般會(huì)告知我們安裝它需要多少存儲(chǔ)空間,讓你選擇在某個(gè)磁盤下安裝。如果將來(lái)某個(gè)分區(qū)磁盤滿了,也不會(huì)影響別的分區(qū)磁盤的使用。
一旦 PV 與PVC綁定,Pod就可以使用這個(gè) PVC 了。如果在系統(tǒng)中沒(méi)有滿足 PVC 要求的 PV,PVC則一直處于 Pending 狀態(tài),直到系統(tǒng)里產(chǎn)生了一個(gè)合適的 PV。
4、StorageClass概念
K8S有兩種存儲(chǔ)資源的供應(yīng)模式:靜態(tài)模式和動(dòng)態(tài)模式,資源供應(yīng)的最終目的就是將適合的PV與PVC綁定:
- 靜態(tài)模式:管理員預(yù)先創(chuàng)建許多各種各樣的PV,等待PVC申請(qǐng)使用。
- 動(dòng)態(tài)模式:管理員無(wú)須預(yù)先創(chuàng)建PV,而是通過(guò)StorageClass自動(dòng)完成PV的創(chuàng)建以及與PVC的綁定。
StorageClass就是動(dòng)態(tài)模式,根據(jù)PVC的需求動(dòng)態(tài)創(chuàng)建合適的PV資源,從而實(shí)現(xiàn)存儲(chǔ)卷的按需創(chuàng)建。
一般某個(gè)商業(yè)性的應(yīng)用程序,會(huì)用到大量的Pod,如果每個(gè)Pod都需要使用存儲(chǔ)資源,那么就需要人工時(shí)不時(shí)的去創(chuàng)建PV,這也是個(gè)麻煩事兒。解決方法就是使用動(dòng)態(tài)模式:當(dāng)Pod通過(guò)PVC申請(qǐng)存儲(chǔ)資源時(shí),直接通過(guò)StorageClass去動(dòng)態(tài)的創(chuàng)建對(duì)應(yīng)大小的PV,然后與PVC綁定,所以基本上PV → PVC是一對(duì)一的關(guān)系。
5、Provisioner概念
在創(chuàng)建 PVC 時(shí)需要指定 StorageClass,PVC 選擇到對(duì)應(yīng)的StorageClass后,與其關(guān)聯(lián)的 Provisioner 組件來(lái)動(dòng)態(tài)創(chuàng)建 PV 資源。
那Provisioner是個(gè)啥呢?其實(shí)就一個(gè)存儲(chǔ)驅(qū)動(dòng),類似操作系統(tǒng)里的磁盤驅(qū)動(dòng)。
StorageClass 資源對(duì)象的定義主要包括:名稱、Provisioner、存儲(chǔ)的相關(guān)參數(shù)配置、回收策略。StorageClass一旦被創(chuàng)建,則無(wú)法修改,只能刪除重新創(chuàng)建。
PV和PVC的生命周期,包括4個(gè)階段:資源供應(yīng)(Provisioning)、資源綁定(Binding)、資源使用(Using)、資源回收(Reclaiming)。首先舊的有資源供應(yīng),說(shuō)白了就是得有存儲(chǔ)驅(qū)動(dòng),然后才能創(chuàng)建、綁定和使用、回收。
6、使用PV / PVC前后對(duì)比
6.1、通過(guò)描述對(duì)比
在沒(méi)有使用PV、PVC之前,各個(gè)Pod都可以任意的向存儲(chǔ)資源里(比如NFS)寫數(shù)據(jù),隨便一個(gè)Pod都可以往磁盤上插一杠子,長(zhǎng)期下去磁盤的管理會(huì)越來(lái)越混亂,然后導(dǎo)致數(shù)據(jù)使用超限,磁盤爆掉,最后導(dǎo)致磁盤上的所有應(yīng)用全部掛掉。
為了解決這個(gè)問(wèn)題,引入了PV、PVC的概念,達(dá)到限制Pod寫入存儲(chǔ)數(shù)據(jù)大小的目的,從而更好地保障了系統(tǒng)的可用性、穩(wěn)定性。
有了PVC、PV之后,所有Pod使用存儲(chǔ)資源,保持一個(gè)原則:先規(guī)劃 → 后申請(qǐng) → 再使用。
那你肯定有一個(gè)疑問(wèn),“StorageClass是自動(dòng)化創(chuàng)建PV,跟原本的無(wú)序不可控是一樣的效果啊,都可以隨便占用存儲(chǔ)資源啊”。
其實(shí)不然,使用StorageClass只是自動(dòng)化了創(chuàng)建PV的流程,但依舊執(zhí)行的是一個(gè)存儲(chǔ)可控的流程。每個(gè)Pod使用多少存儲(chǔ)空間是固定的,Pod沒(méi)有辦法超額使用存儲(chǔ)空間,更不會(huì)影響到別的應(yīng)用,要出故障也只是某個(gè)Pod自己出故障。
6.2、通過(guò)圖片對(duì)比
沒(méi)有使用PV、PVC之前的情況,如下面2張圖:
有了PV、PVC之后的情況,如下圖:
二、實(shí)踐
在實(shí)踐PV、PVC、StorageClass之前,需要讀者朋友自行安裝NFS服務(wù)器。文中演示的內(nèi)容是通過(guò)yaml編排自動(dòng)到NFS服務(wù)器起上創(chuàng)建PV。
1、Pod使用PV、PVC掛載存儲(chǔ)卷
1.1、編排PV、PVC、Pod掛載PVC
文中演示的是:Pod的某個(gè)目錄掛載到NFS的某個(gè)目錄下。使用了nginx鏡像,將html文件寫在PV所在的NFS服務(wù)器上,最終可以看到利用PV / PVC 成功掛載上去了。
yaml文件如下:
# PV編排
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv1
namespace: dev1
labels:
pv: nfs-pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
# Recycle 刪除PVC會(huì)同步刪除PV | Retain 刪除PVC不會(huì)同步刪除PV
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/nfstest/share/pv1
server: 10.20.1.20
readOnly: false
---
# PVC 編排,通過(guò)selector查找PV,K8S里的資源查找都是通過(guò)selector查找label標(biāo)簽
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc1
namespace: dev1
labels:
pv: nfs-pvc1
spec:
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnce
selector:
matchLabels:
pv: nfs-pv1
---
# Pod掛載PVC,這里為了測(cè)試,直接通過(guò)node節(jié)點(diǎn)的hostPort暴露服務(wù)
apiVersion: v1
kind: Pod
metadata:
name: webapp
namespace: dev1
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
hostPort: 8081
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
persistentVolumeClaim:
claimName: nfs-pvc1
執(zhí)行kubectl命令,查看實(shí)踐效果如下:
然后查看pod的情況,發(fā)現(xiàn)pod一直處于創(chuàng)建中,如下:
于是查看pod的情況kubectl describe pod webapp -n dev1,發(fā)現(xiàn)如下異常信息:
是因?yàn)闆](méi)有在NFS上創(chuàng)建此文件夾。到NFS創(chuàng)建此文件夾之后,重啟Pod,一切正常了,然后找到Pod所在Node節(jié)點(diǎn)。通過(guò)http://nodeip:port訪問(wèn),可以看到成功的界面:
[root@k8s-master pv-pvc-storageclass]# kubectl get pods -n dev1 -owide | grep webapp
webapp 1/1 Running 0 4m17s 10.21.69.214 k8s-worker-3 <none> <none>
此時(shí)因?yàn)閚ginx下還沒(méi)有html頁(yè)面,所以看不到內(nèi)容。此時(shí)到NFS服務(wù)器對(duì)應(yīng)的目錄/data/nfstest/share/pv1下增加index.html頁(yè)面,然后刷新頁(yè)面即可,界面如下:
也可以通過(guò)進(jìn)入到Pod內(nèi)部,查看驗(yàn)證是夠掛載成功。
執(zhí)行進(jìn)入Pod的命令kubectl exec -it webapp -n dev1 -- /bin/sh,可以看到如下頁(yè)面:
2、Pod使用StorageClass自動(dòng)掛載存儲(chǔ)卷
2.1、安裝 Provisioner
文中選擇通過(guò)helm的方式安裝nfs-subdir-external-provisioner,這種方式相對(duì)簡(jiǎn)單。安裝文檔、安裝過(guò)程見(jiàn)下文:
- 安裝文檔:
https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
- 安裝過(guò)程:
通過(guò)以下3個(gè)步驟完成nfs-subdir-external-provisioner的安裝。
- 安裝helm,本文以mac為例
brew install heml
- 安裝nfs-subdir-external-provisioner,執(zhí)行以下2個(gè)命令:
$ helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
$ helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -n kube-system \
--set image.repository=dyrnq/nfs-subdir-external-provisioner \
--set nfs.server=10.20.1.20 \
--set nfs.path=/data/nfstest/nfs-storage
這里注意幾個(gè)參數(shù):
image.repository:修改了鏡像的地址,默認(rèn)用的國(guó)外鏡像很有可能拉不下來(lái)
nfs.server:你的NFS服務(wù)器地址
nfs.path:存儲(chǔ)目錄
- 查看helm安裝的結(jié)果:
執(zhí)行命令:helm list -A,查看helm安裝結(jié)果:
查看是否創(chuàng)建了對(duì)應(yīng)的pod,如果沒(méi)有修改鏡像地址會(huì)一直拉取失敗,如下圖:
修改鏡像地址后成功啟動(dòng)Pod,如下圖:
2.2、使用StorageClass
文中演示的是:Pod利用StorageClass自動(dòng)創(chuàng)建PV,同時(shí)在對(duì)應(yīng)的存儲(chǔ)目錄上創(chuàng)建了文件,寫入了數(shù)據(jù)。
yaml文件如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage-1
provisioner: cluster.local/nfs-subdir-external-provisioner
parameters:
# 設(shè)置為"false"時(shí)刪除PVC不會(huì)保留數(shù)據(jù),"true"則保留數(shù)據(jù)
archiveOnDelete: "false"
mountOptions:
# 指定NFS版本,這個(gè)需要根據(jù)NFS Server版本號(hào)設(shè)置
- nfsvers=4
---
# 創(chuàng)建PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs-storage-pvc-1
namespace: dev1
spec:
storageClassName: nfs-storage-1 #需要與上面創(chuàng)建的storageclass的名稱一致
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
---
kind: Pod
apiVersion: v1
metadata:
name: nfs-storage-pod-1
namespace: dev1
spec:
containers:
- name: nfs-storage-pod-1
image: busybox
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/teststorage && echo 111 > /mnt/teststorage && exit 0 || exit 1" ## 創(chuàng)建一個(gè)名稱為"SUCCESS"的文件
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: nfs-storage-pvc-1
執(zhí)行kubectl命令后,可以看到如下效果:
可以看到如我們預(yù)料的那樣,通過(guò)storageClass自動(dòng)創(chuàng)建了PV,同時(shí)在NFS對(duì)應(yīng)的存儲(chǔ)目錄上創(chuàng)建了文件,寫入了數(shù)據(jù)。
至此,我們實(shí)踐過(guò)程全部結(jié)束。
三、總結(jié)
本文主要講解了PV、PVC、StorageClass的理論和實(shí)戰(zhàn)。
一句話總結(jié):PV、PVC是K8S用來(lái)做存儲(chǔ)管理的資源對(duì)象,它們讓存儲(chǔ)資源的使用變得可控,從而保障系統(tǒng)的穩(wěn)定性、可靠性。StorageClass則是為了減少人工的工作量而去自動(dòng)化創(chuàng)建PV的組件。所有Pod使用存儲(chǔ)只有一個(gè)原則:先規(guī)劃 → 后申請(qǐng) → 再使用。