要求:先了解數據docker容器中數據卷的掛載等知識
參考網址:
- https://www.cnblogs.com/sanduzxcvbnm/p/13176938.html
- https://www.cnblogs.com/sanduzxcvbnm/p/13371254.html
容器中的存儲都是臨時的,因此Pod重啟的時候,內部的數據會發生丟失。實際應用中,我們有些應用是無狀態,有些應用則需要保持狀態數據,確保Pod重啟之后能夠讀取到之前的狀態數據,有些應用則作為集群提供服務。這三種服務歸納為無狀態服務、有狀態服務以及有狀態的集群服務,其中后面兩個存在數據保存與共享的需求,Kubermetes對於有狀態的容器應用或者對數據需要持久化的應用,不僅需要將容器內的目錄掛載到宿主機的目錄或者emptyDir臨時存儲卷,而且需要更加可靠的存儲來保存應用產生的重要數據,以便容器應用在重建之后,仍然可以使用之前的數據。
Kubernetes Volume(數據卷)主要解決如下兩方面問題:
- 數據持久性:通常情況下,容器運行起來之后,寫入到其文件系統的文件暫時性的。當容器崩潰后,kubelet 將會重啟該容器,此時原容器運行后寫入的文件將丟失,因為容器將重新從鏡像創建。
- 數據共享:同一個 Pod(容器組)中運行的容器之間,經常會存在共享文件/文件夾的需求
Docker 里同樣也存在一個 volume(數據卷)的概念,但是 docker 對數據卷的管理相對 kubernetes 而言要更少一些。在 Docker 里,一個 Volume(數據卷)僅僅是宿主機(或另一個容器)文件系統上的一個文件夾。Docker 並不管理 Volume(數據卷)的生命周期。
在 Kubernetes 里,Volume(數據卷)存在明確的生命周期(與包含該數據卷的容器組相同)。因此,Volume(數據卷)的生命周期比同一容器組中任意容器的生命周期要更長,不管容器重啟了多少次,數據都能被保留下來。當然,如果容器組退出了,數據卷也就自然退出了。此時,根據容器組所使用的 Volume(數據卷)類型不同,數據可能隨數據卷的退出而刪除,也可能被真正持久化,並在下次容器組重啟時仍然可以使用。
從根本上來說,一個 Volume(數據卷)僅僅是一個可被容器組中的容器訪問的文件目錄(也許其中包含一些數據文件)。這個目錄是怎么來的,取決於該數據卷的類型(不同類型的數據卷使用不同的存儲介質)。
使用 Volume(數據卷)時,我們需要先在容器組中定義一個數據卷,並將其掛載到容器的掛載點上。容器中的一個進程所看到(可訪問)的文件系統是由容器的 docker 鏡像和容器所掛載的數據卷共同組成的。Docker 鏡像將被首先加載到該容器的文件系統,任何數據卷都被在此之后掛載到指定的路徑上。Volume(數據卷)不能被掛載到其他數據卷上,或者通過引用其他數據卷。同一個容器組中的不同容器各自獨立地掛載數據卷,即同一個容器組中的兩個容器可以將同一個數據卷掛載到各自不同的路徑上。
我們現在通過下圖來理解 容器組、容器、掛載點、數據卷、存儲介質(nfs、PVC、ConfigMap)等幾個概念之間的關系:
- 一個容器組可以包含多個數據卷、多個容器
- 一個容器通過掛載點決定某一個數據卷被掛載到容器中的什么路徑
- 不同類型的數據卷對應不同的存儲介質

對上段話的理解
1.docker中的數據卷掛載,是指把宿主機的某個路徑或者另外其他的容器路徑給掛載到當前容器中使用
2.Kubernetes 中的 Volume 是存儲的抽象,並且能夠為Pod提供多種存儲解決方案。Volume 最終會映射為Pod中容器可訪問的一個文件夾或裸設備
3.k8s中提供的數據卷掛載,除了宿主機路徑和數據卷容器外,還包含其他的一些掛載路徑,也就是其他的存儲介質,比如說NFS
額外補充知識
k8s中掛載數據卷,還可以掛載數據卷的子路徑:數據卷內子路徑 用於指定將數據卷所對應目錄下的某一個子目錄掛載到容器的掛載點,而不是將數據卷對應的目錄的根路徑掛載到容器的掛載點
Pod中使用數據卷
Pod的spec.volumes中聲明的卷的使用場景:
- Pod中的容器異常退出被重新拉起后,保證之前產生的數據沒有丟失
- Pod中的多個容器共享數據
容器中通過volumeMounts字段使用卷,volumeMounts[].name字段指定它使用的哪個卷,volumeMounts[].mountPath字段指定容器中的掛載路徑。
多個容器共享卷時,可以通過volumeMounts[].subPath字段隔離不同容器在卷上數據存儲的路徑

k8s中支持的數據卷類型
Volumes的類型
- configMap
- hostPath
- emptyDir
- local
- nfs
- cephfs
- secret
- persistentVolumeClaim
- ConfigMap
描述:
ConfigMap 提供了一種向容器組注入配置信息的途徑。ConfigMap 中的數據可以被 Pod(容器組)中的容器作為一個數據卷掛載。
在數據卷中引用 ConfigMap 時:
- 您可以直接引用整個 ConfigMap 到數據卷,此時 ConfigMap 中的每一個 key 對應一個文件名,value 對應該文件的內容
- 您也可以只引用 ConfigMap 中的某一個名值對,此時可以將 key 映射成一個新的文件名
將 ConfigMap 數據卷掛載到容器時,如果該掛載點指定了 數據卷內子路徑 (subPath),則該 ConfigMap 被改變后,該容器掛載的內容仍然不變。
使用場景:
使用 ConfigMap 中的某一 key 作為文件名,對應 value 作為文件內容,替換 nginx 容器中的 /etc/nginx/conf.d/default.conf 配置文件。
參考網址:https://www.cnblogs.com/sanduzxcvbnm/p/13084135.html
- hostPath
描述:
hostPath 類型的數據卷將 Pod(容器組)所在節點的文件系統上某一個文件或文件夾掛載進容器組(容器)。
除了為 hostPath 指定 path 字段以外,您還可以為其指定 type 字段,可選的 type 字段描述如下:

使用 hostPath 數據卷時,必須十分小心,因為:
不同節點上配置完全相同的容器組(例如同一個Deployment的容器組)可能執行結果不一樣,因為不同節點上 hostPath 所對應的文件內容不同
hostPath 對應的文件/文件夾只有 root 可以寫入。您要么在 privileged Container 以 root 身份運行您的進程,要么修改與 hostPath 數據卷對應的節點上的文件/文件夾的權限
容器中通過volumeMounts[].mountPropagation配置掛載傳播
- None(默認):此卷掛載不會接收到任何后續掛載到該卷或是掛載到該卷的子目錄下的掛載
- HostToContainer:此卷掛載將會接收到任何后續掛載到該卷或是掛載到該卷的子目錄下的掛載
- Bidirectional:類似HostToContainer,但Container創建的所有卷掛載都將傳播回主機和所有使用相同卷的容器
適用場景:
絕大多數容器組並不需要使用 hostPath 數據卷,但是少數情況下,hostPath 數據卷非常有用:
- 某容器需要訪問 Docker,可使用 hostPath 掛載宿主節點的 /var/lib/docker
- 在容器中運行 cAdvisor,使用 hostPath 掛載宿主節點的 /sys
- emptyDir
描述:emptyDir類型的數據卷在容器組被創建時分配給該容器組,並且直到容器組被移除,該數據卷才被釋放。該數據卷初始分配時,始終是一個空目錄。同一容器組中的不同容器都可以對該目錄執行讀寫操作,並且共享其中的數據,(盡管不同的容器可能將該數據卷掛載到容器中的不同路徑)。當容器組被移除時,emptyDir數據卷中的數據將被永久刪除
容器崩潰時,kubelet 並不會刪除容器組,而僅僅是將容器重啟,因此 emptyDir 中的數據在容器崩潰並重啟后,仍然是存在的。
適用場景:
- 空白的初始空間,例如合並/排序算法中,臨時將數據存在磁盤上
- 長時間計算中存儲檢查點(中間結果),以便容器崩潰時,可以從上一次存儲的檢查點(中間結果)繼續進行,而不是從頭開始
- 作為兩個容器的共享存儲,使得第一個內容管理的容器可以將生成的頁面存入其中,同時由一個 webserver 容器對外提供這些頁面
- 默認情況下,emptyDir 數據卷被存儲在 node(節點)的存儲介質(機械硬盤、SSD、或者網絡存儲)上。此外,您可以設置 emptyDir.medium 字段為 "Memory",此時 Kubernetes 將掛載一個 tmpfs(基於 RAM 的文件系統)。tmpfs 的讀寫速度非常快,但是與磁盤不一樣,tmpfs 在節點重啟后將被清空,且您向該 emptyDir 寫入文件時,將消耗對應容器的內存限制。
apiVersion: v1
kind: Pod
metadata:
name: tomcat-ccb
namespace: default
labels:
app: tomcat
node: devops-103
spec:
containers:
- name: tomcat
image: docker.io/tomcat
volumeMounts:
- name: tomcat-storage
mountPath: /data/tomcat
- name: cache-storage
mountPath: /data/cache
ports:
- containerPort: 8080
protocol: TCP
env:
- name: GREETING
value: "Hello from devops-103"
volumes:
- name: tomcat-storage
hostPath:
path: /home/es
- name: cache-storage
emptyDir: {}
- local
local類型作為靜態資源被PersistentVolume使用,不支持Dynamic provisioning。與hostPath相比,因為能夠通過PersistentVolume的節點親和策略來進行調度,因此比hostPath類型更加適用。local類型也存在一些問題,如果Node的狀態異常,那么local存儲將無法訪問,從而導致Pod運行狀態異常。使用這種類型存儲的應用必須能夠承受可用性的降低、可能的數據丟失等。
對於使用了PV的Pod,Kubernetes會調度到具有對應PV的Node上,因此PV的節點親和性 nodeAffinity 屬性是必須的。
apiVersion: v1
kind: PersistentVolume
metadata:
name: www
spec:
capacity:
storage: 100Mi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /home/es
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- devops-102
- devops-103
- NFS
描述:
nfs 類型的數據卷可以加載 NFS(Network File System)到您的容器組/容器。容器組被移除時,將僅僅 umount(卸載)NFS 數據卷,NFS 中的數據仍將被保留。
- 可以在加載 NFS 數據卷前就在其中准備好數據;
- 可以在不同容器組之間共享數據;
- 可以被多個容器組加載並同時讀寫;
適用場景:
- 存儲日志文件
- MySQL的data目錄(建議只在測試環境中
- 用戶上傳的臨時文件
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: nfs-client-root
volumes:
- name: nfs-client-root
nfs:
server: 10.6.229.62
path: /nfs/data/class
-
cephfs
cephfs 數據卷使得您可以掛載一個外部 CephFS 卷到您的容器組中。對於 kubernetes 而言,cephfs 與 nfs 的管理方式和行為完全相似,適用場景也相同。不同的僅僅是背后的存儲介質。 -
secret
描述:
secret 數據卷可以用來注入敏感信息(例如密碼)到容器組。您可以將敏感信息存入 kubernetes secret 對象,並通過 Volume(數據卷)以文件的形式掛載到容器組(或容器)。secret 數據卷使用 tmpfs(基於 RAM 的文件系統)掛載。
將 Secret 數據卷掛載到容器時,如果該掛載點指定了 數據卷內子路徑 (subPath),則該 Secret 被改變后,該容器掛載的內容仍然不變。
使用場景:
將 HTTPS 證書存入 kubernets secret,並掛載到 /etc/nginx/conf.d/myhost.crt、/etc/nginx/conf.d/myhost.pem 路徑,用來配置 nginx 的 HTTPS 證書
- Volumes是最基礎的存儲抽象,其支持多種類型,包括本地存儲、NFS、FC以及眾多的雲存儲,我們也可以編寫自己的存儲插件來支持特定的存儲系統。Volume可以被Pod直接使用,也可以被PV使用。普通的Volume和Pod之間是一種靜態的綁定關系,在定義Pod的同時,通過volume屬性來定義存儲的類型,通過volumeMount來定義容器內的掛載點。
- PersistentVolume。與普通的Volume不同,PV是Kubernetes中的一個資源對象,創建一個PV相當於創建了一個存儲資源對象,這個資源的使用要通過PVC來請求。
- PersistentVolumeClaim。PVC是用戶對存儲資源PV的請求,根據PVC中指定的條件Kubernetes動態的尋找系統中的PV資源並進行綁定。目前PVC與PV匹配可以通過StorageClassName、matchLabels或者matchExpressions三種方式。
- persistentVolumeClaim
persistentVolumeClaim 數據卷用來掛載 PersistentVolume 存儲卷。
數據卷掛載(比較重要)
參考文章:https://www.cnblogs.com/sanduzxcvbnm/p/13072429.html
問題
Pod中聲明的Volume生命周期與Pod相同,有幾種常見的缺點:
- Deployment升級時,新Pod難以復用舊Pod的數據;
- Pod跨主機遷移時,做不到帶Volume遷移;
- 只能做到同一個Pod中的多個容器共享數據,做不到多個Pod之間共享數據;
- 很難實現功能擴展
PV & PVC
PersistentVolume(PV)是集群中由管理員配置的一段網絡存儲。 它是集群中的資源,就像節點是集群資源一樣。 PV是容量插件,如Volumes,但其生命周期獨立於使用PV的任何單個pod。 能夠支持多種數據存儲服務器,通過PV,我們能在K8S集群中,把我們的數據持久化到外部的服務器中。
pv
PV作為存儲資源,主要包括存儲能力、訪問模式、存儲類型、回收策略、后端存儲類型等關鍵信息的設置。下面的例子聲明的PV具有如下屬性: 5Gi 存儲空間,訪問模式為“ReadWriteOnce”,存儲類型為“slow" (要求系統中已存在名為slow的StorageClass),回收策略為“Recycle",並且后端存儲類型為“nfs”(設置了NFS Server的IP地址和路徑):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
nfs:
path: /tmp
server: 172.17.0.2
PV能夠支持的數據存儲服務類型
- gcePersistentDisk: GCE公有雲提供的PersistentDisk。
- AWSElasticBlockStore: AWS公有雲提供的ElasticBlockStore.
- AzureFile: Azure公有雲提供的File。
- AzureDisk: Azure 公有雲提供的Disk。
- FC ( Fibre Channel)。
- Flocker。
- NFS:網絡文件系統。
- iSCSI。
- RBD (Rados Block Device); Ceph塊存儲。
- CephFS。
- Cinder: OpenStack Cinder塊存儲。
- GlusterFS。
- VsphereVolume.
- Quobyte Volumes。
- VMware Photon。
- Portworx Volumes。
- ScaleIO Volumes。
- HostPath:宿主機目錄,僅用於單機測試。
每種存儲類型都有各自的特點,在使用時需要根據它們各自的參數進行設置。
1、Capacity(容量)
描述存儲設備具備的能力,目前僅支持對存儲空間的設置(storage=xx),未來可能加入
2、訪問模式(Access Modes)
ReadWriteOnce (簡寫為 RWO): 讀寫權限,並且只能被單個Node掛載。
ReadOnlyMany (簡寫為 ROX): 只讀權限,允許被多個Node掛載。
ReadWriteMany (簡寫為 RWX): 讀寫權限,允許被多個Node掛載。
注意:即使volume支持很多種訪問模式,但它同時只能使用一種訪問模式。比如,GCEPersistentDisk可以被單個節點映射為ReadWriteOnce,或者多個節點映射為ReadOnlyMany,但不能同時使用這兩種方式來映射。

3、存儲類別(Class)
PV可以設定其存儲的類別(Class),通過storageClassName參數指定一個StorageClass資源對象的名稱。具有特定“類別”的PV只能與請求該“類別”的PVC進行綁定。未設定“類別”的PV則只能與不請求任何“類別”的PVC進行綁定。
4、回收策略(Reclaim Policy)
目前支持如下三種回收策略。
保留(Retain): 保留數據,需要手工處理。
回收空間( Recycle): 簡單清除文件的操作(例如執行rm -rf /thevolume/*命令)。
刪除(Delete): 與PV相連的后端存儲完成volume的刪除操作(如AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等設備的內部volume清理)。
目前,只有NFS和HostPath兩種類型的存儲支持“Recycle”策略; AWS EBS、GCE PD、Azure Disk和Cinder volumes支持“Delete”策略。
- PV生命周期的各個階段( Phase )
某個PV在生命周期中,可能處於以下4個階段之一。
Available: 可用狀態,還未與某個PVC綁定。
Bound: 已與某個PVC綁定。
Released: 綁定的PVC已經刪除,資源已釋放,但沒有被集群回收。
Failed: 自動資源回收失敗。
- 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”
accessModes :
- ”ReadWriteOnce”
gcePersistentDisk:
fsType: "ext4"
pdName : "gce-disk-1
並非所有類型的存儲都支持設置掛載參數。從Kubernetes v1.6版本開始,以下存儲類型支持設置掛載參數。
- gcePersistentDisk。
- AWSElasticBlockStore.
- AzureFile。
- AzureDisk。
- NFS。
- iSCSI。
- RBD
- (Rados Block Device): Ceph 塊存儲。
- CephFS。
- Cinder: OpenStack 塊存儲。
- GlusterFS。
- VsphereVolume.
- Quobyte Volumes.
- VMware Photon。
PVC
PVC(PersistentVolumeClaim)是用戶對PV的一次申請。PVC對於PV就像Pod對於Node一樣,Pod可以申請CPU和Memory資源,而PVC也可以申請PV的大小與權限。
PVC 作為用戶對存儲資源的需求申請,主要包括存儲空間請求、訪問模式、PV選擇條件和存儲類別等信息的設置。下面的例子中聲明的PVC具有如下屬性:申請8Gi存儲空間,訪問模式為"ReadWriteOnce",PV選擇條件為包含標簽"release=stable"並且包含條件為"environment In [dev]"的標簽,存儲類別為"slow"(要求系統中已存在名為slow的StorageClass)。
kind: PersistentVolumeClaim
apiVersion: v1
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也可以設置訪問模式,用於描述用戶應用對存儲資源的訪問權限。可以設置的三種訪問模式與PV相同。
PV選擇條件(Selector):通過Label Selector的設置,可使PVC對於系統中已存在的各種PV進行篩選。系統將根據標簽選擇出合適的PV與該PVC進行綁定。選擇條件可以使用matchLabels和matchExpressions進行設置。如果兩個條件都設置了,則Selector的邏輯是兩組條件同時滿足才能完成匹配。
存儲類別(Class):PVC在定義時可以設定需要的后端存儲的"類別"(通過storageClassName字段指定),以降低對后端存儲特性的詳細信息的依賴。只有設置了該Class的PV才能被系統選出,並與該PVC進行綁定。
PVC也可以不設置Class需求。如果storageClassName字段的值被設置為空(storageClassName=""),則表示該PVC不要求特定的Class,系統將只選擇未設定Class的PV與之匹配和綁定。PVC也可以完全不設置storageClassName字段,此時將根據系統是否啟用了名為"DefaultStorageClass"的admission controller進行相應的操作。
未啟用DefaultStorageClass:等效於PVC設置storageClassName的值為空,即只能選擇未設定Class的PV與之匹配和綁定。
啟用了DefaultStorageClass:要求集群管理員已定義默認的StorageClass。如果系統中不存在默認的StorageClass,則等效於不啟用DefaultStorageClass的情況。如果存在默認的StorageClass,則系統將自動為PVC創建一個PV(使用默認StorageClass的后端存儲),並將它們進行綁定。集群管理員設置默認StorageClass的方法為,在StorageClass的定義中加上一個annotation "storageclass.kubernetes.io/is-default-class=true"。如果管理員將多個StorageClass都定義為default,則由於不唯一,系統將無法為PVC創建相應的PV。
注意,PVC和PV都受限於namespace,PVC在選擇PV時受到namespace的限制,只有相同namespace中的PV才可能與PVC綁定。Pod在引用PVC時同樣受namespace的限制,只有相同namespace中的PVC才能掛載到Pod內。
當Selector和Class都進行了設置時,系統將選擇兩個條件同時滿足的PV與之匹配。
另外,如果資源供應使用的是動態模式,即管理員沒有預先定義PV,僅通過StorageClass交給系統自動完成PV的動態創建,那么PVC再設定Selector時,系統將無法為其供應任何存儲資源了。
在啟動動態供應模式的情況下,一旦用戶刪除了PVC,與之綁定的PV將根據其默認的回收策略"Delete"也會被刪除。如果需要保留PV(用戶數據),則在動態綁定成功后,用戶需要將系統自動生成PV的回收策略從"Delete"改成"Retain"。
PV是和外部的服務器相連,Pod是不能使用的,要用PersistentVolumeClaim 簡稱 PVC 的資源來連接PV,Pod連接PVC 就可以使用了。類似於消息中間件的生產者和消費者的關系。PV是生產者,PVC是消費者。
PVC和PV是一一對應的,一個PV被PVC綁定后,不能被別的PVC綁定。
PV和PVC的生命周期

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

使用案例
1.使用nfs作為存儲客戶端
# 安裝nfs
yum install -y nfs-utils
# 創建nfs目錄
mkdir -p /nfs/data/nginx
# 授予權限
chmod -R 777 /nfs/data
# 編輯export文件
vim /etc/exports
/nfs/data/nginx *(rw,no_root_squash,sync)
# 使得配置生效
exportfs -r
# 查看生效
exportfs
# 啟動rpcbind、nfs服務
systemctl restart rpcbind && systemctl enable rpcbind
systemctl restart nfs && systemctl enable nfs
# 查看rpc服務的注冊情況
rpcinfo -p localhost
# showmount測試
showmount -e ip(ip地址)
2.在K8S集群所有節點上安裝NFS客戶端
yum -y install nfs-utils
systemctl start nfs && systemctl enable nfs
- 定義PV,跟NFS做關聯
# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
nfs:
path: /nfs/data/nginx
server: 10.6.229.62
4.定義pvc,使用pv
# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
- 定義pod,使用pvc
使用 persistentVolumeClaim:claimName: nginx-pvc 定義了要使用名字為nginx-pvc的 PVC
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nginx-persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-persistent-storage
persistentVolumeClaim:
claimName: nginx-pvc
- 查看效果
當提交完yaml之后,pod可以通過該PVC找到綁定的PV。掛載完成后,在Pod內可以看到掛載,在主機上也可以看到掛載。
總結1
可以把上述pv&pvc綁定理解成靜態方式,集群管理員事先規划用戶怎樣使用存儲,並預分配一些存儲(即預創建一些 PV)

pv配置yaml文件中的有關參數解釋:
spec.capacity.storage字段表明創建的存儲的大小
spec.accessModes字段是PV訪問策略控制列表,表明創建出來的存儲的訪問方式
ReadWriteOnce只允許單node上的Pod訪問
ReadOnlyMany允許多個node的Pod只讀訪問
ReadWriteMany允許多個node上的Pod讀寫訪問
spec.persistentVolumeReclaimPolicy表明該Volume使用后被release之后(即與之綁定的PVC被刪除后)的回收策略
spec.nodeAffinity字段限制了可以訪問該volumes的node,對使用該Volume的Pod調度有影響
spec.csi表明此處使用的是CSI存儲:
driver:指定由什么volume plugin來掛載該volume(需要提前在node上部署)
volumeHandle:指示PV的唯一標簽
volumeAttribute:用於附加參數,比如PV定義的是OSS,就可以在這里定義bucket、訪問的地址等信息
spec.flexVolume表明此處使用的是flexVolume存儲:
driver:實現的驅動類型
fsType:文件系統類型
options:包含的具體的參數
PV 狀態
- Available:未被任何PVC使用
- Bound:綁定到了PVC上
- Released:PVC被刪掉,資源未被使用
- Failed:自動回收失敗
pvc配置yaml文件中的有關參數解釋:
spec.resources.requests.storage字段聲明存儲的大小需求
spec.accessModes字段聲明訪問方式
一些屬性
- Access Modes
- Volume Modes
- Resources
- Selector:PVC可以通過標簽選擇器選擇PV資源。可以包含兩個字段matchLabels和matchExpressions。
- storageClassName 類似標簽選擇器,通過storagClassName 來確定PV資源。
PV的狀態流轉:
創建PV對象后,它會暫時處於pending狀態。等真正的PV創建好之后,它處在available狀態(可以使用)。
創建PVC對象后,它會暫時處於pending狀態,直到PV和PVC就結合到一起,此時兩者都處在bound狀態。
當用戶在使用完PVC將其刪除后,PV處於released狀態,依賴於ReclaimPolicy配置被刪除或保留:
- Recycle(已廢棄):會進入Available狀態;如果轉變失敗,就會進入Failed 的狀態。
- Delete:Volume被released之后直接刪除,需要volume plugin支持
- Retain:默認策略,由系統管理員來手動管理該Volume。此時PV已經處在released狀態下,沒有辦法再次回到available狀態(即無法被一個新的PVC綁定),需要:
新建一個PV對象,把之前的released的PV的相關字段的信息填到新的PV對象里面,這個PV就可以結合新的PVC了;
刪除pod之后不刪除PVC對象,這樣給PV綁定的PVC還是存在的,下次pod使用的時候,就可以直接通過PVC去復用。(K8s中的StatefulSet管理的Pod帶存儲的遷移就是通過這種方式)。
修改PV的spec.claimRef字段,該字段記錄着原來PVC的綁定信息,刪除綁定信息,即可重新釋放PV從而達到Available。
處於Bound狀態的PVC,與其關聯的PV被刪除后,變成Lost狀態;重新與PV綁定后變成Bound狀態。
問題1:PV,PVC都定義好了,在PVC定義中並沒有看到和PV綁定的相關配置,那他們兩個是怎么綁定的呢?
K8s內部相關組件會根據PVC的size和accessMode,判斷它跟靜態創建的哪個PV匹配,然后綁定到一起。
注:一個PV可以設置多個訪問策略。PVC與PV綁定時,PV Controller會優先找到AccessModes列表最短並且匹配PVC AccessModes列表的PV集合,然后從該集合中找到Capacity最小且符合PVC size需求的PV對

問題2:關聯pvc到特定的pv?
參考網址:https://www.cnblogs.com/sanduzxcvbnm/p/14695032.html
問題3:
1.使用的pv數量比較多,難道需要一一創建?
2.pvc沒有找到合適的pv進行綁定,需要再次手動創建合適的pv,這樣是不是太麻煩了?
解決辦法:
假如有一個模板,pvc想要多少資源,直接在模板里配置好,啟動的時候自動創建對應的pv資源,上面的問題是不是就解決了。這就是StorageClass的功能
StorageClass
可以把上述步驟理解成靜態產生方式 - Static Volume Provisioning,使用StorageClass可以理解成是動態產生方式 - Dynamic Volume Provisioning
集群管理員不預分配PV,而是預先准備StoregeClass(創建PV的模板),它包含了創建某種具體類型(塊存儲、文件存儲等)的PV所需要的參數信息。StorageClass為管理員提供了描述存儲 "類" 的方法

每個StorageClass都包含字段provisioner、參數和reclaimPolicy,這些字段在屬於該類的PersistentVolume需要動態配置時使用。
StorageClass對象的名稱很重要,它是用戶請求特定類的方式。管理員在第一次創建StorageClass對象時設置類的名稱和其他參數,這些對象一旦創建就無法更新。
StorageClass聲明存儲插件,用於自動創建PV。說白了就是創建PV的模板,其中有兩個重要部分:PV屬性和創建此PV所需要的插件。這樣PVC就可以按“Class”來匹配PV。可以為PV指定storageClassName屬性,標識PV歸屬於哪一個Class。
StorageClass的定義主要包括名稱、后端存儲的提供者(Provisioner)和后端存儲的相關參數配置。StorageClass一旦被創建出來,就將無法修改,只能刪除原StorageClass的定義重建。下面的例子中定義了一個名為“standard"的StorageClass,提供者為aws-ebs,其參數設置了一個type=gp2。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
1 StorageClass的關鍵配置參數
1)提供者(Provisioner)
描述存儲資源的提供者,也可以看作后端存儲驅動。
目前k8s支持的Provisioner都以"kubernetes.io/"為開頭,用戶也可以使用自定義的后端存儲提供者。為了符合StorageClass的用法,自定義Provisioner需要符合存儲卷的開發規范,詳見該鏈接的說明:https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/volume-provisioning.md 。
2)參數(Parameters)
后端存儲資源提供者的參數設置,不同的Provisioner包括不同的參數設置。某些參數可以不顯示設定,Provisioner將使用其默認值。
設置默認的StorageClass
要在系統中設置一個默認的StorageClass,首先需要啟動名為"DefaultStorageClass"的admission controller, 即在kube-apiserver的命令行參數--admission-controll中增加:
--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/cinder
parameters:
type: fast
availability: nova
通過kubectl create命令創建成功后,查看StorageClass列表,可以看到名為gold的StorageClass被標記為"default":
# kubectl get sc
gold (default) kubernetes.io/cinder
1、storageClassName 類似標簽選擇器,通過storagClassName 來確定PV資源。
2、PVC與PV匹配可以通過StorageClassName、matchLabels或者matchExpressions三種方式
對於PV或者StorageClass只能對應一種后端存儲
對於手動的情況,一般我們會創建很多的PV,等有PVC需要使用的時候就可以直接使用了
對於自動的情況,那么就由StorageClass來自動管理創建
如果Pod想要使用共享存儲,一般會在創建PVC,PVC中描述了想要什么類型的后端存儲、空間等,K8s從而會匹配對應的PV,如果沒有匹配成功,Pod就會處於Pending狀態。Pod中使用只需要像使用volumes一樣,指定名字就可以使用了
一個Pod可以使用多個PVC,一個PVC也可以給多個Pod使用
一個PVC只能綁定一個PV,一個PV只能對應一種后端存儲
sc實際操作
1.准備好NFS服務器[並且確保nfs可以正常工作],創建持久化需要的目錄。
path: /nfs/data/class
server: 10.6.229.62
以下yaml配置文件的參考網址:https://github.com/kubernetes-retired/external-storage/tree/master/nfs-client/deploy
2.創建rbac.yaml
這個文件是創建授權賬戶。在K8S中, ApiServer 組件管理創建的 deployment, pod,service等資源,但是有些資源它是管不到的,比如說 K8S本身運行需要的組件等等,同樣StorageClass這種資源它也管不到,所以,需要授權賬戶。
在master上創建:kubectl apply -f rbac.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io
3.根據deployment.yaml文件創建資源
在master上創建:kubectl apply -f deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: nfs-provisioner
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: 10.6.229.62
- name: NFS_PATH
value: /nfs/data/class
volumes:
- name: nfs-client-root
nfs:
server: 10.6.229.62
path: /nfs/data/class
4.根據class.yaml創建資源
在master上創建:kubectl apply -f class.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-nfs
provisioner: example.com/nfs # must match deployment's env PROVISIONER_NAME'
5.根據my-pvc.yaml創建資源
在master上創建:kubectl apply -f my-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
# 我們定義了1M的PVC,storageClass會自動給我們創建這個資源,不用再去手動創建1M的PV
storage: 1Mi
# 這個名字要和上面創建的storageclass名稱一致
storageClassName: example-nfs
6.根據nginx-pod.yaml創建資源
在master上創建:kubectl apply -f nginx-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: my-pvc
mountPath: "/usr/class"
restartPolicy: "Never"
volumes:
- name: my-pvc
persistentVolumeClaim:
claimName: my-pvc
需要注意一點的是,NFS服務器下的/nfs/data/class並不是數據存儲的根目錄,pod起來之后,會自動在里面創建一個文件夾來存放數據。
部分參數解析:
-
reclaimPolicy
表示由SC動態創建出來的PV使用結束后(Pod及PVC被刪除后)該怎么處理,可以是Delete(默認)或者 Retain -
allowVolumeExpansion
若設置為true,則部分存儲(如CSI)支持卷擴展,可以編輯PVC對象來擴大(不能縮小)卷大小 -
mountOptions
由SC動態創建的PV將使用該字段指定的掛載選項。但掛載選項在SC和PV上都不會做驗證,如果卷插件不支持掛載選項,則分配操作會失敗。 -
volumeBindingMode
控制了卷綁定和動態分配的發生時間。
Immediate模式表示一旦創建了PVC,也就完成了卷的動態分配和綁定。對於由於拓撲限制而非集群所有節點可達的存儲后端,PV會在不知道Pod調度要求的情況下分配、綁定。
WaitForFirstConsumer模式將延遲PV的分配和綁定,直到使用該PVC的Pod被創建。PV再根據Pod調度約束指定的拓撲來選擇和分配。 -
allowedTopologies
指定了允許的拓撲結構
volumeBindingMode配置為WaitForFirstConsumer模式的情況下一般無需再進行配置此字段
需要動態創建的PV就有拓撲位置的限制時需要配置同時配置WaitForFirstConsumer和allowedTopologies,這樣既限制了動態創建的PV要能被這個可用區訪問、也限制了Pod在選擇node時要落在這個可用區內 -
parameters
以key:values的形式描述屬於卷的參數,最多可定義512個,總長度不超過256KB
PV分配器可以接受哪些參數,需要后端存儲的提供者提供。
用戶在PVC中指定需要使用的StoregeClass模板。PVC配置文件里存儲的大小、訪問模式的配置是不變的,只需新增spec.StorageClassName字段,指定動態創建PV的模板文件的名字。
新增后,若PVC找不到相應的PV,就會用該標簽所指定的StorageClass去動態創建PV,並將PVC和PV進行綁定;存在一個滿足條件的PV時,就會直接使用現有的PV。
PV的狀態和回收策略
PV的狀態
Available:表示當前的pv沒有被綁定
Bound:表示已經被pvc掛載
Released:pvc沒有在使用pv, 需要管理員手工釋放pv
Failed:資源回收失敗
PV回收策略
Retain:表示刪除PVC的時候,PV不會一起刪除,而是變成Released狀態等待管理員手動清理
Recycle:在Kubernetes新版本就不用了,采用動態PV供給來替代
Delete:表示刪除PVC的時候,PV也會一起刪除,同時也刪除PV所指向的實際存儲空間
注意:目前只有NFS和HostPath支持Recycle策略。AWS EBS、GCE PD、Azure Disk和Cinder支持Delete策略
存儲快照
k8s引入了存儲快照功能,不過只有CSI支持
VolumeSnapshotContent/VolumeSnapshot/VolumeSnapshotClass體系與PV/PVC/SC體系類似
首先創建VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1a1pha1
kind: VolumeSnapshotClass
metadata:
name: disk-snapshotclass
snapshotter: diskplug.csi.alibabacloud.com
snapshotter指定了真正創建存儲快照所使用的Volume Plugin
然后通過Volumesnapshot聲明創建存儲快照:
apiVersion: snapshot.storage.k8s.io/v1a1pha1
kind: VolumeSnapshot
metadata:
name:disk-snapshot
namespace: xxx
spec:
snapshotClassName: disk-snapshotclass
source:
name: disk-pvc
kind: PersistentVolumeClaim
spec.snapshotClassName指定了使用的VolumeSnapshotClass
spec.source.name指定了作為數據源的PVC
提交VolumeSnapshot對象后,集群中的相關組件會找到數據源PVC對應的PV存儲,對這個PV存儲做一次快照。並自動創建volumesnapshotcontent對象。
volumesnapshotcontent對象中記錄了雲存儲廠商返回的snapshot的ID、作為數據源的PVC
刪除VolumeSnapshot后volumesnapshotcontent也會自動刪除
數據恢復時,將PVC對象的spec.dataSource指定為VolumeSnapshot對象。這樣當PVC提交之后,會由集群中的相關組件找到dataSource所指向的VolumeSnapshotContent,然后新創建對應的存儲以及pv對象,將存儲快照數據恢復到新的pv中。
k8s的存儲架構

參考網址:
https://www.orchome.com/1284#item-4
https://www.cnblogs.com/yangyuliufeng/p/14301359.html
https://blog.csdn.net/cuixhao110/article/details/105858553
https://blog.csdn.net/qq_33591903/article/details/103783627
https://www.cnblogs.com/cocowool/p/kubernetes_storage.html
https://www.cnblogs.com/fat-girl-spring/p/14545547.html
