持久卷 PersistentVolume
PersistentVolume(PV)是集群中一塊存儲資源,由管理員主動創(chuàng)建提供或使用 StorageClass 動態(tài)提供。它與節(jié)點資源一樣,不屬于任何命名空間,有著自己獨立的生命周期。而用戶則通過 PersistentVolumeClaim (PVC) 來申請所需的存儲資源。
1. 生命周期
在 Kubernetes 集群中,PV 作為存儲資源存在,Pod 通過 PVC 來使用 PV。PV 和 PVC 之間的交互過程有著自己的生命周期,這個生命周期分為5個階段:
- 供應(yīng)(Provisioning):即 PV 的創(chuàng)建,可以直接創(chuàng)建 PV(靜態(tài)方式),也可以使用 StorageClass(動態(tài)方式);
- 綁定(Binding):將 PV 分配給 PVC;
- 使用(Using):Pod 通過 PVC 使用該 Volume;
- 釋放(Releasing):Pod 釋放 Volume 并刪除 PVC;
- 回收(Reclaiming):回收 PV,可以保留 PV 以便下次使用,也可以直接從云存儲中刪除;
根據(jù)上述的5個階段,存儲卷的存在下面的 4 種狀態(tài):
- Available:可用狀態(tài),處于此狀態(tài)表明 PV 以及準(zhǔn)備就緒了,可以被 PVC 使用了;
- Bound:綁定狀態(tài),表明 PV 已被分配給了 PVC;
- Released:釋放狀態(tài),表明 PVC 解綁 PV,但還未執(zhí)行回收策略;
- Failed:錯誤狀態(tài),表明 PV 發(fā)生錯誤;
1.1 供應(yīng)(Provisioning)
供應(yīng)是為集群提供可用的存儲卷,在 Kubernetes 中有兩種持久化存儲卷的提供方式:靜態(tài)或者動態(tài),兩者區(qū)別在于。
1.1.1 靜態(tài) (Static)
PV 是由 Kubernetes 的集群管理員創(chuàng)建的,代表真實的存儲,可被 Pod 作為真實存儲使用。在靜態(tài)供應(yīng)的情況下,由集群管理員預(yù)先創(chuàng)建 PV,開發(fā)者創(chuàng)建 PVC 和 Pod,Pod 通過 PVC 使用 PV 提供的存儲。靜態(tài)供應(yīng)方式的過程如下圖所示:
[圖片上傳中...(image-5c62e8-1597076467904-3)]
<figcaption></figcaption>
2.1.2 動態(tài)(Dynamic)
對于動態(tài)的提供方式,當(dāng)管理員創(chuàng)建的靜態(tài) PV 都不能夠匹配用戶的 PVC 時,集群會嘗試自動為 PVC 提供一個存儲卷,這種提供方式基于 StorageClass。在動態(tài)提供方面,PVC 需要請求一個存儲類,但此存儲類必須由管理員預(yù)先創(chuàng)建和配置。集群管理員需要在 API-Server 中啟用 DefaultStorageClass 控制器。動態(tài)供應(yīng)過程如下圖所示:
[圖片上傳中...(image-7fd658-1597076467904-2)]
<figcaption></figcaption>
1.2 綁定(Binding)
Kubernetes 會動態(tài)的將 PVC 與可用的 PV 的進行綁定,如果一個 PV 曾經(jīng)動態(tài)供給到了一個新的 PVC,那么 PVC 綁定就是專屬的。另外,用戶總是能得到它們所要求的存儲,但是 volume 可能超過它們的請求。一旦綁定了,PVC綁定就是專屬的,無論它們的綁定模式是什么。
如果沒有匹配的 PV,那么 PVC 會無限期的處于未綁定狀態(tài),直到存在匹配的 PV。比如,就算集群中存在很多的 50G 的 PV,需要 100G 容量的 PVC 也不會匹配滿足需求的 PV,直到集群中有 100G 的 PV 時,PVC 才會被綁定。PVC 會基于下面的條件綁定PV,如果下面的條件同時存在,則選擇符合所有要求的 PV 進行綁定:
如果PVC指定了存儲類,則只會綁定指定了同樣存儲類的PV;
如果PVC設(shè)置了選擇器,則選擇器去匹配符合的PV;
如果沒有指定存儲類和設(shè)置選取器,PVC會根據(jù)存儲空間容量大小和訪問模式匹配符合的PV。
1.3 使用(Using)
Pod 把 PVC 作為卷來使用,集群會通過 PVC 查找綁定的 PV,并將其掛接至 Pod。對于支持多種訪問方式的卷,用戶在使用 PVC 作為卷時,可以指定需要的訪問方式。一旦用戶擁有了一個已經(jīng)綁定的 PVC,被綁定的 PV 就歸該用戶所有。用戶能夠通過在 Pod 的存儲卷中包含的 PVC,從而訪問所占有的 PV。
1.4釋放(Releasing)
當(dāng)用戶完成對卷的使用時,可以通過請求 API-Server 來刪除 PVC,刪除后還可以重新申請,在刪除 PVC 后對應(yīng)的持久化存儲卷被視為“被釋放”,但這時還不能給其他的 PVC 使用。之前的 PVC 數(shù)據(jù)還保存在卷中,要根據(jù)策略來進行后續(xù)處理。
1.5 回收(Reclaiming)
PV 的回收策略向集群闡述了在 PVC 釋放卷時,應(yīng)如何進行后續(xù)工作。目前可以采用三種策略:保留,回收或者刪除。保留策略允許重新申請這一資源。在 PVC 能夠支持的情況下,刪除策略會同時刪除卷以及AWS EBS/GCE PD或者Cinder卷中的存儲內(nèi)容。如果插件能夠支持,回收策略會執(zhí)行基礎(chǔ)的擦除操作(rm -rf /thevolume/*),這一卷就能被重新申請了。
1.5.1 保留
保留回收策略允許手工回收資源。當(dāng) PVC 被刪除,PV 將繼續(xù)存儲現(xiàn)有的數(shù)據(jù),雖然存儲卷被處于已釋放的狀態(tài),但是它對于其他的 PVC 仍是不可用的。管理員能夠通過下面的步驟手工回收存儲卷:
刪除 PV:在 PV 被刪除后,在外部設(shè)施中相關(guān)的存儲資產(chǎn)仍然還在;
手工刪除遺留在外部存儲中的數(shù)據(jù);
手工刪除存儲資產(chǎn),如果需要重用這些存儲資產(chǎn),則需要創(chuàng)建新的 PV;
1.5.2 循環(huán)
此策略將會被遺棄,建議后續(xù)使用動態(tài)供應(yīng)的模式。
循環(huán)回收會在存儲卷上執(zhí)行基本擦除命令 rm -rf /thevolume/,使數(shù)據(jù)對于新的 PVC 可用。
1.5.3 刪除
對于支持刪除回收策略的存儲卷插件,刪除即會從 Kubernetes 中移除 PV,也會從相關(guān)的外部設(shè)施中刪除存儲資產(chǎn),例如 AWS EBS, GCE PD, Azure Disk 或者 Cinder 存儲卷。
2. 創(chuàng)建持久存儲卷
在創(chuàng)建持久卷 PV 時需要指定實際存儲類型,類型是作為插件實現(xiàn)的,目前支持以下插件:
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- CSI
- FC (Fibre Channel)
- FlexVolume
- Flocker
- NFS
- iSCSI
- RBD (Ceph Block Device)
- CephFS
- Cinder (OpenStack block storage)
- Glusterfs
- VsphereVolume
- Quobyte Volumes
- HostPath (Single node testing only -- local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
- Portworx Volumes
- ScaleIO Volumes
- StorageOS
下面是一個持久卷的聲明 YAML 配置文件。在此配置文件中要求提供 5Gi 的存儲空間,存儲模式為Filesystem ,訪問模式是 ReadWriteOnce,通過 Recycle 回收策略進行持久化存儲卷的回收,指定存儲類為 slow,使用 nfs 的插件類型。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv
spec:
capacity: #容量
storage: 5Gi
volumeMode: Filesystem #存儲卷模式
accessModes: #訪問模式
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle #持久化卷回收策略
storageClassName: slow #存儲類
mountOptions: #掛接選項
- hard
- nfsvers=4.1
nfs:
path:/tmp
server:172.17.0.2
復(fù)制代碼
2.1 容量(Capacity)
PV 需通過 capcity 屬性指定存儲容量,目前 capcity 屬性僅有 storage(存儲大?。┻@唯一一個設(shè)置。
2.2 存儲卷模式(Volume Mode)
存儲卷模式的默認(rèn)值為 Filesystem。在 Kubernetesv1.9 版本后用戶可以指定volumeMode 的值,除了支持文件系統(tǒng)(files ystem)外,也支持塊設(shè)備(raw block devices)。
2.3 訪問模式(Access Modes)
訪問模式的可選范圍如下:
- ReadWriteOnce(縮寫 RWO):僅允許單個節(jié)點掛載進行讀寫;
- ReadOnlyMany(縮寫 ROX):允許多個節(jié)點掛載且只讀;
- ReadWriteMany(縮寫 RWX):允許多個節(jié)點掛載進行讀寫;
即使某個存儲卷插件支持多種訪問模式,但一次也只能配置一種訪問模式。下面是各個存儲卷插件支持的訪問模式清單:
| 存儲卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany |
|---|---|---|---|
| AWSElasticBlockStore | ? | – | – |
| AzureFile | ? | ? | ? |
| AzureDisk | ? | – | – |
| CephFS | ? | ? | ? |
| Cinder | ? | – | – |
| FC | ? | ? | – |
| FlexVolume | ? | ? | – |
| Flocker | ? | – | – |
| GCEPersistentDisk | ? | ? | – |
| Glusterfs | ? | ? | ? |
| HostPath | ? | – | – |
| iSCSI | ? | ? | – |
| PhotonPersistentDisk | ? | – | – |
| Quobyte | ? | ? | ? |
| NFS | ? | ? | ? |
| RBD | ? | ? | – |
| VsphereVolume | ? | – | – (works when pods are collocated) |
| PortworxVolume | ? | – | ? |
| ScaleIO | ? | ? | – |
| StorageOS | ? | – | – |
2.4 類(Class)
通過 storageClassName 屬性指定存儲類別,即 StorageClass 資源對象的名稱。具有特定類別的 PV 只能與請求了該類別的 PVC 進行綁定。
2.5 回收策略
當(dāng)前的回收策略可選值包括:
- Retain -- 持久化卷被釋放后,仍保留數(shù)據(jù),需要手工進行回收操作;
-
Recycle -- 回收空間,刪除 PVC 后,執(zhí)行基礎(chǔ)擦除命令
rm-rf /thevolume/*(NFS、HostPath 存儲支持); - Delete -- 刪除 PVC 后,會刪除與 PV 相關(guān)的存儲數(shù)據(jù)(AWSEBS、GCE PD 存儲支持);
2.6 掛載參數(shù)(Mount Options)
當(dāng) PV 掛載到一個節(jié)點時,可能需要設(shè)置額外的掛載參數(shù),可以通過 mountOptions 字段設(shè)置。但只有下面的存儲卷類型支持掛載參數(shù):
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- NFS
- iSCSI
- RBD (Ceph Block Device)
- CephFS
- Cinder (OpenStack block storage)
- Glusterfs
- VsphereVolume
- Quobyte Volumes
- VMware Photon
3. 持久卷聲明
下面是一個名稱為 myclaim 的持久卷聲明 YAML 配置文件,它的訪問模式為 ReadWriteOnce,存儲卷模式是 Filesystem,需要的存儲空間大小為 8Gi,指定的存儲類為 slow,并設(shè)置了標(biāo)簽選擇器和匹配表達(dá)式。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc
spec:
accessModes: #訪問模式
- ReadWriteOnce
volumeMode: Filesystem #存儲卷模式
resources: #資源
requests:
storage: 8Gi
storageClassName: slow #存儲類
selector: #選擇器
matchLabels:
release: "stable"
matchExpressions: #匹配表達(dá)式
- {key: environment, operator: In, values: [dev]}
復(fù)制代碼
3.1 選擇器
在 PVC 中,可以通過標(biāo)簽選擇器來進一步的過濾 PV。選擇器的有兩種:
- matchLabels: 只有存在與此處的標(biāo)簽一樣的 PV 才會被 PVC 選中;
- matchExpressions :匹配表達(dá)式由鍵、值和操作符組成,操作符包括 In, NotIn, Exists 和DoesNotExist,只有符合表達(dá)式的 PV 才能被選擇;
如果同時設(shè)置了 matchLabels 和 matchExpressions,則會進行求與(&),即只有同時滿足上述匹配要求的 PV 才會被選擇。
3.2 存儲類
除了使用標(biāo)簽過濾 PV,還可通過 storageClassName 屬性指定存儲類,PV 只有為該存儲類的才能被綁定到 PVC 上。
4. Pod 中使用 PVC
Pod 通過使用 PVC 來訪問 PV,Pod 需與 PVC 在同一個命名空間中,PVC 會與集群中合適的 PV 進行綁定,并將 PV 掛載到主機和 Pod 上。
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd # 掛載的存儲卷的名稱
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim # PVC名稱
復(fù)制代碼
5. 本地持久化存儲
本地持久化存儲(Local Persistent Volume)就是把數(shù)據(jù)存儲到 Pod 運行的宿主機上,其必須保證 Pod 被調(diào)度到具有本地持久化存儲的節(jié)點上。
為什么需要這種類型的存儲呢?有時候你的應(yīng)用對磁盤 IO 有很高的要求,網(wǎng)絡(luò)存儲性能肯定不如本地的高,尤其是本地使用了SSD這種磁盤。
在非本地持久化存儲的場景中,我們會先創(chuàng)建 PV,然后創(chuàng)建 PVC,此時如果兩者匹配則會自動進行綁定。即使是動態(tài) PV 創(chuàng)建,Pod 也會先調(diào)度到某個節(jié)點上,然后根據(jù) PVC 進行創(chuàng)建 PV 最后綁定到 Pod 中。
可是對于本地持久化存儲有一個問題就是 PV 必須提前準(zhǔn)備好,而且并非每一個集群節(jié)點都有該 PV,因此 Pod 不能隨意調(diào)度 。那么如何保證 Pod 一定被調(diào)度到有 PV 的節(jié)點上呢?這時就需要在 PV 中聲明節(jié)點親和性,且 Pod 被調(diào)度的時候還要考慮卷的分布情況。
- 下面是 PV 的定義,即使 node02 節(jié)點上也有
/data/vol1目錄,但 PV 也不一定會在 node2 上,因為nodeAffinity屬性設(shè)置了親和主機 node01。
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local: # local類型
path: /data/vol1 # 節(jié)點上的具體路徑
nodeAffinity: # 這里就設(shè)置了節(jié)點親和
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node01 # 這里我們使用node01節(jié)點,該節(jié)點有/data/vol1路徑
復(fù)制代碼
- 定義存儲類:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
復(fù)制代碼
這里的 volumeBindingMode: WaitForFirstConsumer 很關(guān)鍵,表示為延遲綁定(等待第一個消費者后再進行綁定),該配置會影響 PVC 與 PV 的綁定時機,只有在出現(xiàn)第一個使用指定 PVC 的 Pod 時,才會將 PVC 與 PV 進行綁定。
因為通常 PVC 會在有合適的 PV 時立即綁定,但如果 Pod 被調(diào)度到?jīng)]有該 PV 的節(jié)點上,會導(dǎo)致 Pod 一直被掛起。而延遲綁定的作用即 Pod 的調(diào)度要參考卷的分布。當(dāng)調(diào)度器開始調(diào)度 Pod 的時候會看看它要求的 LPV 在哪里,然后再調(diào)度到該節(jié)點,進行 PVC 的綁定,最后再掛載到 Pod 中,保證了 Pod 所在的節(jié)點一定就是 LPV 所在的節(jié)點。所以讓 PVC 延遲綁定,就是要等到使用這個 PVC 的 Pod 出現(xiàn)在調(diào)度器上之后,根據(jù)綜合評估在來綁定 PVC。
- 定義 PVC:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: local-storage
復(fù)制代碼
可以看到這個 PVC 是 pending 狀態(tài),這也就是延遲綁定,因為此時還沒有 Pod。
- 定義 Pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deploy
spec:
replicas: 1
selector:
matchLabels:
appname: myapp
template:
metadata:
name: myapp
labels:
appname: myapp
spec:
containers:
- name: myapp
image: tomcat:8.5.38-jre8
ports:
- name: http
containerPort: 8080
protocol: TCP
volumeMounts:
- name: tomcatedata
mountPath : "/data"
volumes:
- name: tomcatedata
persistentVolumeClaim:
claimName: local-claim
復(fù)制代碼
此時因為 PV 在 node01 節(jié)點上,因此這個 Pod 被調(diào)度到該節(jié)點上,即使刪除在創(chuàng)建該 Pod,依然會被調(diào)度到 node01 節(jié)點上。并且 PVC 已經(jīng)為綁定狀態(tài)。