Kubernetes 對于有狀態(tài)的容器應用或者對數據需要持久化的應用,不僅需要將容器內的目錄掛載到宿主機的目錄或者 emptyDir 臨時存儲卷,而且需要更加可靠的存儲來保護應用產生的重要數據,以便容器應用在重建之后,仍然可以使用之前的數據。不過,存儲資源和計算資源(GPU/內存)的管理方式完全不同。為了能夠屏蔽底層存儲實現的細節(jié),讓用戶方便使用,同時能讓管理員方便管理,Kubernetes從v1.0版本就引入了 PersistentVolume 和PersistentVolumeClaim 兩個資源對象來實現對存儲的管理子系統(tǒng)。
PersistentVolume(PV) 是對底層網絡共享存儲的對象,將共享存儲定義為一種”資源“,比如節(jié)點(Node)也是一種容器應用可以”消費“的資源。PV由管理員進行創(chuàng)建和配置,它與共享存儲的具體實現直接相關,例如 GlusterFS、iSCSI、RBD或GCE/AWS 公有云提供的共享存儲,通過插件式的機制完成與共享存儲的對接,以供應用訪問和使用。
PersistentVolumeClaim(PVC) 則是用戶對于存儲資源的一個”申請“。就像 Pod ”消費“ Node 的資源一樣,PVC 會”消費“ PV資源。PVC 可以申請?zhí)囟ǖ拇鎯臻g和訪問模式。
使用 PVC ”申請“到一定的存儲空間仍然不足以滿足應用對于存儲設備的各種需求。通常應用程序都會對存儲設備的特性和性能有不同的要求,包括讀寫速度、并發(fā)性能、數據冗余等更高的要求,Kubernetes 從 v1.4 版本開始引入了一個新的資源對象StorageClass,用于標記存儲資源的特性和性能。到v1.6版本時,StorageClass 和動態(tài)資源供應的機制得到了完善,實現了存儲卷的按需創(chuàng)建,在共享存儲的自動化管理進程中實現了重要的一步。
通過 StorageClass 的定義,管理員可以將存儲資源定義為某種類別(Class),正如存儲設備對于自身的配置描述(Profile),例如"快速存儲"、”慢速存儲“、”有數據冗余“、”無數據冗余“等。用戶依據 StorageClass 的描述就能夠直觀得知各種存儲資源的特性,就可以根據應用對存儲資源的需求去申請存儲資源了。
下面對Kubernetes的PV、PVC、StorageClass和動態(tài)資源供應等共享存儲管理機制進行詳細說明。
PV詳解
PV作為存儲資源,主要包括存儲能力、訪問模式、存儲類型、回收策略、后端存儲類型等關鍵信息的設置。下面的例子聲明的PV具有如下屬性:5Gi 存儲空間、訪問模式為 “ReadWriteOnce”,存儲類型為 “slow” (要求系統(tǒng)中已存在名為 slow 的 StorageClass),回收策略為 “Recycle”,并且后端存儲類型為 “nfs” (設置了 NFS Server 的IP地址和路徑):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 5Gi
accessMode:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
nfs:
path: /tmp
server: 172.17.0.2
Kubernetes支持的PV類型如下:
- gcePersistentDisk:GCE公共云提供的 PersistentDisk。
- AWSElasticBlockStore:AWS公共云提供的 ElasticBlockStore。
- AzureFile:Azure公共云提供的File。
- AzureDisk:Azure公共云提供的Disk。
- FC(Fibre Channel)
- Flocker
- NFS:網絡文件系統(tǒng)。
- iSCSI
- RBD(Rados Block Device):Ceph塊存儲。
- CephFS
- GlusterFS
- Cinder:OpenStack Cinder塊存儲。
- VsphereVolume
- Quobyte Volumes
- VMware Photon
- Portworx Volumes
- ScaleIO Volumes
- HostPath:宿主機目錄,僅用于單機測試。
每種存儲類型都有各自的特定,在使用時需要根據它們各自的參數進行設置。
1. PV的關鍵配置參數
1)存儲能力(Capacity)
描述存儲設備的能力,目前僅支持對存儲空間的設置(storage=xx),未來可能加入IOPS、吞吐率等指標的設置。
2)訪問模式(Access Modes)
對PV進行訪問模式的設置,用于描述用戶應用對存儲資源的訪問權限。訪問模式如下:
-
ReadWriteOnce(簡寫為RWO):讀寫權限,并且只能被單個Node掛載。 -
ReadOnlyMany(簡寫為ROX):只讀權限,允許被多個Node掛載。 -
ReadWriteMany(簡寫為RWX):讀寫權限,允許被多個Node掛載。
某些PV可能支持多種訪問模式,但PV在掛載時只能使用一種訪問模式,多種訪問模式不能同時生效。
下表描述了不同的存儲提供者支持的訪問模式,在PV的定義時需要與它們匹配:

3)存儲類別(Class)
PV可以設定其存儲的類型(Class),通過 storageClassName 參數指定一個 StorageClass 資源對象的名稱。具有特定“類別”的 PV 只能與請求了該“類別”的 PVC 進行綁定。未設定 “類別” 的 PV 則只能與不請求任何 “類別” 的 PVC 進行綁定。
4)回收策略
目前支持如下三種回收策略:
- 保留(Retain):保留數據,需要手工處理。
- 回收空間(Recycle):簡單清除文件的操作(例如執(zhí)行 rm -rf /thevolume/* 命令)。
- 刪除(Delete):與 PV 向量的后端存儲完成 volume 的刪除操作;如 AWS EBS、 GCE PD、Azure Disk 和 Cinder volumes 支持 “Delete” 策略。
目前,只有 NFS 和 HostPath 兩種類型的存儲設備支持 “Recycle” 策略; AWS EBS、 GCE PD、Azure Disk 和 Cinder volumes 支持 “Delete” 策略。
2. PV 生命周期的各個階段(Phase)
某個 PV 在生命周期中,可以處于以下4個階段之一:
- Available:可用狀態(tài),還未與某個 PVC 綁定。
- Bound:已與某個 PVC 綁定。
- Released:綁定的 PVC 已經刪除,資源已釋放,但沒有被集群回收。
- Failed:自動資源回收失敗。
3. PV 的掛載參數(Mount Options)
在將PV掛載到一個 Node 上時,根據后端存儲的特點,可能需要設置額外的掛載參數,目前可以通過在PV的定義中,設置一個名為“volume.beta.kubernetes.io/mount-options”的 annotation 來實現。下面的例子對一個類型的 gcePersistentDisk 的 PV 設置了掛載參數 “discard”:
apiVersion: v1
kind: PersistentVolume
metadata:
name: gce-disk-1
annotations:
volume.beta.kubernetes.io/mount-options: discard
spec:
capacity:
storage: 10Gi
accessMode:
- ReadWriteOnce
gcePersistentDisk:
fsType: ext4
pdName: gce-disk-1
并非所有類型的存儲都支持設置掛載參數。從 Kubernetes v1.6 版本開始,以下存儲類型支持設置掛載參數:
- gcePersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- NFS
- iSCSI
- RBD(Rados Block Device)
- CephFS
- GlusterFS
- Cinder
- VsphereVolume
- Quobyte Volumes
- VMware Photon
定義了 PV 以后如何使用呢?這是就需要用到 PVC 了。
PVC詳解
PVC 作為用戶對存儲資源的需求申請,主要包括存儲空間請求、訪問模式、PV 選擇條件和存儲類別等信息的設置。下面的例子聲明的 PVC 具有如下屬性:申請8Gi存儲空間,訪問模式為 “ReadWriteOnce”, PV 選擇條件為包含標簽 “release=stable” 并且包含條件為 “environment In [dev]”的標簽,存儲類別為 “slow” (要求系統(tǒng)中已存在名為 slow 的 StorageClass):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: stable
matchExpressions:
- {key: environment, operator: In, values: [dev]}
PVC的關鍵配置參數說明如下:
-
資源請求(Resources):描述對存儲資源的請求,目前僅支持request.storage的設置,即存儲空間大小。 -
訪問模式(Access Modes):PVC 也可以設置訪問模式,用于描述用戶應用對存儲資源的訪問權限??梢栽O置的三種訪問模式與 PV 的設置相同。 -
PV選擇條件(Selector):通過 Label Selector 的設置,可使 PVC 對應系統(tǒng)中已存在的各種 PV 進行篩選。系統(tǒng)將根據標簽選擇出合適的 PV 與該 PVC 進行綁定。選擇條件可以使用matchLabels和matchExpressions進行設置,如果兩個字段都設置了,則 Selector 的邏輯將是兩組條件同時滿足才能完成匹配。 -
存儲類別(Class):PVC 在定義時可以設定需要的后端存儲的“類別”(通過 storageClassName 字段指定),以降低對后端存儲特性的詳細信息的依賴。只有設置了該 Class 的 PV 才能被系統(tǒng)選出,并與該 PVC 進行綁定。
PVC 也可以不設置 Class 需求。如果 storageClassName 字段的值被設置為空(storageClassName=“”),則表示該 PVC 不要求特定的 Class,系統(tǒng)將只選擇未設定 Class 的 PV 與之匹配和綁定。 PVC 也可以完全不設置 storageClassName 字段,此時系統(tǒng)將根據系統(tǒng)是否啟用了名為 “DefaultStorageClass” 的 admission controller 進行相應的操作。
- 未啟用 DefaultStorageClass:等效于 PVC 設置 storageClassName 的值為空(storageClassName=“”),即只能選擇未設定 Class 的 PV 與之匹配和綁定。
-
啟用DefaultStorageClass:要求極其管理員已定義默認的 StorageClass。如果系統(tǒng)中不存在默認的 StorageClass, 則等效于不啟用 DefaultStorageClass 的情況。如果存在默認的 StorageClass,則系統(tǒng)將自動為 PVC 創(chuàng)建一個 PV(使用默認 StorageClass 的后端存儲),并將它們進行綁定。
集群管理員設置默認 StorageClass 的方法為,在 StorageClass 的定義中加上一個 annotation “storageclass.kubernetes.io/is-default-class=true”,如果管理員將多個 StorageClass 都定義為default,則由于不唯一,系統(tǒng)將無法為 PVC 創(chuàng)建相應的 PV。
注意,PVC 和 PV 都受限于 namespace,PVC 在選擇 PV 時受到 namespace 的限制,只有相同 namespace 中的 PV 才可能與 PVC 綁定。 Pod 在應用 PVC 時同樣受到 namespace 的限制,只有相同 namespace 中的 PVC 才能掛載到 Pod 內。
當 Selector 中的 Class 都進行設置時,系統(tǒng)將選擇兩個條件同時滿足的 PV 與之匹配。
另外,如果資源供應使用的是動態(tài)模式,即管理員沒有預先定義 PV,僅通過 StorageClass 交給系統(tǒng)自動完成 PV 的動態(tài)創(chuàng)建,那么 PVC 再設定 Selector 時,系統(tǒng)將無法為其供應任何存儲資源了。
在啟用動態(tài)供應模式的情況下,一旦用戶刪除了 PVC, 與之綁定的 PV 將根據其默認的回收策略 “Delete” 也會被刪除。如果需要保留 PV (用戶數據),則在動態(tài)綁定成功后,用戶需要將系統(tǒng)自動生成 PV 的回收策略從“Delete” 改成 “Retain”。
PV 和 PVC 的生命周期
PV 可以看做可用的存儲資源, PVC則是對存儲資源的需求,PV 和 PVC 的相互關系遵循如下圖所示的聲明周期:

1.資源供應(Provisioning)
Kubernetes 支持兩種資源的供應模式:靜態(tài)模式(Static)和動態(tài)模式(Dynamic)。資源供應的結果就是創(chuàng)建好的 PV。
- 靜態(tài)模式:集群管理員手工創(chuàng)建許多 PV,在定義 PV 時需要將后端存儲的特性進行設置。
- 動態(tài)模式:集群管理員無需手工創(chuàng)建 PV,而是通過 StorageClass 的設置對后端存儲進行描述,標記為某種“類型(Class)”。此時要求 PVC 對存儲的類型進行聲明,系統(tǒng)將自動完成 PV 的創(chuàng)建及與 PVC 的綁定。PVC 可以聲明 Class 為 "",說明該 PVC 禁止使用動態(tài)模式。
2.資源綁定(Binding)
在用戶定義好 PVC 之后,系統(tǒng)將根據 PVC 對存儲資源的請求(存儲空間和訪問模式)在已存在的 PV 中選擇一個滿足 PVC 要求的 PV,一旦找到,就將該 PV 與用戶定義的 PVC 進行綁定,然后用戶的應用就可以使用這個 PVC 了。如果系統(tǒng)中沒有滿足 PVC 要求的 PV, PVC則會無限期處于 Pending 狀態(tài),知道等到系統(tǒng)管理員創(chuàng)建了一個符合其要求的 PV。PV 一旦綁定到某個 PVC 上,就被這個 PVC 獨占,不能再與其他 PVC 進行綁定了。在這種情況下,當 PVC 申請的存儲空間比 PV 少時,整個 PV 的空間都能夠為 PVC 所用,可能會造成資源的浪費,如果資源供應使用的是動態(tài)模式,則系統(tǒng)在為 PVC 找到合適的 StorageClass 后,將自動創(chuàng)建一個 PV 并完成與 PVC 的綁定。
3.資源使用(Using)
Pod 使用 volume 的定義,將 PVC 掛載到容器內的某個路徑進行使用。volume 的類型為 “persistentVolumeClaim”,在后面的示例中再進行詳細說明。在容器應用掛載了一個 PVC 后,就能被持續(xù)獨占使用。不過,多個 Pod 可以掛載同一個 PVC,應用程序需要考慮多個實例共同訪問一塊存儲空間的問題。
4.資源釋放(Releasing)
當用戶對存儲資源使用完畢后,用戶可以刪除 PVC,與該 PVC 綁定的 PV 將會被標記為“已釋放”,但還不能立刻與其他 PVC 進行綁定。通過之前 PVC 寫入的數據可能還留在存儲設備上,只有在清除之后該 PV 才能再次使用。
5.資源回收(Reclaiming)
對于 PV,管理員可以設定回收策略(Reclaim Policy),用于設置與之綁定的 PVC 釋放資源之后,對于遺留數據如何處理。只有 PV 的存儲空間完成回收,才能供新的 PVC 綁定和使用?;厥詹呗栽斠娤鹿?jié)的說明。
下面通過兩張圖分別對在靜態(tài)資源供應模式和動態(tài)資源供應模式下,PV、PVC、StorageClass 及 Pod 使用 PVC 的原理進行說明。
下圖描述了在靜態(tài)資源供應模式下,通過 PV 和 PVC 完成綁定,并供 Pod 使用的存儲管理機制。

下圖描述了在動態(tài)資源供應模式下,通過 StorageClass 和 PVC 完成資源動態(tài)綁定(系統(tǒng)自動生成 PV),并供 Pod 使用的存儲管理機制。

接下來,我們再看看 StorageClass 的概念和用法。
StorageClass 詳解
StorageClass 作為對存儲資源的抽象定義,對用戶設置的 PVC 申請屏蔽后端資源存儲的細節(jié),一方面減輕用戶對存儲資源細節(jié)的關注,另一方面也減輕了管理員手工管理 PV 的工作,由系統(tǒng)自動完成 PV 的創(chuàng)建和綁定,實現了動態(tài)的資源供應。使用基于 StorageClass 的動態(tài)資源供應模式將逐步成為云平臺的標準存儲配置模式。
StorageClass 的定義主要包括名稱、后端存儲的提供者(Provisioner)和后端存儲的相關參數配置。StorageClass 一旦被創(chuàng)建出來,將無法修改。如果需要更改,則只能刪除原 StorageClass 的定義重建。下面的例子定義了一個名為 “standard” 的 StorageClass,提供者為 aws-ebs,其參數設置了一個 type=gp2。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
1.StorageClass 的關鍵配置參數
1) 提供者(Provisioner)
描述存儲資源的提供者,也可以看做后端存儲驅動。目前 Kubernetes 支持的 Provisioner 都以 “kubernetes.io/” 為開頭,用戶也可以使用自定義的后端存儲提供者。為了符合 StorageClass 的用法,自定義 Provisioner 需要符合存儲卷的開發(fā)規(guī)范。
2)參數(Parameters)
后端存儲資源提供者的參數設置,不同的 Provisioner 包括不同的參數設置。某些參數可以不顯示設定, Provisioner 將使用其默認值。
接下來通過幾種常見的 Provisioner 對 StorageClass 的定義進行詳細說明:
AWS EBS 存儲卷
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
type: io1
zone: us-east-1d
iopsPerGB: "10"
參數說明如下(詳細說明請參數 AWS EBS文檔):
- type:可選項為io1,gp2,sc1,st1,默認值為gp2
- zone:AWS zone的名稱
- iopsPerGB:及用于io1類型的volume,意為每秒GiB的I/O操作數量
- encrypted:是否加密
- kmsKeyId:加密時的 Amazon Resource Name
GCE PD 存儲卷
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
zone: us-centrall-a
參數說明如下(詳細說明請參考GCE文檔):
- type:可選項為pd-standard、pd-ssd,默認值為pd-standard
- zone:GCE zone名稱
GlusterFS 存儲卷
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://127.0.0.1:8081"
clusterid: "2bce7ww8rwruisfdfuuy67wer"
restauthenabled: "true"
restuser: "admin"
restuserkey: "My Secret"
secretNamespace: "default"
secretName: "heketi-secret"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:3"
參數說明如下(詳細說明請參數 GlusterFS 和 Heketi 的文檔):
-resturl:Gluster REST服務(Heketi)的URL地址,用于自動完成 GlusterFSvolume的設置。
-restauthenabled:是否對 Gluster REST服務啟用安全機制。
-restuser:訪問Gluster REST服務的用戶名。
-secretNamespace 和 secretName:保存訪問 Gluster REST服務密碼的 Secret 資源對象名。
-clusterid:GlusterFS 的 Cluster ID。
-gidMin 和 gidMax:StorageClass 的 GID 范圍,用于動態(tài)資源供應時為 PV 設置的 GID。
-volumetype:GlusterFS 的 volume 類型設置,例如 replicate:3 (Replicate類型,3份副本);disperse:4:2(Disperse類型,數據4份,冗余2份);“none”(Distribute類型)。
OpenStack Cinder存儲卷
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gold
provisioner: kubernetes.io/cinder
parameters:
type: fast
availability: nova
參數說明如下:
- type:Cinder 的 VolumeType,默認值為空。
- availability:Availability Zone,默認值為空。
其他 Provisioner 的 StorageClass 相關參數設置請參考它們各自的配置手冊。
2.設置默認的(Default)StorageClass
要在系統(tǒng)中設置一個默認的 StorageClass,首先需要啟用名為“DefaultStorageClass”的 admission controller,即在 kube-apiserver 的命令行參數 --admission-control 中增加:
--admission-control=...,DefaultStorageClass
然后,在 StorageClass 的定義中設置一個 annotation:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gold
annotations:
storageclass.beta.kubernetes.io/is-default-class="true"
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
通過 kubectl create命令創(chuàng)建成功后,查看 StorageClass 列表,可以看到名為 gold 的 StorageClass 被標記為“default”:
# kubectl get sc
NAME TYPE
gold(default) kubernetes.io/gce-pd