k8s (十二) --- Kubernetes 存儲 之 PV 持久卷

一、persistent volumes簡介

簡介

管理存儲和管理計算有着明顯的不同。PersistentVolume給用戶和管理員提供了一套API,抽象出存儲是如何提供和消耗的細節。在這裏,我們介紹兩種新的API資源:PersistentVolume(簡稱PV)和PersistentVolumeClaim(簡稱PVC)。

  • PersistentVolume(持久卷,簡稱PV) 是集羣內,由管理員提供的網絡存儲的一部分。就像集羣中的節點一樣,PV也是集羣中的一種資源。它也像Volume一樣,是一種volume插件,但是它的生命週期卻是和使用它的Pod相互獨立的。PV這個API對象,捕獲了諸如NFS、ISCSI、或其他雲存儲系統的實現細節。

  • PersistentVolumeClaim(持久卷聲明,簡稱PVC) 是用戶的一種存儲請求。它和Pod類似,Pod消耗Node資源,而PVC消耗PV資源。Pod能夠請求特定的資源(如CPU和內存)。PVC能夠請求指定的大小和訪問的模式(可以被映射爲一次讀寫或者多次只讀)。

PVC允許用戶消耗抽象的存儲資源,用戶也經常需要各種屬性(如性能)的PV。集羣管理員需要提供各種各樣、不同大小、不同訪問模式的PV,而不用向用戶暴露這些volume如何實現的細節。因爲這種需求,就催生出一種StorageClass資源。

StorageClass提供了一種方式,使得管理員能夠描述他提供的存儲的等級。集羣管理員可以將不同的等級映射到不同的服務等級、不同的後端策略。

pv和pvc的區別

PersistentVolume(持久卷)和PersistentVolumeClaim(持久卷申請)是k8s提供的兩種API資源,用於抽象存儲細節。管理員關注於如何通過pv提供存儲功能而無需關注用戶如何使用,同樣的用戶只需要掛載pvc到容器中而不需要關注存儲卷採用何種技術實現。

pvc和pv的關係與pod和node關係類似,前者消耗後者的資源。pvc可以向pv申請指定大小的存儲資源並設置訪問模式,這就可以通過Provision -> Claim 的方式,來對存儲資源進行控制。

二、volume和claim的生命週期

PV是集羣中的資源,PVC是對這些資源的請求,同時也是這些資源的“提取證”。PV和PVC的交互遵循以下生命週期:

供給

有兩種PV提供的方式:靜態和動態。

靜態

集羣管理員創建多個PV,它們攜帶着真實存儲的詳細信息,這些存儲對於集羣用戶是可用的。它們存在於Kubernetes API中,並可用於存儲使用。

動態

當管理員創建的靜態PV都不匹配用戶的PVC時,集羣可能會嘗試專門地供給volume給PVC。這種供給基於StorageClass:PVC必須請求這樣一個等級,而管理員必須已經創建和配置過這樣一個等級,以備發生這種動態供給的情況。請求等級配置爲“ ”的PVC,有效地禁用了它自身的動態供給功能。

綁定

用戶創建一個PVC(或者之前就已經就爲動態供給創建了),指定要求存儲的大小和訪問模式。master中有一個控制迴路用於監控新的PVC,查找匹配的PV(如果有),並把PVC和PV綁定在一起。如果一個PV曾經動態供給到了一個新的PVC,那麼這個迴路會一直綁定這個PV和PVC。另外,用戶總是至少能得到它們所要求的存儲,但是volume可能超過它們的請求。一旦綁定了,PVC綁定就是專屬的,無論它們的綁定模式是什麼。

如果沒找到匹配的PV,那麼PVC會無限期得處於unbound未綁定狀態,一旦PV可用了,PVC就會又變成綁定狀態。比如,如果一個供給了很多50G的PV集羣,不會匹配要求100G的PVC。直到100G的PV添加到該集羣時,PVC纔會被綁定。

使用

Pod使用PVC就像使用volume一樣。集羣檢查PVC,查找綁定的PV,並映射PV給Pod。對於支持多種訪問模式的PV,用戶可以指定想用的模式。一旦用戶擁有了一個PVC,並且PVC被綁定,那麼只要用戶還需要,PV就一直屬於這個用戶。用戶調度Pod,通過在Pod的volume塊中包含PVC來訪問PV。

釋放

當用戶使用PV完畢後,他們可以通過API來刪除PVC對象。當PVC被刪除後,對應的PV就被認爲是已經是“released”了,但還不能再給另外一個PVC使用。前一個PVC的屬於還存在於該PV中,必須根據策略來處理掉。

回收

PV的回收策略告訴集羣,在PV被釋放之後集羣應該如何處理該PV。當前,PV可以被Retained(保留)、 Recycled(再利用)或者Deleted(刪除)。保留允許手動地再次聲明資源。對於支持刪除操作的PV卷,刪除操作會從Kubernetes中移除PV對象,還有對應的外部存儲(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。動態供給的卷總是會被刪除。

三、PV詳解

每個PV都包含一個spec和狀態,即說明書和PV卷的狀態,如以下示例:

[root@server1 volumes]# vim pv1.yaml 
[root@server1 volumes]# cat pv1.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 500Mi					#設置pv大小
  volumeMode: Filesystem			#卷類型
  accessModes:
    - ReadWriteOnce							#訪問模式
  persistentVolumeReclaimPolicy: Recycle		#回收策略
  storageClassName: slow			#存儲類別名稱
  nfs:
    server: 172.25.63.1			#設置nfs掛載路徑和服務器地址
    path: /nfs

創建pv:

[root@server1 volumes]# kubectl create -f pv1.yaml 
persistentvolume/pv1 created

查看pv:

[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           slow                    11s

刪除:

[root@server1 volumes]# kubectl delete -f pv1.yaml 
persistentvolume "pv1" deleted

Capacity(容量)

一般來說,PV會指定存儲的容量,使用PV的capacity屬性來設置。查看Kubernetes的Resource Model來了解capacity。
  當前,存儲大小是唯一能被設置或請求的資源。未來可能包含IOPS,吞吐率等屬性。

訪問模式

PV可以使用存儲資源提供商支持的任何方法來映射到host中。如下的表格中所示,提供商有着不同的功能,每個PV的訪問模式被設置爲卷支持的指定模式。比如,NFS可以支持多個讀/寫的客戶端,但可以在服務器上指定一個只讀的NFS PV。每個PV有它自己的訪問模式。

訪問模式包括:
   ▷ ReadWriteOnce —— 該volume只能被單個節點以讀寫的方式映射
   ▷ ReadOnlyMany —— 該volume可以被多個節點以只讀方式映射
   ▷ ReadWriteMany —— 該volume只能被多個節點以讀寫的方式映射
 
在CLI中,訪問模式可以簡寫爲:
   ▷ RWO - ReadWriteOnce
   ▷ ROX - ReadOnlyMany
   ▷ RWX - ReadWriteMany
   
注意:即使volume支持很多種訪問模式,但它同時只能使用一種方式來映射。比如,GCEPersistentDisk可以被單個節點映射爲ReadWriteOnce,或者多個節點映射爲ReadOnlyMany,但不能同時使用這兩種方式來映射。

在這裏插入圖片描述

Class

一個PV可以有一種class,通過設置storageClassName屬性來選擇指定的StorageClass。有指定class的PV只能綁定給請求該class的PVC。沒有設置storageClassName屬性的PV只能綁定給未請求class的PVC。

過去,使用volume.beta.kubernetes.io/storage-class註解,而不是storageClassName屬性。該註解現在依然可以工作,但在Kubernetes的未來版本中已經被完全棄用了。

回收策略

當前的回收策略有:
   ▷ Retain:手動回收
   ▷ Recycle:需要擦除後才能再使用
   ▷ Delete:相關聯的存儲資產,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都會被刪除

當前,只有NFS和HostPath支持回收利用,AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持刪除操作。

階段

一個volume卷處於以下幾個階段之一:
   ▷ Available:空閒的資源,未綁定給PVC
   ▷ Bound:綁定給了某個PVC
   ▷ Released:PVC已經刪除了,但是PV還沒有被集羣回收
   ▷ Failed:PV在自動回收中失敗了

CLI可以顯示PV綁定的PVC名稱。

四、PersistentVolumeClaims(PVC)詳解

每個PVC都包含一個spec和status,即該PVC的規則說明和狀態。

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使用的規則和PV相同。

資源

PVC,就像pod一樣,可以請求指定數量的資源。請求資源時,PV和PVC都使用相同的資源樣式。

選擇器(Selector)

PVC可以指定標籤選擇器進行更深度的過濾PV,只有匹配了選擇器標籤的PV才能綁定給PVC。選擇器包含兩個字段:
   ● matchLabels(匹配標籤) - PV必須有一個包含該值得標籤
   ● matchExpressions(匹配表達式) - 一個請求列表,包含指定的鍵、值的列表、關聯鍵和值的操作符。合法的操作符包含In,NotIn,Exists,和DoesNotExist。
  所有來自matchLabels和matchExpressions的請求,都是邏輯與關係的,它們必須全部滿足才能匹配上。

等級(Class)

PVC可以使用屬性storageClassName來指定StorageClass的名稱,從而請求指定的等級。只有滿足請求等級的PV,即那些包含了和PVC相同storageClassName的PV,才能與PVC綁定。

PVC並非必須要請求一個等級。設置storageClassName爲“”的PVC總是被理解爲請求一個無等級的PV,因此它只能被綁定到無等級的PV(未設置對應的標註,或者設置爲“”)。未設置storageClassName的PVC不太相同,DefaultStorageClass的權限插件打開與否,集羣也會區別處理PVC。
   • 如果權限插件被打開,管理員可能會指定一個默認的StorageClass。所有沒有指定StorageClassName的PVC只能被綁定到默認等級的PV。要指定默認的StorageClass,需要在StorageClass對象中將標註storageclass.kubernetes.io/is-default-class設置爲“true”。如果管理員沒有指定這個默認值,集羣對PVC創建請求的迴應就和權限插件被關閉時一樣。如果指定了多個默認等級,那麼權限插件禁止PVC創建請求。
   
   • 如果權限插件被關閉,那麼久沒有默認StorageClass的概念。所有沒有設置StorageClassName的PVC都只能綁定到沒有等級的PV。因此,沒有設置StorageClassName的PVC就如同設置StorageClassName爲“”的PVC一樣被對待。

根據安裝方法的不同,默認的StorageClass可能會在安裝過程中被插件管理默認的部署在Kubernetes集羣中。

當PVC指定selector來請求StorageClass時,所有請求都是與操作的。只有滿足了指定等級和標籤的PV纔可能綁定給PVC。當前,一個非空selector的PVC不能使用PV動態供給。

過去,使用volume.beta.kubernetes.io/storage-class註解,而不是storageClassName屬性。該註解現在依然可以工作,但在Kubernetes的未來版本中已經被完全棄用了。

使用PVC

Pod通過使用PVC(使用方式和volume一樣)來訪問存儲。PVC必須和使用它的pod在同一個命名空間,集羣發現pod命名空間的PVC,根據PVC得到其後端的PV,然後PV被映射到host中,再提供給pod。

五、nfs pv示例

1.首先搭建nfs服務器,這裏不做演示。

2.創建NFS PV 卷

[root@server1 volumes]# vim pv2.yaml
[root@server1 volumes]# cat pv2.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 500Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    server: 172.25.63.1
    path: /nfs

創建並查看狀態:

[root@server1 volumes]# kubectl create -f pv2.yaml 
persistentvolume/pv1 created
[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           nfs                     21s

可以看到狀態是Available

3.創建PVC

[root@server1 volumes]# vim pvc2.yaml 
[root@server1 volumes]# cat pvc2.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

注意我們在上述文件中只是聲明我們要求的PV是什麼樣子的,我們這裏的要求與上面創建的PV相同。

同時需要注意的是隻有pv滿足storageClassName,accessModes,resources的所有條件纔會被綁定。

創建並查看:

[root@server1 volumes]# kubectl create -f pvc2.yaml 
persistentvolumeclaim/pvc1 created
[root@server1 volumes]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv1      500Mi      RWO            nfs            7s

可以看到pvc1的狀態已經是Bound綁定狀態了,且綁定的是pv1,然後我們現在查看pv狀態:

[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Bound    default/pvc1   nfs                     4m55s

同樣顯示與pvc1是綁定狀態,default表示默認的namespace,此時刪除pvc1再查看pv狀態:

[root@server1 volumes]# kubectl delete -f pvc2.yaml 
persistentvolumeclaim "pvc1" deleted

[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           nfs                     5m53s

可以看出刪除pvc後,由於pv的回收策略是Recycle,因此pv現在又處於Available可用狀態。

4.再創建一個pv

[root@server1 volumes]# vim pv3.yaml 
[root@server1 volumes]# cat pv3.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  storageClassName: nfs
  nfs:
    server: 172.25.63.1
    path: /nfs
[root@server1 volumes]# kubectl create -f pv3.yaml 
persistentvolume/pv2 created
[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           nfs                     9m44s
pv2    1Gi        RWX            Delete           Available           nfs                     45s

注意我們設置的回收策略設置的是刪除,即刪除pvc時pv也會被刪除,大小設置的是1G.

5.再創建一個pvc

[root@server1 volumes]# vim pvc2.yaml 
[root@server1 volumes]# cat pvc2.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
[root@server1 volumes]# kubectl create -f pvc2.yaml 
persistentvolumeclaim/pvc1 created
[root@server1 volumes]# 
[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available                  nfs                     10m
pv2    1Gi        RWX            Delete           Bound       default/pvc1   nfs                     88s
[root@server1 volumes]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv2      1Gi        RWX            nfs            12s

可以看出pvc1的要求 pv2滿足,因此創建後就和pv2綁定。

然後現在刪除pvc1後查看pv2的狀態:

[root@server1 volumes]# kubectl delete -f pvc2.yaml 
persistentvolumeclaim "pvc1" deleted
[root@server1 volumes]# kubectl get pvc
No resources found in default namespace.
[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available                  nfs                     11m
pv2    1Gi        RWX            Delete           Failed      default/pvc1   nfs                     2m13s

可以看出pv2的狀態是faild,因爲nfs不支持刪除,因此顯示刪除失敗。

實驗完成後將pv2刪除:

[root@server1 volumes]# kubectl delete -f pv3.yaml 
persistentvolume "pv2" deleted

六、pod使用pvc示例

首先創建pv和pvc:

[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           nfs                     11m

[root@server1 volumes]# vim pvc2.yaml 
[root@server1 volumes]# cat pvc2.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
[root@server1 volumes]# kubectl create -f pvc2.yaml 
persistentvolumeclaim/pvc1 created
[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Bound    default/pvc1   nfs                     13m
[root@server1 volumes]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv1      500Mi      RWO            nfs            8s

之後創建pod:

[root@server1 volumes]# vim pod3.yaml
[root@server1 volumes]# cat pod3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpd
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: storage1
  volumes:
  - name: storage1
    nfs:
    persistentVolumeClaim:
      claimName: pvc1

上述部署文件表示將pvc1掛載到容器內的nginx發佈目錄,創建pod:

[root@server1 volumes]# kubectl create -f pod3.yaml 
pod/testpd created
[root@server1 volumes]# kubectl get pod
NAME      READY   STATUS        RESTARTS   AGE
testpd    1/1     Running       0          5s
[root@server1 volumes]# kubectl describe pod testpd 

在這裏插入圖片描述
現在我們進入容器內部寫入測試文件:

[root@server1 volumes]# kubectl exec -it testpd -- bash
root@testpd:/# cd /usr/share/nginx/html/
root@testpd:/usr/share/nginx/html# ls
root@testpd:/usr/share/nginx/html# echo redhat > index.html
root@testpd:/usr/share/nginx/html# cat index.html 
redhat
root@testpd:/usr/share/nginx/html# exit

然後測試訪問:

[root@server1 volumes]# kubectl get pod -o wide
NAME      READY   STATUS        RESTARTS   AGE    IP            NODE      NOMINATED NODE   READINESS GATES
testpd    1/1     Running       0          119s   10.244.2.67   server3   <none>           <none>
[root@server1 volumes]# curl 10.244.2.67
redhat

可以成功訪問,需要注意的是,在容器內寫入的數據會直接寫入到nfs服務器中:

[root@server1 volumes]# cat /nfs/index.html 
redhat

此時我們將pod刪除後再查看pvc的狀態:

[root@server1 volumes]# kubectl delete -f pod3.yaml 
pod "testpd" deleted
[root@server1 volumes]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv1      500Mi      RWO            nfs            7m52s

可以看出pvc依然存在,這個pvc也可以被其他pod掛載:

[root@server1 volumes]# vim pod3.yaml
[root@server1 volumes]# cat pod3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: storage1
  volumes:
  - name: storage1
    nfs:
    persistentVolumeClaim:
      claimName: pvc1
[root@server1 volumes]# kubectl create -f pod3.yaml 
pod/mypod created

[root@server1 volumes]# kubectl get pod -o wide
NAME      READY   STATUS        RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
mypod     1/1     Running       0          27s   10.244.2.68   server3   <none>           <none>

掛載後直接訪問其虛擬地址:

[root@server1 volumes]# curl 10.244.2.68
redhat

依然可以訪問到之前寫入的文件,這就是持久卷pv的作用。

但是當我們把pod 和 pvc全部刪除後,nfs服務器中的文件也就刪除了:

[root@server1 volumes]# kubectl delete -f pvc2.yaml 
persistentvolumeclaim "pvc1" deleted
[root@server1 volumes]# kubectl delete -f pod3.yaml 
pod "mypod" deleted
[root@server1 volumes]# ls /nfs/
[root@server1 volumes]# 

此時pv恢復成了Available可用狀態:

[root@server1 volumes]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    500Mi      RWO            Recycle          Available           nfs                     24m
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章