您好,登錄后才能下訂單哦!
在一切虛擬化解決方案中,數據的持久化都是需要我們非常關心的問題,docker如此,K8s也不例外。在k8s中,有一個數據卷的概念。
k8s數據卷主要解決了以下兩方面問題:
- 數據持久性:通常情況下,容器運行起來后,寫入到其文件系統的文件時暫時性的。當容器崩潰后,kebelet將這個容器kill掉,然后生成一個新的容器,此時,新運行的容器將沒有原來容器內的文件,因為容器是重新從鏡像創建的。
- 數據共享:同一個pod中運行的容器之間,經常會存在共享文件/文件夾的需求。
在k8s中,Volume(數據卷)存在明確的生命周期(與包含該數據卷的容器組(pod)相同)。因此Volume的生命周期比同一容器組(pod)中任意容器的生命周期要更長,不管容器重啟了多少次,數據都被保留下來。當然,如果pod不存在了,數據卷自然退出了。此時,根據pod所使用的數據卷類型不同,數據可能隨著數據卷的退出而刪除,也可能被真正持久化,并在下次容器組重啟時仍然可以使用。
從根本上來說,一個數據卷僅僅是一個可以被pod訪問的目錄或文件。這個目錄是怎么來的,取決于該數據卷的類型(不同類型的數據卷使用不同的存儲介質)。同一個pod中的兩個容器可以將一個數據卷掛載到不同的目錄下。
k8s目前支持28種數據卷類型(其中大多數特定于云環境),這里將寫下在k8s中常用的幾種數據卷類型。
emptyDir類型的數據卷在創建pod時分配給該pod,并且直到pod被移除,該數據卷才被釋放。該數據卷初始分配時,始終是一個空目錄。同一個pod中的不同容器都可以對該目錄執行讀寫操作,并且共享其中的數據(盡管不同容器可能將該數據卷掛載到容器中的不同路徑)。當pod被刪除后,emptyDir數據卷中的數據將被永久刪除。(PS:容器奔潰時,kubelet并不會刪除pod,而僅僅是將容器重啟,因此emptyDir中的數據在容器崩潰并重啟后,仍然是存在的)。
emptyDir的使用場景如下:
- 空白的初始空間,例如合并/排序算法中,臨時將數據保存在磁盤上。
- 長時間計算中存儲檢查點(中間結果),以便容器崩潰時,可以從上一次存儲的檢查點(中間結果)繼續進行,而不是從頭開始。
- 作為兩個容器的共享存儲,使得第一個內容管理的容器可以將生成的數據存入其中,同時由一個webserver容器對外提供這些頁面。
- 默認情況下,emptyDir數據卷存儲在node節點的存儲介質(機械硬盤、SSD或網絡存儲)上。
[root@master ~]# vim emtydir.yaml #pod的yaml文件如下
apiVersion: v1
kind: Pod
metadata:
name: read-write
spec:
containers:
- name: write #定義一個名為write的容器
image: busybox
volumeMounts:
- mountPath: /write #當數據持久化類型為emtydir時,這里的路徑指的是容器內的路徑
name: share-volume #指定本地的目錄名
args: #容器運行后,進行寫操作
- /bin/sh
- -c
- echo "emtydir test" > /write/hello;sleep 30000
- name: read #定義一個名為read的容器
image: busybox
volumeMounts:
- mountPath: /read
name: share-volume #指定本地的目錄名
args: #容器運行后,進行讀操作
- /bin/sh
- -c
- cat /read/hello; sleep 30000
volumes: #這里的volumes是指對上面掛載的進行解釋
- name: share-volume #這里的名字必須和上面pod的mountPath下的name值對應
emptyDir: {} #這里表示是個空目錄,主要是定義了一個數據持久化的類型
[root@master ~]# kubectl apply -f emtydir.yaml #執行yaml文件
[root@master ~]# kubectl exec -it read-write -c write /bin/sh #進入第一個pod
/ # cat /write/hello #確認yaml文件執行的命令是否生效
emtydir test
[root@master ~]# kubectl exec -it read-write -c read /bin/sh #進入第二個容器名為read的容器查看
/ # cat /read/hello #查看指定掛載的目錄下是否和write容器中的內容一致
emtydir test
#至此,起碼可以確認這兩個pod是掛載了同一個本地目錄,文件內容都一致。
#那么,現在看看具體掛載的是本地哪個目錄?
[root@master ~]# kubectl get pod -o wide #先通過此命令查看pod是運行在哪個節點上的
#我這里是運行在node01節點的,所以接下來需要到node01節點上進行查看
#node01節點操作如下:
[root@node01 ~]# docker ps #通過此命令查看出運行的容器ID號
CONTAINER ID IMAGE #省略部分內容
6186a08c6d5f busybox
5f19986f0879 busybox
[root@node01 ~]# docker inspect 6186a08c6d5f #查看第一個容器的詳細信息
"Mounts": [ #找到mount字段
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume",
#上面的source就是指定的本地目錄
"Destination": "/read",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
[root@node01 ~]# docker inspect 5f19986f0879 #查看第二個容器的詳細信息
"Mounts": [ #同樣定位到mount字段
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume",
#可以看到,上面指定的本地目錄和第一個容器指定的是同一個目錄
"Destination": "/write",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
},
#至此,已經可以確定這兩個容器的掛載目錄共享的是同一個本地目錄
[root@node01 ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
#查看本地該目錄下的內容,和pod中的一致
emtydir test
至此,emptyDir的特性就已經驗證了,只要這個pod中還有一個容器在運行,那么這個本地的數據就不會丟失,但如果這個pod被刪除,那么本地的數據也將不復存在。
驗證如下:
node01上刪除一個pod并再次查看本地目錄:
[root@node01 ~]# docker rm -f 6186a08c6d5f #刪除一個pod
6186a08c6d5f
[root@node01 ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
#查看本地目錄,發現文件還在
emtydir test
在master上將此pod刪除,再次去node01節點上查看本地目錄是否存在:
#master上刪除pod
[root@master ~]# kubectl delete -f emtydir.yaml
#在node01上再次查看本地目錄,會提示不存在這個目錄
[root@node01 ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
cat: /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello: 沒有那個文件或目錄
emptyDir總結:
同個pod里面的不同容器,共享同一個持久化目錄,當pod節點刪除時,volume的內容也會被刪除。但如果僅僅是容器被銷毀,pod還在,則volume不會受到任何影響。說白了,emptyDir的數據持久化的生命周期和使用的pod一致。一般是作為臨時存儲使用。
hostPath 類型的數據卷將 Pod(容器組)所在節點的文件系統上某一個文件或目錄掛載進容器組(容器內部),類似于docker中的bind mount掛載方式。
這種數據持久化的方式,使用場景不多,因為它增加了pod與節點之間的耦合。
絕大多數容器組并不需要使用 hostPath 數據卷,但是少數情況下,hostPath 數據卷非常有用:
適用場景如下:
- 某容器需要訪問 Docker,可使用 hostPath 掛載宿主節點的 /var/lib/docker
- 在容器中運行 cAdvisor,使用 hostPath 掛載宿主節點的 /sys
總言而之,一般對K8s集群本身的數據持久化和docker本身的數據持久化會使用這種方式。
由于其使用場景比較少,這里就不舉例了。
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的管理過程描述如下:
1、在主機上劃分出一個單獨的目錄用于PV使用,并且定義其可用大小
2、創建PVC這個資源對象,以便請求PV的存儲空間
3、pod中添加數據卷,數據卷關聯到PVC;
4、Pod中包含容器,容器掛載數據卷
其實上面解釋那么多,可能還是云里霧里的,下面是一個使用案例,僅供參考。
案例大概過程如下:
底層存儲采用nfs存儲,然后在nfs的目錄下劃分1G的容量供PV調度。然后通過創建PVC來申請PV的存儲資源空間,最后創建pod測試,使用PVC聲明的存儲資源來實現數據的持久化。
為了方便操作,我直接在master上搭建nfs存儲。
[root@master ~]# yum -y install nfs-utils
[root@master ~]# systemctl enable rpcbind
[root@master ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash)
[root@master ~]# systemctl start nfs-server
[root@master ~]# systemctl enable nfs-server
[root@master ~]# showmount -e
Export list for master:
/nfsdata *
[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.20.6 #nfs服務器的IP
#關于上述的具體解釋
#capacity:指定PV的大小
#AccessModes:指定訪問模式
#ReadWriteOnce:只能以讀寫的方式掛載到單個節點(單個節點意味著只能被單個PVC聲明使用)
#ReadOnlyMany:能以只讀的方式掛載到多個節點
#ReadWriteMany:能以讀寫的方式掛載到多個節點
#persistentVolumeReclaimPolicy:PV的回收策略
#Recycle:清除PV中的數據,然后自動回收。
#Retain:需要手動回收。
#Delete:刪除云存儲資源。(云存儲專用)
#PS:注意這里的回收策略是指,在PV被刪除后,在這個PV下所存儲的源文件是否刪除。
#storageClassName:PV和PVC關聯的依據。
[root@master ~]# kubectl apply -f test-pv.yaml #執行yaml文件
[root@master ~]# kubectl get pv test-pv #既然PV是一個資源對象,那么自然可以通過此方式查看其狀態
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
test-pv 1Gi RWO Recycle Available nfs 38s
#查看PV的狀態必須為Available才可以正常使用
[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文件
#再次查看PV及PVC的狀態(狀態為bound,表示該PV正在被使用)
[root@master ~]# kubectl get pvc #查看PVC的狀態
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-pvc Bound test-pv 1Gi RWO nfs 2m10s
[root@master ~]# kubectl get pv #查看PV的狀態
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
test-pv 1Gi RWO Recycle Bound default/test-pvc nfs 8m24s
這里創建的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.20.6:/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的狀態
#如果pod的狀態還是正在創建,那么就是因為運行該pod的節點上的kubelet組件還沒有反應過來
#如果要追求pod的啟動速度,可以手動將pod所在節點的kubelet組件進行重啟即可。
[root@master ~]# kubectl get pod test-pod #稍等片刻,再次查看,發現其pod已經running了
NAME READY STATUS RESTARTS AGE
test-pod 1/1 Running 0 8m
[root@master ~]# kubectl exec -it test-pod /bin/sh #進入pod
/ # echo "test pv pvc" > /testdata/test.txt #向數據持久化的目錄寫入測試信息
#回到nfs服務器,查看共享的目錄下是否有容器中寫入的信息
[root@master ~]# cat /nfsdata/test-pv/test.txt #確定是有的
test pv pvc
#現在查看到pod容器的所在節點,然后到對應節點將其刪除
[root@master ~]# kubectl get pod -o wide #我這里是運行在node02節點
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-pod 1/1 Running 0 11m 10.244.2.2 node02 <none> <none>
#在node02節點查看到其pod容器的ID號,然后將其刪除
[root@node02 ~]# docker ps #獲取容器的ID號
[root@node02 ~]# docker rm -f dd445dce9530 #刪除剛剛創建的容器
#回到nfs服務器,發現其本地目錄下的數據還是在的
[root@master ~]# cat /nfsdata/test-pv/test.txt
test 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的生命周期是一致的。
———————— 本文至此結束,感謝閱讀 ————————
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。