一、存儲機制介紹
在 Kubernetes 中,存儲資源和計算資源(CPU、Memory)同樣重要,Kubernetes 為了能讓管理員方便管理集群中的存儲資源,同時也為了讓使用者使用存儲更加方便,所以屏蔽了底層存儲的實現細節,將存儲抽象出兩個 API 資源 PersistentVolume 和 PersistentVolumeClaim 對象來對存儲進行管理。
PersistentVolume(持久化卷): PersistentVolume 簡稱 PV, 是對底層共享存儲的一種抽象,將共享存儲定義為一種資源,它屬於集群級別資源,不屬於任何 Namespace,用戶使用 PV 需要通過 PVC 申請。PV 是由管理員進行創建和配置,它和具體的底層的共享存儲技術的實現方式有關,比如說 Ceph、GlusterFS、NFS 等,都是通過插件機制完成與共享存儲的對接,且根據不同的存儲 PV 可配置參數也是不相同。
PersistentVolumeClaim(持久化卷聲明): PersistentVolumeClaim 簡稱 PVC,是用戶存儲的一種聲明,類似於對存儲資源的申請,它屬於一個 Namespace 中的資源,可用於向 PV 申請存儲資源。PVC 和 Pod 比較類似,Pod 消耗的是 Node 節點資源,而 PVC 消耗的是 PV 存儲資源,Pod 可以請求 CPU 和 Memory,而 PVC 可以請求特定的存儲空間和訪問模式。
二、PersistentVolume 詳解
1、PV 支持存儲的類型
PersistentVolume 類型實現為插件,目前 Kubernetes 支持以下插件:
RBD:Ceph 塊存儲。
FC:光纖存儲設備。
NFS:網絡問卷存儲卷。
iSCSI:iSCSI 存儲設備。
CephFS:開源共享存儲系統。
Flocker:一種開源共享存儲系統。
Glusterfs:一種開源共享存儲系統。
Flexvolume:一種插件式的存儲機制。
HostPath:宿主機目錄,僅能用於單機。
AzureFile:Azure 公有雲提供的 File。
AzureDisk:Azure 公有雲提供的 Disk。
ScaleIO Volumes:DellEMC 的存儲設備。
StorageOS:StorageOS 提供的存儲服務。
VsphereVolume:VMWare 提供的存儲系統。
Quobyte Volumes:Quobyte 提供的存儲服務。
Portworx Volumes:Portworx 提供的存儲服務。
GCEPersistentDisk:GCE 公有雲提供的 PersistentDisk。
AWSElasticBlockStore:AWS 公有雲提供的 ElasticBlockStore。
2、PV 的生命周期
PV 生命周期總共四個階段:
- Available: 可用狀態,尚未被 PVC 綁定。
- Bound: 綁定狀態,已經與某個 PVC 綁定。
- Failed: 當刪除 PVC 清理資源,自動回收卷時失敗,所以處於故障狀態。
- Released: 與之綁定的 PVC 已經被刪除,但資源尚未被集群回收。
3、基於 NFS 的 PV 示例
Kubernetes 支持多種存儲,這里使用最廣泛的 NFS 存儲為例來介紹,下面是一個 PV 的例子:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
label:
app: example
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /nfs/space
server: 192.168.2.11
4、PV 的常用配置參數
(1)、存儲能力 (capacity)
PV 可以通過配置 capacity 中的 storage 參數,對 PV 掛多大存儲空間進行設置
目前 capacity 只有一個設置存儲大小的選項,未來可能會增加。
capacity:
storage: 5Gi
(2)、存儲卷模式(volumeMode)
PV 可以通過配置 volumeMode 參數,對存儲卷類型進行設置,可選項包括:
- Filesystem: 文件系統,默認是此選項。
- Block: 塊設備
目前 Block 模式只有 AWSElasticBlockStore、AzureDisk、FC、GCEPersistentDisk、iSCSI、LocalVolume、RBD、VsphereVolume 等支持)。
volumeMode: Filesystem
(3)、訪問模式(accessModes)
PV 可以通過配置 accessModes 參數,設置訪問模式來限制應用對資源的訪問權限,有以下機制訪問模式:
- ReadWriteOnce(RWO): 讀寫權限,只能被單個節點掛載。
- ReadOnlyMany(ROX): 只讀權限,允許被多個節點掛載讀。
- ReadWriteMany(RWX): 讀寫權限,允許被多個節點掛載。
accessModes:
- ReadWriteOnce
不過不同的存儲所支持的訪問模式也不相同,具體如下:

(4)、掛載參數(mountOptions)
PV 可以根據不同的存儲卷類型,設置不同的掛載參數,每種類型的存儲卷可配置參數都不相同。如 NFS 存儲,可以設置 NFS 掛載配置,如下:
下面例子只是 NFS 支持的部分參數,其它參數請自行查找 NFS 掛載參數。
mountOptions:
- hard
- nfsvers=4.1
(5)、存儲類 (storageClassName)
PV 可以通過配置 storageClassName 參數指定一個存儲類 StorageClass 資源,具有特定 StorageClass 的 PV 只能與指定相同 StorageClass 的 PVC 進行綁定,沒有設置 StorageClass 的 PV 也是同樣只能與沒有指定 StorageClass 的 PVC 綁定。
storageClassName: slow
(6)、回收策略(persistentVolumeReclaimPolicy)
PV 可以通過配置 persistentVolumeReclaimPolicy 參數設置回收策略,可選項如下:
- Retain(保留): 保留數據,需要由管理員手動清理。
- Recycle(回收): 刪除數據,即刪除目錄下的所有文件,比如說執行 rm -rf /thevolume/* 命令,目前只有 NFS 和 HostPath 支持。
- Delete(刪除): 刪除存儲資源,僅僅部分雲存儲系統支持,比如刪除 AWS EBS 卷,目前只有 AWS EBS,GCE PD,Azure 磁盤和 Cinder 卷支持刪除。
persistentVolumeReclaimPolicy: Recycle
三、PersistentVolumeClaim 詳解
1、PVC 示例
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- key: environment
operator: In
values: dev
2、PVC 的常用配置參數
(1)、篩選器(selector)
PVC 可以通過在 Selecter 中設置 Laberl 標簽,篩選出帶有指定 Label 的 PV 進行綁定。Selecter 中可以指定 matchLabels 或 matchExpressions,如果兩個字段都設定了就需要同時滿足才能匹配。
selector:
matchLabels:
release: "stable"
matchExpressions:
- key: environment
operator: In
values: dev
(2)、資源請求(resources)
PVC 設置目前只有 requests.storage 一個參數,用於指定申請存儲空間的大小。
resources:
requests:
storage: 8Gi
(3)、存儲類(storageClass)
PVC 要想綁定帶有特定 StorageClass 的 PV 時,也必須設定 storageClassName 參數,且名稱也必須要和 PV 中的 storageClassName 保持一致。如果要綁定的 PV 沒有設置 storageClassName 則 PVC 中也不需要設置。
當 PVC 中如果未指定 storageClassName 參數或者指定為空值,則還需要考慮 Kubernetes 中是否設置了默認的 StorageClass:
- 未啟用 DefaultStorageClass:等於 storageClassName 值為空。
- 啟用 DefaultStorageClass:等於 storageClassName 值為默認的 StorageClass。
如果設置 storageClassName=““,則表示該 PVC 不指定 StorageClass。
storageClassName: slow
(4)、訪問模式(accessModes)
PVC 中可設置的訪問模式與 PV 種一樣,用於限制應用對資源的訪問權限。
(5)、存儲卷模式(volumeMode)
PVC 中可設置的存儲卷模式與 PV 種一樣,分為 Filesystem 和 Block 兩種。
四、StorageClass 詳解
1、StorageClass 示例
這里使用 NFS 存儲,創建 StorageClass 示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true" #設置為默認的 StorageClass
provisioner: nfs-client
mountOptions:
- hard
- nfsvers=4
parameters:
archiveOnDelete: "true"
2、StorageClass 的常用配置參數
(1)、提供者(provisioner)
在創建 StorageClass 之前需要 Kubernetes 集群中存在 Provisioner(存儲分配提供者)應用,如 NFS 存儲需要有 NFS-Provisioner (NFS 存儲分配提供者)應用,如果集群中沒有該應用,那么創建的 StorageClass 只能作為標記,而不能提供創建 PV 的作用。
provisioner: nfs-client
(2)、參數(parameters)
后端存儲提供的參數,不同的 Provisioner 可與配置的參數也是不相同。例如 NFS Provisioner 可與提供如下參數:
parameters:
archiveOnDelete: "true" #刪除 PV 后是否保留數據
(3)、掛載參數(mountOptions)
在 StorageClass 中,可以根據不同的存儲來指定不同的掛載參數,此參數會與 StorageClass 綁定的 Provisioner 創建 PV 時,將此掛載參數與創建的 PV 關聯。
mountOptions:
- hard
- nfsvers=4
(4)、設置默認的 StorageClass
可與在 Kubernetes 集群中設置一個默認的 StorageClass,這樣當創建 PVC 時如果未指定 StorageClass 則會使用默認的 StorageClass。
metadata:
annotations:
storageclass.kubernetes.io/is-default-class: "true"
五、PV 和 PVC 的生命周期
PV 是 Kubernetes 集群的存儲資源,而 PVC 則是對存儲資源的需求,創建 PVC 需要對 PV 發起使用申請,即和 PV 進行綁定。 PV 和 PVC 是一一對應的關系,它們二者的交互遵循如下生命周期:
1、存儲供給
存儲供給(Provisioning)是指為 PVC 准備可用的 PV 的一種機制。Kubernetes 支持 PV 供給方式有 靜態供給 和 動態供給 兩種:
- 靜態供給: 指由集群管理員手動創建一定數量的 PV,創建 PV 時需要根據后端存儲的不同,配置的參數也不同。
- 動態供給: 指不需要集群管理員手動創建 PV,將 PV 的創建工作交由 StorageClass 關聯的 Provisioner 進行創建,會根據存儲的不同自動配置相關的參數。創建完成 PV 后系統會自動將 PVC 與其綁定。
2、存儲綁定
在靜態模式下,在用戶定義好 PVC 后,Kubernetes 將根據 PVC 提出的“申請空間的大小”、“訪問模式”從集群中尋找已經存在且滿足條件的 PV 進行綁定,如果集群中沒有匹配的 PV 則 PVC 將處於 Pending 等待狀態,知道系統創建了符合條件的 PV 再與其綁定。PV 與 PVC 綁定后就不能和別的 PVC 進行綁定。
在動態模式下,當創建 PVC 並且指定 StorageClass 后,與 StorageClass 關聯的存儲插件會自動創建對應的 PV 與該 PVC 進行綁定。
3、存儲回收
完成存儲卷的使用目標之后刪除 PVC 對象,以便進行資源回收。不過,至於如何操作則取決於 PV 的回收策略 ,目前有三種策略:
- 保存策略(Retain): 刪除 PVC 之后,Kubernetes 系統不會自動刪除 PV ,而僅是將它標識為 Released 狀態,不過處於 Released 狀態的 PV 不能被其他 PVC 申請所綁定,因為之前 PVC 綁定的應用數據仍然存在,需要由管理員手動清理數據,然后決定如何處理 PV 的使用。
- 回收策略: 當 PVC 刪除后,此 PV 變成 Available 可用狀態。不過此策略需要后端存儲插件的支持。
- 刪除策略: 當刪除 PVC 后 PV 和存儲中的數據會被立即刪除,不過此策略也需要后端存儲插件的支持。
六、PVC 使用示例
Deployment 中使用 PVC
一般 Deployment 中使用 PVC,大部分都是靜態供給方式創建,即先創建 PV,再創建 PVC 與 PV 綁定,在設置應用於 PVC 關聯。
下面是一個 NFS 存儲創建 PV 的例子,如下:
`apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
labels:
app: nginx-pv
spec:
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
capacity:
storage: 1Gi
mountOptions:
- hard
- nfsvers=4.1
nfs:
server: 192.168.2.11
path: /nfs/data/nginx`
創建 PVC 與 PV 進行關聯綁定:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
selector:
matchLabels:
app: nginx-pv
創建應用於 PVC 進行關聯:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 1
containers:
- name: nginx
image: nginx:latest
ports:
- name: server
containerPort: 80
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: nginx-pvc
StatefulSet 中使用 PVC
在有狀態的應用中,我們經常使用動態供給方式創建 PV 和 PVC,不過提前需要集群擁有:
- StorageClass:存儲類
- Provisioner:與存儲類關聯的管理后端存儲的插件
只有擁有上面兩種資源同時存在時才能使用動態存儲,這里使用的是 NFS 存儲,關於如何創建 NFS Provisioner 可查看以下文章Kubernetes 中部署 NFS-Subdir-External-Provisioner 為 NFS 提供動態分配卷,假如 Kubernetes 集群中使用 NFS 存儲,且存在 Provisioner 的名稱為 nfs-client 那就可以下面創建 StorageClass 示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: nfs-client
parameters:
archiveOnDelete: "true"
mountOptions:
- hard
- nfsvers=4
然后 StatefulSet 可以按下方式,在 volumeClaimTemplates 參數中指定使用的 StorageClass,然后與 StorageClass 關聯的 NFS Provisioner 會執行創建 PVC 和 PV,然后兩者進行綁定,下面是 StatefulSet 方式使用 volumeClaimTemplates掛載存儲的示例:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 1
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: web
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs-storage"
resources:
requests:
storage: 1Gi
