K8s實現數據持久化

前言

在一切虛擬化解決方案中,數據的持久化都是須要咱們很是關心的問題,docker如此,K8s也不例外。在k8s中,有一個數據卷的概念。node

*k8s數據卷主要解決了如下兩方面問題:*

* 數據持久性:一般狀況下,容器運行起來後,寫入到其文件系統的文件時暫時性的。當容器崩潰後,kebelet
將這個容器kill掉,而後生成一個新的容器,此時,新運行的容器將沒有原來容器內的文件,由於容器是從新從鏡像建立的。
* 數據共享:同一個pod中運行的容器之間,常常會存在共享文件/文件夾的需求。

在k8s中,Volume(數據卷)存在明確的生命週期(與包含該數據卷的容器組(pod)相同)。所以Volume的生命週期比同一容器組(pod)中任意容器的生命週期要更長,無論容器重啓了多少次,數據都被保留下來。固然,若是pod不存在了,數據卷天然退出了。此時,根據pod所使用的數據卷類型不一樣,數據可能隨着數據卷的退出而刪除,也可能被真正持久化,並在下次容器組重啓時仍然可使用。web

從根本上來講,一個數據卷僅僅是一個能夠被pod訪問的目錄或文件。這個目錄是怎麼來的,取決於該數據卷的類型(不一樣類型的數據卷使用不一樣的存儲介質)。同一個pod中的兩個容器能夠將一個數據卷掛載到不一樣的目錄下。算法

1、數據卷類型

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   //查看第一個容器的詳細信息

K8s實現數據持久化

[root@node01 ~]# docker inspect  918e6ca1b218  //查看第二個容器的詳細信息

K8s實現數據持久化

//從以上內容能夠看出這兩個容器的源目錄是同樣,掛載的是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(存儲卷聲明)的關係以下圖所示:
K8s實現數據持久化

上圖中的解釋以下:

* 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的生命週期是一致的。

相關文章
相關標籤/搜索