在一切虛擬化解決方案中,數據的持久化都是須要咱們很是關心的問題,docker如此,K8s也不例外。在k8s中,有一個數據卷的概念。node
*k8s數據卷主要解決了如下兩方面問題:* * 數據持久性:一般狀況下,容器運行起來後,寫入到其文件系統的文件時暫時性的。當容器崩潰後,kebelet 將這個容器kill掉,而後生成一個新的容器,此時,新運行的容器將沒有原來容器內的文件,由於容器是從新從鏡像建立的。 * 數據共享:同一個pod中運行的容器之間,常常會存在共享文件/文件夾的需求。
在k8s中,Volume(數據卷)存在明確的生命週期(與包含該數據卷的容器組(pod)相同)。所以Volume的生命週期比同一容器組(pod)中任意容器的生命週期要更長,無論容器重啓了多少次,數據都被保留下來。固然,若是pod不存在了,數據卷天然退出了。此時,根據pod所使用的數據卷類型不一樣,數據可能隨着數據卷的退出而刪除,也可能被真正持久化,並在下次容器組重啓時仍然可使用。web
從根本上來講,一個數據卷僅僅是一個能夠被pod訪問的目錄或文件。這個目錄是怎麼來的,取決於該數據卷的類型(不一樣類型的數據卷使用不一樣的存儲介質)。同一個pod中的兩個容器能夠將一個數據卷掛載到不一樣的目錄下。算法
k8s目前支持28種數據卷類型(其中大多數特定於雲環境),這裏將寫下在k8s中經常使用的幾種數據卷類型以下:docker
一、emptyDir
emptyDir類型的數據卷在建立pod時分配給該pod,而且直到pod被移除,該數據卷才被釋放。該數據卷初始分配時,始終是一個空目錄。同一個pod中的不一樣容器均可以對該目錄執行讀寫操做,而且共享其中的數據(儘管不一樣容器可能將該數據卷掛載到容器中的不一樣路徑)。當pod被刪除後,emptyDir數據卷中的數據將被永久刪除。(PS:容器奔潰時,kubelet並不會刪除pod,而僅僅是將容器重啓,所以emptyDir中的數據在容器崩潰並重啓後,仍然是存在的)。vim
emptyDir的使用場景以下: * 空白的初始空間,例如合併/排序算法中,臨時將數據保存在磁盤上。 * 長時間計算中存儲檢查點(中間結果),以便容器崩潰時,能夠從上一次存儲的檢查點(中間結果)繼續進行,而不是從頭開始。 * 做爲兩個容器的共享存儲,使得第一個內容管理的容器能夠將生成的數據存入其中,同時由一個webserver容器對外提供這些頁面。 * 默認狀況下,emptyDir數據卷存儲在node節點的存儲介質(機械硬盤、SSD或網絡存儲)上。
emptyDir的使用示例api
[root@master yaml]# vim emptyDir.yaml apiVersion: v1 kind: Pod metadata: name: producer-consumer //定義Pod的名稱 spec: containers: - image: busybox name: producer //定義容器的名稱 volumeMounts: - mountPath: /producer_dir //指定容器內的路徑 name: shared-volume //表示把shared-volume掛載到容器中 args: //當容器運行完成後,執行如下的寫操做 - /bin/sh - -c - echo "hello k8s" > /producer_dir/hello; sleep 30000 - image: busybox name: consumer //定義容器的名稱 volumeMounts: - mountPath: /consumer_dir name: shared-volume //與上一個容器同樣 args: - /bin/sh - -c - cat /consumer_dir/hello; sleep 30000 volumes: - name: shared-volume //定義數據卷的名稱,必須與以上掛載的數據卷名稱一致 emptyDir: {} //定義一個類型爲emptyDir的數據卷,名稱爲shared-volume [root@master yaml]# kubectl apply -f emptyDir.yam //生成所需的Pod資源 [root@master yaml]# kubectl exec -it producer-consumer -c producer /bin/sh //進入第一個容器進行驗證 / # cat /producer_dir/hello hello k8s [root@master yaml]# kubectl exec -it producer-consumer -c consumer /bin/sh //進行第二個容器進行驗證 / # cat /consumer_dir/hello hello k8s
注:到此能夠看出這個pod中的兩個容器指定的目錄內容都是同樣的,具體是本地的那個目錄還需進一步進行驗證。服務器
[root@master yaml]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES producer-consumer 2/2 Running 0 7m58s 10.244.2.2 node01 <none> <none> //能夠看出這個pod是運行在node01上的
node01: [root@node01 ~]# docker ps | grep busybox //經過此命令查看容器的ID號 60806212f198 busybox "/bin/sh -c 'cat /co…" 7 minutes ago Up 7 minutes k8s_consumer_producer-consumer_default_d4c5ba5c-d16e-4549-8456-94575c511b96_0 918e6ca1b218 busybox "/bin/sh -c 'echo \"h…" 7 minutes ago Up 7 minutes k8s_producer_producer-consumer_default_d4c5ba5c-d16e-4549-8456-94575c511b96_0 [root@node01 ~]# docker inspect 60806212f198 //查看第一個容器的詳細信息
[root@node01 ~]# docker inspect 918e6ca1b218 //查看第二個容器的詳細信息
//從以上內容能夠看出這兩個容器的源目錄是同樣,掛載的是docker host本地的同一個目錄 [root@node01 ~]# cd /var/lib/kubelet/pods/d4c5ba5c-d16e-4549-8456-94575c511b96/volumes/kubernetes.io~empty-dir/shared-volume [root@node01 shared-volume]# ls hello [root@node01 shared-volume]# cat hello hello k8s //查看本地該目錄下的內容,和pod中的一致
至此,emptyDir的特性就已經驗證了,只要這個pod中還有一個容器在運行,那麼這個本地的數據就不會丟失,但若是這個pod被刪除,那麼本地的數據也將不復存在。
驗證以下:網絡
node01上刪除一個pod並再次查看本地目錄: [root@node01 ~]# docker rm -f 60806212f198 //刪除一個pod 60806212f198 [root@node01 ~]# cd /var/lib/kubelet/pods/d4c5ba5c-d16e-4549-8456-94575c511b96/volumes/kubernetes.io~empty-dir/shared-volume [root@node01 shared-volume]# cat hello hello k8s //查看本地目錄,發現文件還在
master上將此pod刪除,再次去node01節點上查看本地目錄是否存在: [root@master ~]# kubectl delete -f emtydir.yaml //master上刪除pod //在node01上再次查看本地目錄,會提示不存在這個目錄 [root@node01 ~]# cat /var/lib/kubelet/pods/d4c5ba5c-d16e-4549-8456-94575c511b96/volumes/kubernetes.io~empty-dir/shared-volume cat: /var/lib/kubelet/pods/d4c5ba5c-d16e-4549-8456-94575c511b96/volumes/kubernetes.io~empty-dir/shared-volume: 沒有那個文件或目錄
emptyDir總結:app
同個pod裏面的不一樣容器,共享同一個持久化目錄,當pod節點刪除時,volume的內容也會被刪除。但若是僅僅是容器被銷燬,pod還在,則volume不會受到任何影響。說白了,emptyDir的數據持久化的生命週期和使用的pod一致。通常是做爲臨時存儲使用。ide
二、HostPath數據卷類型
hostPath 類型的數據卷將 Pod(容器組)所在節點的文件系統上某一個文件或目錄掛載進容器組(容器內部),相似於docker中的bind mount掛載方式。
這種數據持久化的方式,使用場景很少,由於它增長了pod與節點之間的耦合。
絕大多數容器組並不須要使用 hostPath 數據卷,可是少數狀況下,hostPath 數據卷很是有用:
適用場景以下: * 某容器須要訪問 Docker,可以使用 hostPath 掛載宿主節點的 /var/lib/docker * 在容器中運行 cAdvisor,使用 hostPath 掛載宿主節點的 /sys * 總言而之,通常對K8s集羣自己的數據持久化和docker自己的數據持久化會使用這種方式。
注:因爲其使用場景比較少,這裏就不舉例了。
三、Persistent 數據卷類型
PersistentVolume(PV存儲卷)是集羣中的一塊存儲空間,由集羣管理員管理或者由Storage class(存儲類)自動管理,PV和pod、deployment、Service同樣,都是一個資源對象。
既然有了PV這個概念,那麼PVC(PersistentVolumeClaim)這個概念也不得不說一下,PVC表明用戶使用存儲的請求,應用申請PV持久化空間的一個申請、聲明。K8s集羣可能會有多個PV,你須要不停的爲不一樣的應用建立多個PV。
好比說,pod是消耗node節點的計算資源,而PVC存儲卷聲明是消耗PV的存儲資源。Pod能夠請求的是特定數量的計算資源(CPU或內存等),而PVC請求的是特定大小或特定訪問模式(只能被單節點讀寫/可被多節點只讀/可被多節點讀寫)的存儲資源。
PV(存儲卷)和PVC(存儲卷聲明)的關係以下圖所示:
上圖中的解釋以下: * PV是集羣中的存儲資源,一般由集羣管理員建立和管理; * StorageClass用於對PV進行分類,若是配置正確,Storage也能夠根據PVC的請求動態建立PV; * PVC是使用該資源的請求,一般由應用程序提出請求,並指定對應的StorageClass和需求的空間大小; * PVC能夠做爲數據卷的一種,被掛載到pod中使用。
PV和PVC的管理過程描述以下: 一、在主機上劃分出一個單獨的目錄用於PV使用,而且定義其可用大小 二、建立PVC這個資源對象,以便請求PV的存儲空間 三、pod中添加數據卷,數據卷關聯到PVC; 四、Pod中包含容器,容器掛載數據卷
其實上面解釋那麼多,可能仍是雲裏霧裏的,下面是一個使用案例,僅供參考。
案例大概過程以下:
底層存儲採用nfs存儲,而後在nfs的目錄下劃分1G的容量供PV調度。而後經過建立PVC來申請PV的存儲資源空間,最後建立pod測試,使用PVC聲明的存儲資源來實現數據的持久化。
1)搭建nfs存儲
爲了方便操做,我直接在master上搭建nfs存儲。
[root@master ~]# yum -y install nfs-utils rpcbind [root@master ~]# vim /etc/exports /nfsdata *(rw,sync,no_root_squash) [root@master ~]# systemctl start nfs-server [root@master ~]# systemctl start rpcbind [root@master ~]# showmount -e Export list for master: /nfsdata *
2)建立PV資源對象
[root@master ~]# vim test-pv.yaml //編輯PV的yaml文件 apiVersion: v1 kind: PersistentVolume metadata: name: test-pv spec: capacity: storage: 1Gi //該PV可分配的容量爲1G accessModes: - ReadWriteOnce //訪問模式爲只能以讀寫的方式掛載到單個節點 persistentVolumeReclaimPolicy: Recycle //回收策略爲Recycle storageClassName: nfs //定義存儲類名字 nfs: //這裏和上面定義的存儲類名字須要一致 path: /nfsdata/test-pv //指定nfs的目錄 server: 192.168.45.129 //nfs服務器的IP #關於上述的具體解釋 //capacity:指定PV的大小 //AccessModes:指定訪問模式 //ReadWriteOnce:只能以讀寫的方式掛載到單個節點(單個節點意味着只能被單個PVC聲明使用) //ReadOnlyMany:能以只讀的方式掛載到多個節點 //ReadWriteMany:能以讀寫的方式掛載到多個節點 //persistentVolumeReclaimPolicy:PV的回收策略 //Recycle:清除PV中的數據,而後自動回收。 //Retain:須要手動回收。 //Delete:刪除雲存儲資源。(雲存儲專用) //PS:注意這裏的回收策略是指,在PV被刪除後,在這個PV下所存儲的源文件是否刪除。 //storageClassName:PV和PVC關聯的依據。 [root@master ~]# kubectl get pv test-pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE test-pv 1Gi RWO Recycle Available nfs 43s //查看PV的狀態必須爲Available才能夠正常使用
3)建立PVC資源對象
[root@master ~]# vim test-pvc.yaml //編寫yaml文件 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-pvc spec: accessModes: //定義訪問模式,必須和PV定義的訪問模式一致 - ReadWriteOnce resources: requests: storage: 1Gi //直接請求使用最大的容量 storageClassName: nfs //這裏的名字必須和PV定義的名字一致 [root@master ~]# kubectl apply -f test-pvc.yaml //執行yaml文件 [root@master ~]# kubectl get pv,pvc //再次查看PV及PVC的狀態(狀態爲bound,表示該PV正在被使用) //下列表示pv和pvc已經關聯上了 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/test-pv 1Gi RWO Recycle Bound default/test-pvc nfs 2m33s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/test-pvc Bound test-pv 1Gi RWO nfs 24s
注:上述yaml文件中,主要字段的解釋:
1)accessModes(訪問模式) * ReadWriteOnce:以讀寫的方式掛載到單個節點; * ReadWriteMany:以讀寫的方式掛載到多個節點 ; * ReadOnlyMany:以只讀的方式掛載到多個節點;
2)persistentVolumeReclaimPolicy(PV的回收策略) * Recycle:自動清除PV中的數據,自動回收; * Retain:須要手動回收; * Delete:刪除雲存儲資源(雲存儲專用);
4)建立一個Pod
這裏建立的pod使用剛剛建立的PV來實現數據的持久化。
[root@master ~]# vim test-pod.yaml #編寫pod的yaml文件 apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-pod image: busybox args: - /bin/sh - -c - sleep 30000 volumeMounts: - mountPath: /testdata name: volumedata #這裏自定義個名稱 volumes: - name: volumedata #這裏的是上面定義的名稱解釋,這兩個名稱必須一致 persistentVolumeClaim: claimName: test-pvc [root@master ~]# kubectl apply -f test-pod.yaml #執行yaml文件 [root@master ~]# kubectl get pod //查看pod的狀態,發現其一直處於ContainerCreating狀態,可是查看時間,這麼長時間沒有建立好,不太正常 NAME READY STATUS RESTARTS AGE test-pod 0/1 ContainerCreating 0 23s
排錯方法以下: * 當遇到pod狀態不正常時,通常咱們能夠採用三種方式來排錯 * 第一就是使用kubectl describe命令來查看pod的詳細信息 * 第二就是使用kubectl logs命令來查看pod的日誌 * 第三就是查看宿主機本機的message日誌
這裏我採用第一種方法排錯!!!
[root@master ~]# kubectl describe pod test-pod //根據提示最後的一條的信息以下: mount.nfs: mounting 192.168.45.129:/nfsdata/test-pv failed, reason given by server: No such file or directory //原來是咱們在掛載nfs存儲目錄時,指定的目錄並不存在 //那就在nfs服務器上(這裏是本機)進行建立相關目錄咯 [root@master ~]# mkdir -p /nfsdata/test-pv //建立對應目錄 [root@master ~]# kubectl get pod test-pod //而後再次查看pod的狀態 NAME READY STATUS RESTARTS AGE test-pod 1/1 Running 0 8m59s //發現已成功啓動了
5)測試其數據持久化的效果
[root@master ~]# kubectl exec -it test-pod /bin/sh //進入pod / # echo "hello pv,pvc" >/testdata/test.txt //數據持久化的目錄寫入測試信息 //到nfs服務器,查看共享的目錄下是否有容器中寫入的信息 [root@master ~]# cat /nfsdata/test-pv/test.txt hello pv,pvc [root@master ~]# kubectl get pod test-pod -o wide //查看容器運行在node02節點上了 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pod 1/1 Running 0 14m 10.244.1.2 node02 <none> <none> node02節點上: [root@node02 ~]# docker ps //查看容器ID [root@node02 ~]# docker rm -f 2af647789525 //刪除剛剛建立的容器 master節點上: [root@master ~]# cat /nfsdata/test-pv/test.txt hello pv,pvc //那麼如今測試,將這個pod刪除,nfs本地的數據是否還在? [root@master ~]# kubectl delete -f test-pod.yaml [root@master ~]# cat /nfsdata/test-pv/test.txt //發現數據還在 test pv pvc //那如今要是將PVC刪除呢? [root@master ~]# kubectl delete -f test-pvc.yaml [root@master ~]# cat /nfsdata/test-pv/test.txt //發現數據不存在了 cat: /nfsdata/test-pv/test.txt: 沒有那個文件或目錄
總結:因爲咱們在建立pv這個資源對象時,採用的回收策略是清除PV中的數據,而後自動回收,而PV這個資源對象是由PVC來申請使用的,因此不論是容器也好,pod也好,它們的銷燬並不會影響用於實現數據持久化的nfs本地目錄下的數據,可是,一旦這個PVC被刪除,那麼本地的數據就會隨着PVC的銷燬而不復存在,也就是說,採用PV這種數據捲來實現數據的持久化,它這個數據持久化的生命週期是和PVC的生命週期是一致的。