PV的全稱是: PersistentVolume (持久化卷),是對底層的共享存儲的一種抽象,PV由管理員進行建立和配置,它和具體的底層的共享存儲技術的實現方式有關,好比Ceph、GlusterFS、NFS等,都是經過插件機制完成與共享存儲的對接.html
PVC的全稱是: PersistenVolumeClaim (持久化卷聲明),PVC是用戶存儲的一種聲明,PVC和Pod比較類型,Pod是消耗節點,PVC消耗的是PV資源,Pod能夠請求CPU的內存,而PVC能夠請求特定的存儲空間和訪問模式。對於真正存儲的用戶不須要關心底層的存儲實現細節,只須要直接使用PVC便可.node
可是經過PVC請求必定的存儲空間也頗有可能不足以知足對於存儲設備的各類需求,並且不一樣的應用程序對於存儲性能的要求也能也不盡相同,好比讀寫速度、併發性能等,爲了解決這一問題,Kubernetes又爲咱們引入了一個新的資源對象: StorageClass,經過StorageClass的定義,管理員能夠將存儲資源定義爲某種類型的資源,好比快速存儲、慢速存儲等,用戶根據StorageClass的描述就能夠很是直觀的知道各類存儲資源特性了,這樣就能夠根據應用的特性去申請合適的存儲資源了.python
PV能夠看做可用的存儲資源,PVC則是對存儲資源的需求,PV和PVC的互相關係遵循以下圖nginx
Kubernetes支持兩種資源的供應模式:靜態模式(Staic)和動態模式(Dynamic)。資源供應的結果就是建立好的PV.git
靜態模式: 管理員手工建立許多PV,在定義PV時須要將後端存儲的特性進行設置vim
動態模式: 管理員無需手動建立PV,而是經過StorageClass的設置對後端存儲進行描述,標記爲某種"類型(Class)",此時要求PVC對存儲的類型進行聲明,系統將自動完成PV的建立及PVC的綁定,PVC能夠聲明爲Class爲"",說明該PVC禁止使用動態模式後端
在用戶定義好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的綁定api
Pod 使用volume的定義,將PVC掛載到容器內的某個路徑進行使用。volume的類型爲persistentVoulumeClaim,在容器應用掛載了一個PVC後,就能被持續獨佔使用。不過,多個Pod能夠掛載同一個PVC,應用程序須要考慮多個實例共同訪問一塊存儲空間的問題服務器
當用戶對存儲資源使用哪一個完畢後,用戶能夠刪除PVC,與該PVC綁定的PV將會被標記爲已釋放,但還不能馬上與其餘PVC進行綁定。經過以前PVC寫入的數據可能還留在存儲設備上,只有在清除以後該PV才能繼續使用.網絡
對於PV,管理員能夠設定回收策略(Reclaim Policy)用於設置與之綁定的PVC釋放資源以後,對於遺留數據如何處理。只有PV的存儲空間完成回收,才能供新的PVC綁定和使用。
1 . 靜態資源下,經過PV和PVC完成綁定,並供Pod使用的存儲管理機制
2 . 動態資源下,經過StorageClass和PVC完成資源動態綁定(系統自動生成PV,並供Pod使用的存儲管理機制
因爲容器自己是非持久化的,所以須要解決在容器中運行應用程序遇到的一些問題。首先,當容器崩潰時,kubelet將從新啓動容器,可是寫入容器的文件將會丟失,容器將會以鏡像的初始狀態從新開始;第二,在經過一個Pod中一塊兒運行的容器,一般須要共享容器之間一些文件。Kubernetes經過存儲卷解決上述的兩個問題。
在Docker有存儲卷的概念卷,但Docker中存儲卷只是磁盤的或另外一個容器中的目錄,並無對其生命週期進行管理。Kubernetes的存儲卷有本身的生命週期,它的生命週期與使用的它Pod生命週期一致。所以,相比於在Pod中運行的容器來講,存儲卷的存在時間會比的其中的任何容器都長,而且在容器從新啓動時會保留數據。固然,當Pod中止存在時,存儲卷也將再也不存在。在Kubernetes支持多種類型的卷,而Pod能夠同時使用各類類型和任意數量的存儲卷。在Pod中經過指定下面的字段來使用存儲卷:
spec.volumes: 經過此字段提供指定的存儲卷
spec.containers.volumeMounts: 經過此字段將存儲卷掛載到容器中
當前Kubernetes支持以下所列的存儲卷類型,並以hostPath、nfs和persistentVolumeClaim類型的存儲卷爲例,介紹如何定義存儲卷,以及如何在Pod中被使用.
* awsElasticBlockStore * azureDisk * azureFile * cephfs * configMap * csi * downwardAPI * emptyDir * fc (fibre channel) * flocker * gcePersistentDisk * gitRepo * glusterfs * hostPath * iscsi * local * nfs * persistentVolumeClaim * projected * portworxVolume * quobyte * rbd * scaleIO * secret * storageos * vsphereVolume
EmptyDir是一個空目錄,他的生命週期和所屬的Pod是徹底一致的,他用處是把同一Pod內的不一樣容器之間共享工做過程產生的文件,
mkdir /storage cd /storage mkdir volumes cat pod_volume.demo1.yaml apiVersion: v1 kind: Pod metadata: name: pod-demo namespace: default labels: app: myapp tier: frontend annotations: youmen.com/created-by: "youmen admin" spec: containers: - name: myapp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ - name: busybox image: busybox:latest imagePullPolicy: IfNotPresent volumeMounts: - name: html mountPath: /data/ command: ['/bin/sh', '-c'] args: - 'while true; do echo $(date) >> /data/index.html; sleep 3; done' volumes: - name: html emptyDir: {} kubectl apply -f pod_volume.demo1.yaml curl 10.244.3.34 -s Tue Dec 24 15:37:09 UTC 2019 Tue Dec 24 15:37:12 UTC 2019 Tue Dec 24 15:37:15 UTC 2019 Tue Dec 24 15:37:18 UTC 2019 Tue Dec 24 15:37:21 UTC 2019 Tue Dec 24 15:37:24 UTC 2019
依賴於node,這種會把宿主機的指定卷加載到容器之中,實現數據持久,可是若是Pod發生跨主機的重建,內容很難保證,或者Node節點宕機了
這種卷通常和DaemonSet搭配使用,用來操做主機文件,例如進行日誌採集的FLK的FluentD就採用這種方式,加載主機的容器日誌目錄,達到收集本機全部日誌的目的
# hostPath # 將Pod裏面的目錄內容綁定到宿主機目錄,Pod刪除並不到致使宿主機刪除. # 若是指定宿主機目錄不存在要不要先建立取決於type mkdir /data/pod/volume1 -p cat pod-volhost.demo.yaml apiVersion: v1 kind: Pod metadata: name: pod-vol-hostpath namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ volumes: - name: html hostPath: path: /data/pod/volume1 type: DirectoryOrCreate kubectl apply -f pod-volhost-demo.yaml echo youmen >> /data/pod/volume1/index.html kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-vol-hostpath 1/1 Running 0 3m49s 10.244.3.35 node1 <none> <none> curl 10.244.3.35 youmen # 咱們刪除Pod,能夠到Node主機查看目錄,依然存在. [root@node1 ~]# cat /data/pod/volume1/index.html youmen
# 建議找集羣外的一臺機器作NFS hostnamectl set-hostname stor01 echo 172.19.0.18 stor01 >> /etc/hosts yum -y install nfs-utils mkdir -pv /data/volumes -p chmod 755 /data/volumes vim /etc/exports /data/volumes *(rw,no_root_squash,sync) # 存儲目錄,*容許全部人鏈接,rw讀寫權限,sync文件同時寫入硬盤及內存,no_root_squash 使用者root用戶自動修改成普通用戶 systemctl start nfs && systemctl enable nfs
# 查看nfs目錄掛載權限 cat /var/lib/nfs/etab /data/volumes *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,rw,secure,no_root_squash,no_all_squash) # 檢查下掛載目錄是否正常 showmount -e localhost Export list for localhost: /data/volumes *
# 切換到k8s集羣內的節點掛載存儲卷 yum -y install nfs-utils echo 172.19.0.18 stor01 >> /etc/hosts # 掛載測試如下,而後umount掉,待會建立Pod自動會掛載 mount -t nfs stor01:/data/volumes /mnt # 驗證一下 df -h |grep nfs1 nfs1:/data/volumes 17G 1.4G 16G 9% /mnt cat pod-volnfs.demo.yaml apiVersion: v1 kind: Pod metadata: name: pod-volnfs-demo namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ volumes: - name: html nfs: path: /data/volumes server: stor01 kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-volnfs-demo 1/1 Running 0 20s 10.244.1.49 node2 <none> <none> curl 10.244.1.49 nfs # 能夠刪除Pod再建立數據依然存在,哪怕節點宕掉依然數據不會丟失,可是當掛載的數據,Pod多了,效果就不盡人意了 # 接下來咱們經過pv,pvc來使用這個nfs存儲
Pv和Pvc是K8s的一種標準資源,Pvc被Pv調用後就會被綁定起來,取決於用戶怎麼綁,
由於pvc屬於集羣資源級別的不能定義在名成空間
# 這裏使用nfs類型後端存儲, 1g存儲空間,訪問模式爲ReadWriteOnce,回收策略爲Recyle cat nfs_demo1.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv1 #pv名稱 spec: capacity: #存儲能力,一個pv對象都要指定一個存儲能力, 目前僅支持存儲空間的設置 storage: 1Gi #存儲空間 accessModes: - ReadWriteOnce #訪問模式 persistentVolumeReclaimPolicy: Recycle #回收策略 nfs: #服務模式 (nfs、ceph、hostpath等) path: /data/volumes #共享數據目錄掛載點 server: nfs1 #nfs服務器地址 kubectl apply -f nfs_demo1.yaml persistentvolume/pv1 created [root@master storage]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWO Recycle Available 5s # pv1: 名稱 # 1Gi: 存儲空間大小 # RWO: 訪問模式(ReadWriteOnce縮寫) # Recycle: 回收策略 # Available: PV狀態 # persistentVolumeReclaimPolicy回收策略 # Retain (保留) 保留數據,須要管理員手動清理 # Recycle (回收) 清除PV中的數據,效果至關於執行刪除命令 # Delete (刪除) 與PV相連的後端存儲完成volume的刪除操做,常見於雲服務商的存儲服務 # 不過須要注意的是,目前只有NFS和HostPath兩類支持回收策略,通常設置Retain比較保險 # 狀態有如下幾種 # Available: PV狀態,表示可用狀態,還未被任何PVC綁定 # Bound: 已綁定,已經綁定到某個PVC # Released: 已釋放,對應的pvc已經刪除,但資源尚未被集羣收回 # Failed: PV自動回收失敗
Capacity 存儲能力
經過PV的capacity屬性來設置存儲空間,目前僅支持storage=數據大小,將來可能會加入IOPS、吞吐量等指標配置
AccessModes訪問模式
AccessModes 是用來對PV進行訪問模式的設置,用於描述用戶應用對存儲資源的訪問權限
ReadWriteOnce (RWO):讀寫權限,可是隻能被單個節點掛載
ReadOnlyMany (ROX):只讀權限,可能被多個節點掛載
ReadWriteMany (RWX):讀寫權限,能夠被多個節點掛載
注意: 一些pv可能支持多種訪問模式,但掛載時候只可使用一種訪問模式,多種訪問模式不奏效
PV實際上沒有存儲,至關於咱們node同樣,還須要建立Pod進行消費,接下來咱們進行PVC的建立與配置
# 建立一個數據卷聲明 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi kubectl apply -f pvc-nfs-demo1.yaml kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs Bound pv1 1Gi RWO kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv1 1Gi RWO Recycle Bound default/pvc-nfs 10m # 當咱們建立pvc以後,pv的狀態變成了Bound綁定狀態,而且和pvc的狀態相同。 # 而且能夠看到pvc已經綁定到名稱爲pv1的volume上, # 同時在pv上能夠看到綁定到名稱爲pvc-nfs的pvc中
[root@master storage]# kubectl describe pv pv1 Name: pv1 Labels: <none> Annotations: pv.kubernetes.io/bound-by-controller: yes Finalizers: [kubernetes.io/pv-protection] StorageClass: Status: Bound Claim: default/pvc-nfs Reclaim Policy: Recycle Access Modes: RWO VolumeMode: Filesystem Capacity: 1Gi Node Affinity: <none> Message: Source: Type: NFS (an NFS mount that lasts the lifetime of a pod) Server: nfs1 Path: /data/volumes ReadOnly: false Events: <none> [root@master storage]# kubectl describe pvc pvc-nfs Name: pvc-nfs Namespace: default StorageClass: Status: Bound Volume: pv1 Labels: <none> Annotations: pv.kubernetes.io/bind-completed: yes pv.kubernetes.io/bound-by-controller: yes Finalizers: [kubernetes.io/pvc-protection] Capacity: 1Gi Access Modes: RWO VolumeMode: Filesystem Mounted By: <none> Events: <none> # 在Kubernetes中會自動幫咱們查看pv狀態爲Available而且根據聲明pvc容量storage的大小進行篩選匹配, # 同時還會根據AccessMode進行匹配。若是pvc匹配不到pv會一直處於pending狀態。
同時,pv與pvc中間還能夠經過label標籤進行匹配,配置以下
# 記得下面配置文件修改一下名字,不能夠重複 apiVersion: v1 kind: PersistentVolume metadata: name: pv2 labels: #這裏將pv設置一個labels app: nfs spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: /data/volumes server: nfs1 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc2-nfs spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi selector: # pvc匹配標籤爲app=nfs的pv matchLabels: app: nfs [root@master storage]# kubectl apply -f nfs_demo2.yaml [root@master storage]# kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pv1 1Gi RWO Recycle Bound default/pvc-nfs 28m persistentvolume/pv2 1Gi RWO Recycle Bound default/pvc2-nfs 94s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/pvc-nfs Bound pv1 1Gi RWO 17m persistentvolumeclaim/pvc2-nfs Bound pv2 1Gi RWO 94s # 有一點須要注意,當咱們pvc申請的容量小於咱們pv的容量是能夠進行綁定的 # 當咱們申請pvc的容量大於pv的容量是沒法進行綁定的。 這裏須要注意
[root@master storage]# cat deployment-nfs1.yaml apiVersion: apps/v1 kind: Deployment metadata: name: pv-nfs-nginx spec: replicas: 3 selector: matchLabels: app: pv-nfs-nginx template: metadata: labels: app: pv-nfs-nginx spec: containers: - name: pv-nfs-nginx image: nginx ports: - containerPort: 80 volumeMounts: #掛載,首先添加須要掛載的目錄 - name: pv-nginx #掛載點的名稱 mountPath: /usr/share/nginx/html #掛載點的路徑 volumes: #綁定 - name: pv-nginx persistentVolumeClaim: #將鏡像中的nginx目錄掛載到下面名稱的pvc中 claimName: pvc-nfs #pvc名稱 --- apiVersion: v1 kind: Service metadata: name: nfs-pvc labels: app: pv-nfs-nginx spec: type: NodePort ports: - port: 80 targetPort: 80 selector: app: pv-nfs-nginx [root@master storage]# kubectl apply -f deployment-nfs1.yaml
[root@master storage]# kubectl get pods,svc |grep pv pod/pv-nfs-nginx-6b4759b5b8-95vhk 1/1 Running 0 74s pod/pv-nfs-nginx-6b4759b5b8-mjwhn 1/1 Running 0 58s pod/pv-nfs-nginx-6b4759b5b8-p4jhn 1/1 Running 0 11s service/nfs-pvc NodePort 10.0.0.57 <none> 80:31850/TCP 4m23s # 這裏咱們能夠看到pod已經正常啓動,而且svc也已經暴露端口了。 # 若是須要更改顯示內容,更改以前nfs存儲裏面的那個目錄文件就能夠了
因爲咱們的index.html直接掛在到了/data1/k8s目錄下面,若是有不少個pod都使用pvc進行掛載,會形成咱們數據目錄的文件比較亂
這裏咱們添加一個
subpath
subPath的目的是爲了在單一Pod中屢次使用同一個volume而設計的。
[root@master storage]# cat deployment-nfs1.yaml apiVersion: apps/v1 kind: Deployment metadata: name: pv-nfs-nginx spec: replicas: 3 selector: matchLabels: app: pv-nfs-nginx template: metadata: labels: app: pv-nfs-nginx spec: containers: - name: pv-nfs-nginx image: nginx ports: - containerPort: 80 volumeMounts: #掛載,首先添加須要掛載的目錄 - name: pv-nginx #掛載點的名稱 mountPath: /usr/share/nginx/html #掛載點的路徑 subPath: nginx-pvc volumes: #綁定 - name: pv-nginx persistentVolumeClaim: #將鏡像中的nginx目錄掛載到下面名稱的pvc中 claimName: pvc-nfs #pvc名稱 --- apiVersion: v1 kind: Service metadata: name: nfs-pvc labels: app: pv-nfs-nginx spec: type: NodePort ports: - port: 80 targetPort: 80 selector: app: pv-nfs-nginx [root@master storage]# kubectl apply -f deployment-nfs1.yaml # 當咱們更新完pod以後,等pod正常啓動。 # 就能夠看到在咱們nfs存儲目錄下面單首創建了一個名稱爲nginx-pvc的目錄, # 這個目錄實際上就是咱們subpath後面指定的名稱 [root@nfs1 ~]# ls /data/volumes/ index.html nginx-pvc [root@nfs1 ~]# ls /data/volumes/nginx-pvc/ [root@nfs1 ~]# mv /data/volumes/index.html /data/volumes/nginx-pvc/ # 這個目錄下面也是沒有任何文件的,咱們須要將原來index.html拷貝過去便可 # 如今咱們刪除deployment,下面的數據並不會刪除。這樣使用pv和pvc持久化就完成 # 若是咱們直接刪除或者有pod在使用pv或者pvc是沒法直接刪除的, # 當咱們使用Recycle模式時,刪除全部pv和pvc後, # 數據也會進行刪除。因此刪除pv和pvc請謹慎操做 [root@nfs1 ~]# cat /data/volumes/nginx-pvc/index.html Pvc Persistence
NFS存儲的缺點
不支持動態建立持久卷,只能手工建立
先手工建立PV,再經過PV手工建立PVC,PVC就是真正可用的持久卷PVC是和PV進行綁定的: PVC會根據本身需求空間的大小自動選擇合適的PV,好比須要一個5G的PVC,PV分別爲2G,7G和10G,那麼PVC會自動選擇7G的,可是剩餘的空間不是浪費了麼?緣由以下: 一個被綁定的PV只能用於一個PVC,他們是一對一綁定的,若是PVC大小隻須要5G,可是所選的PV有7G,那麼剩餘的2G是沒辦法使用的,若是不想這樣浪費空間只能使用動態建立的方式.