Kubernetes 項目引入了一組叫做 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 對象用於管理存儲卷。html
簡單的說PersistentVolume (PV) 是集羣中已由管理員配置的一段網絡存儲,是持久化存儲數據卷;Persistent Volume Claim(PVC)描述的,則是 Pod 所但願使用的持久化存儲的屬性,好比,Volume 存儲的大小、可讀寫權限等等。node
上面的這段文字說明可能過於模糊,下面舉個例子看看:nginx
咱們定義一個PVC,聲明須要的Volume屬性:web
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs spec: accessModes: - ReadWriteMany storageClassName: manual resources: requests: storage: 1Gi
yaml文件中定義了一個1 GiB的PVC,Access Modes表示須要的volume存儲類型,ReadWriteOnce表示只能在一個node節點上進行讀寫操做,其餘的Access Modes詳見:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes。shell
而後再定義一個PV:api
apiVersion: v1 kind: PersistentVolume metadata: name: nfs spec: storageClassName: manual capacity: storage: 1Gi accessModes: - ReadWriteMany nfs: server: 10.244.1.4 path: "/"
這個 PV 對象中會詳細定義存儲的類型是NFS,以及大小是1 GiB。網絡
PVC和PV至關於「接口」和「實現」,因此咱們須要將PVC和PV綁定起來纔可使用,而PVC和PV綁定的時候須要知足:frontend
作好PVC的聲明以後,並創建好PV,而後就可使用這個PVC了:分佈式
apiVersion: v1 kind: Pod metadata: labels: role: web-frontend spec: containers: - name: web image: nginx ports: - name: web containerPort: 80 volumeMounts: - name: nfs mountPath: "/usr/share/nginx/html" volumes: - name: nfs persistentVolumeClaim: claimName: nfs
在Pod中只須要聲明PVC的名字,等Pod建立後kubelet 就會把這個 PVC 所對應的 PV,也就是一個 NFS 類型的 Volume,掛載在這個 Pod 容器內的目錄上。ui
PersistentVolumeController會不斷地查看當前每個 PVC,是否是已經處於 Bound(已綁定)狀態。若是不是,那它就會遍歷全部的、可用的 PV,並嘗試將其與這個「單身」的 PVC 進行綁定。因此若是出現沒有PV能夠和PVC綁定,那麼Pod 的啓動就會報錯。
這個時候就須要用到StorageClass了,在上面咱們說的PV和PVC綁定的過程稱爲Static Provisioning,須要手動的建立PV;StorageClass還提供了Dynamic Provisioning機制,能夠根據模板建立PV。
StorageClass 對象會定義以下兩個部份內容:
這樣k8s就可以根據用戶提交的 PVC,找到一個對應的 StorageClass ,而後調用該 StorageClass 聲明的存儲插件,建立出須要的 PV。
例如聲明以下StorageClass:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: block-service provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd
這裏定義了名叫 block-service 的 StorageClass,provisioner 字段的值是:kubernetes.io/gce-pd,這是k8s內置的存儲插件,type字段也是跟着provisioner定義的,官方默認支持 Dynamic Provisioning 的內置存儲插件:https://kubernetes.io/docs/concepts/storage/storage-classes/。
而後就能夠在PVC中聲明storageClassName爲block-service,當建立好PVC 對象以後,k8s就會調用相應的存儲插件API建立一個PV對象。
以下:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: claim1 spec: accessModes: - ReadWriteOnce storageClassName: block-service resources: requests: storage: 30Gi
這種自動建立PV的機制就是Dynamic Provisioning,Kubernetes 就可以根據用戶提交的 PVC,找到一個對應的 StorageClass ,而後會調用StorageClass 聲明的存儲插件,建立出須要的 PV。
須要注意的是,若是沒有聲明StorageClassName在PVC中,PVC 的 storageClassName 的值就是"",這也意味着它只可以跟 storageClassName 也是""的 PV 進行綁定。
PV和PVC之間的相互做用遵循這個生命週期:
Provisioning --->Binding --->Using --->Reclaiming
k8s提供了兩種PV生成方式: statically or dynamically
statically:由管理員建立PV,它們攜帶可供集羣用戶使用的真實存儲的詳細信息。 它們存在於Kubernetes API中,可用於消費。
dynamically:當管理員建立的靜態PV都不匹配用戶的PersistentVolumeClaim時,集羣可能會嘗試爲PVC動態配置卷。 此配置基於StorageClasses,PVC必須請求一個StorageClasses,而且管理員必須已建立並配置該類才能進行動態配置。
由用戶建立好PersistentVolumeClaim 後,PersistentVolumeController會不斷地查看當前每個 PVC,是否是已經處於 Bound(已綁定)狀態。若是不是,那它就會遍歷全部的、可用的 PV,並嘗試將其與這個「單身」的 PVC 進行綁定。
Pods聲明並使用PVC做爲volume後,集羣會找到該PVC,若是該PVC已經綁定了PV,那麼會將該volume掛載到Pod中。
當用戶已經再也不使用該volume,能夠將該PVC刪除,以便讓資源得以回收。相應的在PVC刪除後,PV的回收策略能夠是Retained, Recycled, or Deleted,這個策略能夠在字段spec.persistentVolumeReclaimPolicy中設置。
通常的狀況下,咱們遵循這個刪除流程:
Local Persistent Volume適用於相似分佈式數據存儲好比 MongoDB、Cassandra等須要在多個不一樣節點上存儲數據,而且對I/O 較爲敏感的應用。可是相比於正常的 PV,一旦這些節點宕機且不能恢復時,Local Persistent Volume 的數據就可能丟失。
在咱們的實驗環境中,在宿主機上掛載幾個 RAM Disk(內存盤)來模擬本地磁盤。例如:
咱們在node1節點上掛載幾個磁盤
$ mkdir /mnt/disks $ for vol in vol1 vol2 vol3; do mkdir /mnt/disks/$vol mount -t tmpfs $vol /mnt/disks/$vol done
而後建立相應的PV:
apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 512Mi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/disks/vol1 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node1
這個 PV 的定義裏:local 字段指定了它是一個 Local Persistent Volume;而 path 字段,指定的正是這個 PV 對應的本地磁盤的路徑,即:/mnt/disks/vol1。而且用nodeAffinity指定這個PV必須運行在node1節點上。
運行上面的PV:
$ kubectl create -f local-pv.yaml persistentvolume/example-pv created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE example-pv 512Mi RWO Delete Available local-storage 16s
而後建立一個StorageClass 來描述這個 PV:
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer
這個 StorageClass叫local-storage,provisioner爲no-provisioner表示不須要自動建立PV。
volumeBindingMode=WaitForFirstConsumer表示須要等到Pod運行以後才讓PVC和PV綁定。由於在使用Local Persistent Volume的時候PV和對應的PVC必需要跟隨Pod在同一node下面,不然會調度失敗。
而後咱們運行StorageClass:
$ kubectl create -f local-sc.yaml storageclass.storage.k8s.io/local-storage created
再建立一個PVC:
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: example-local-claim spec: accessModes: - ReadWriteOnce resources: requests: storage: 512Mi storageClassName: local-storage
這裏注意聲明storageClassName須要是咱們上面建立的StorageClass。
而後建立PVC:
$ kubectl create -f local-pvc.yaml persistentvolumeclaim/example-local-claim created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE example-local-claim Pending local-storage 7s
這個時候由於還沒建立Pod,因此狀態仍是Pending。
建立一個pod:
kind: Pod apiVersion: v1 metadata: name: example-pv-pod spec: volumes: - name: example-pv-storage persistentVolumeClaim: claimName: example-local-claim containers: - name: example-pv-container image: nginx ports: - containerPort: 80 name: "http-server" volumeMounts: - mountPath: "/usr/share/nginx/html" name: example-pv-storage
而後咱們建立pod後再看看PVC綁定狀態:
$ kubectl create -f local-pod.yaml pod/example-pv-pod created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE example-local-claim Bound example-pv 512Mi RWO local-storage 6h
而後咱們試着寫入一個文件到/usr/share/nginx/html中:
$ kubectl exec -it example-pv-pod -- /bin/sh # cd /usr/share/nginx/html # touch test.txt # 在node1上 $ ls /mnt/disks/vol1 test.txt