Kubernetes存儲卷介紹-emptyDir/hostPath/NFS/configMap

存儲卷介紹

一個容器的對文件系統的寫入都是發生在文件系統的可寫層的,一旦該容器運行結束,所有寫入數據都會被丟棄。在K8S集羣之中,Pod會在各個節點之間漂移,如何保障Pod的數據持久和不同節點數據的共享。Kubernetes提出了存儲卷Volume的概念,Kubernetes存儲卷主要解決了依次遞增的幾個問題:

  • 當運行的容器崩潰時,kubelet會重新啓動該容器,但容器會以乾淨狀態被重新啓動。容器崩潰之前寫入的文件將會被丟失。
  • 當一個Pod中同時運行多個容器時,這些容器之間需要共享文件。
  • 在k8s中,由於Pod分佈在各個不同的節點之上,並不能實現不同節點之間持久性數據的共享,並且在節點故障時,可能會導致數據的永久性丟失。

Kubernetes存儲卷擁有明確的生命週期,與所在的Pod的生命週期相同。因此Kubernetes存儲卷獨立於任何容器,所以數據在Pod重啓的過程中還會保留,當然如果這個Pod被刪除了,那麼這些數據也會被刪除。

Kubernetes 支持的卷類型

Type Type Type Type Type Type
awsElasticBlockStore azureDisk azureFile cephfs cinder configMap
csi downwardAPI emptyDir fc (fibre channel) flexVolume flocker
gcePersistentDisk glusterfs hostPath iscsi local nfs
persistentVolumeClaim projected portworxVolume quobyte rbd scaleIO
storageos vsphereVolume

emptyDir

emptyDir類型的存儲卷創建於Pod被調度到宿主機上的時候,同一個Pod內的多個容器能讀寫emptyDir中的同一個文件,一旦這個Pod銷燬或者漂移開該宿主機,emptyDir中的數據就會被永久刪除。emptyDir類型的存儲卷主要用作臨時空間或者不同容器之間的數據共享。容器的crashing事件並不會導致emptyDir中的數據被刪除。

emptyDir支持內存作爲存儲資源,emptyDir.medium設爲Memory即可,但如果遇到node節點重啓,emptyDir中的數據也會全部丟失。

以下演示如何在Pod中不同容器之間共享數據。

vi emptydir-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: emptydir-pod
spec:
  #定義emptyDir存儲卷,名稱my-emptydir-vol
  volumes:
    - name: my-emptydir-vol
      emptyDir: {}
  containers:
    - name: busybox-1
      image: busybox:latest
      command:
        - "sleep"
        - "3600"
      #將my-emptydir-vol存儲卷Mount到容器中
      volumeMounts:
        - name: my-emptydir-vol
          #此路徑爲容器中目錄
          mountPath: /data-1
    - name: busybox-2
      image: busybox:latest
      command:
        - "sleep"
        - "3600"
      #將my-emptydir-vol存儲卷Mount到容器中
      volumeMounts:
        - name: my-emptydir-vol
          mountPath: /data-2

kubectl apply -f emptydir-pod.yaml

進入容器busybox-1,存在/data-1目錄

kubectl exec -it emptydir-pod -c busybox-1 sh

ls
bin     data-1  dev     etc     home    proc    root    sys     tmp     usr     var

在容器busybox-1向共享目錄中/data-1創建一個文件

cd data-1
echo "hello world" > hello.txt
exit

容器busybox-2的共享目錄中/data-2確實能夠訪問容器busybox-1創建的文件,注意busybox-1:/data-1 = busybox-2:/data-2

kubectl exec -it emptydir-pod -c busybox-2 sh
cat /data-2/hello.txt
hello world
exit

我們看看emptyDir存儲卷在宿主機的位置。在k8s-node1節點,在Pod臨時目錄下存在my-emptydir-vol目錄,容器創建的文件就存放在該目錄中。Pod的臨時目錄會在Pod銷燬時刪除,其中my-emptydir-vol也會被附帶刪除。

kubectl get pod -o yaml | grep nodeName
    nodeName: k8s-node1
kubectl get pod -o yaml | grep uid
    uid: e70923f9-156a-4fde-98be-e77f3f65fbd9

#在k8s-node1節點
tree /var/lib/kubelet/pods/e70923f9-156a-4fde-98be-e77f3f65fbd9/
/var/lib/kubelet/pods/e70923f9-156a-4fde-98be-e77f3f65fbd9/
├── containers
│   ├── busybox-1
│   │   ├── 753e3ae2
│   │   └── 96560e76
│   └── busybox-2
│       ├── 1a8b5849
│       └── a6a696a2
├── etc-hosts
├── plugins
│   └── kubernetes.io~empty-dir
│       ├── my-emptydir-vol
│       │   └── ready
│       └── wrapped_default-token-wtqgl
│           └── ready
└── volumes
    ├── kubernetes.io~empty-dir
    │   └── my-emptydir-vol
    │       └── hello.txt
    └── kubernetes.io~secret
        └── default-token-wtqgl
            ├── ca.crt -> ..data/ca.crt
            ├── namespace -> ..data/namespace
            └── token -> ..data/token

cat /var/lib/kubelet/pods/e70923f9-156a-4fde-98be-e77f3f65fbd9/volumes/kubernetes.io~empty-dir/my-emptydir-vol/hello.txt
hello world

刪除Pod,查看Pod臨時目錄。發現已經沒有Pod臨時目錄e70923f9-156a-4fde-98be-e77f3f65fbd9了。

kubectl delete -f emptydir-pod.yaml

#在k8s-node1節點
ls /var/lib/kubelet/pods/

hostPath

hostPath存儲卷爲pod掛載宿主機上的目錄或文件,使得容器可以使用宿主機的文件系統進行存儲。缺點是不提供Pod的親和性,即hostPath映射的目錄在node1,當Pod可能被調度到node2,導致原來的在node1的數據不存在,Pod一直無法啓動起來。

hostPath存儲卷類型

行爲
空字符串(默認)用於向後兼容,這意味着在安裝hostPath卷之前不會執行任何檢查。
DirectoryOrCreate 如果給定路徑中不存在任何內容,則將根據需要創建一個空目錄,權限設置爲0755,與Kubelet具有相同的組和所有權。
Directory 目錄必須存在於給定路徑中
FileOrCreate 如果給定路徑中不存在任何內容,則會根據需要創建一個空文件,權限設置爲0644,與Kubelet具有相同的組和所有權。
File 文件必須存在於給定路徑中
Socket UNIX套接字必須存在於給定路徑中
CharDevice 字符設備必須存在於給定路徑中
BlockDevice 塊設備必須存在於給定路徑中

以下演示以下hostPath存儲卷的使用。可以先看一下各個node節點是否存在/data目錄。

vi hostpath-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  volumes:
    - name: my-hostpath-vol
      hostPath:
    #爲宿主機的共享目錄
        path: /data/hostpath-1
    #如果宿主機目錄不存在,則創建
        type: DirectoryOrCreate
  containers:
    - name: busybox
      image: busybox:latest
      #將my-hostpath-vol存儲卷Mount到/data目錄,/data目錄爲容器中目錄
      volumeMounts:
        - mountPath: /data
          name: my-hostpath-vol
      command:
        - "/bin/sh"
        - "-c"
        - "echo 'hello world' > /data/hello.txt; sleep 3600" 

kubectl apply -f hostpath-pod.yaml

kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE    IP            NODE        NOMINATED NODE   READINESS GATES
hostpath-pod   1/1     Running   0          20s    10.244.2.23   k8s-node2   <none>           <none>

在k8s-node2節點查看,已經創建了一個共享目錄/data/hostpath-1,並且其中確實存在容器新創建的文件。

cd /data/hostpath-1
cat hello.txt
hello world

刪除Pod,可以發現k8s-node2節點中的共享目錄和文件仍然還存在。

kubectl delete -f hostpath-pod.yaml

cat /data/hostpath-1/hello.txt
hello world

NFS

emptyDir在Pod銷燬時會自動刪除,hostPath無法隨着Pod在node節點之間漂移。和emptyDir和hostPath不同,NFS服務器獨立提供共享文件系統,Pod直接Mount共享目錄,Pod漂移到另一個node節點時,會重新自動Mount共享目錄,數據不會丟失,可以在Pod和node之間共享。

NFS服務器的搭建請參見NFS v4的安裝和使用-CentOS 7。並創建一個新的共享目錄。

mkdir /data/vol -p
chmod 777 /data/vol

vim /etc/exports
/data/vol                     192.168.1.0/24(sync,rw,no_root_squash)

exportfs -r

showmount -e nfs
Export list for nfs:
/data/vol    192.168.1.0/24

在Kubernetes的所有worker節點安裝NFS客戶端,並測試是否能夠連接NFS服務器。

yum install -y nfs-utils

showmount -e 192.168.1.80
Export list for 192.168.1.80:
/data/vol      192.168.1.0/24

以下演示一下NFS存儲卷的使用。創建兩個Pod,分別部署到兩個不同的node節點,並同時向同一個文件中寫入數據。

vi nfs-pod-1.yaml    
apiVersion: v1
kind: Pod
metadata:
  name: nfs-pod-1
spec:
  nodeName: k8s-node1
  volumes:
    - name: my-nfs-vol
      nfs:
        #NFS服務器上創建的共享目錄
        path: /data/vol
        #NFS服務器IP地址
        server: 192.168.1.80
  containers:
    - name: busybox
      image: busybox:latest
      volumeMounts:
        #關聯存儲卷名稱
        - name: my-nfs-vol
          #容器內目錄,NFS的共享目錄就是Mount到該目錄的
          mountPath: /data
      command:
      - "/bin/sh"
      - "-c"
      - "while true; do echo 'nfs-pod-1' >> /data/hello.txt; sleep 2; done"

vi nfs-pod-2.yaml    
apiVersion: v1
kind: Pod
metadata:
  name: nfs-pod-2
spec:
  nodeName: k8s-node2
  volumes:
    - name: my-nfs-vol
      nfs:
        #NFS服務器上創建的共享目錄
        path: /data/vol
        #NFS服務器IP地址
        server: 192.168.1.80
  containers:
    - name: busybox
      image: busybox:latest
      volumeMounts:
        #關聯存儲卷名稱
        - name: my-nfs-vol
          #容器內目錄,NFS的共享目錄就是Mount到該目錄的
          mountPath: /data
      command:
      - "/bin/sh"
      - "-c"
      - "while true; do echo 'nfs-pod-2' >> /data/hello.txt; sleep 2; done"

kubectl apply -f nfs-pod-1.yaml
kubectl apply -f nfs-pod-2.yaml

在NFS服務器查看結果。可以看出兩個不同的Pod在不同的node節點可以同時Mount同一個共享目錄,並同時向一個文件中寫入數據。

tail -f /data/vol/hello.txt
nfs-pod-1
nfs-pod-1
nfs-pod-1
nfs-pod-2
nfs-pod-1
nfs-pod-2
nfs-pod-1
nfs-pod-2
nfs-pod-1
nfs-pod-2
nfs-pod-1
nfs-pod-2

configMap

configMap用於保存配置數據的鍵值對,可以用來保存單個屬性,也可以用來保存配置文件。可以通過環境變量的方式使用configMap,也可以通過存儲卷的方式使用configMap,下面我們演示如何通過存儲卷使用configMap。

vi my-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  age: "12"
  name: |+
    firstName=Zhang
    lastName=San

vi configmap-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  volumes:
    - name: my-configmap-vol
      configMap:
        name: my-configmap
  containers:
    - name: busybox
      image: busybox:latest
      volumeMounts:
        - name: my-configmap-vol
          mountPath: /data
      command:
        - "sleep"
        - "3600"

kubectl apply -f my-configmap.yaml
kubectl apply -f configmap-pod.yaml

進入Pod,可以看出創建了一個/data目錄,其中包含兩個文件age和name,分別對應configMap中的兩個鍵值對,鍵名爲文件名,鍵值爲文件內容。

kubectl exec -it configmap-pod sh
ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
cd /data
ls
age   name
cat age
12
cat name
firstName=Zhang
lastName=San

exit

configMap作爲存儲卷使用時可以熱更新。將my-configmap的鍵name的鍵值增加一個屬性,並重新應用,可以發現容器中的文件內容發生了變化。熱更新間隔時間缺省爲小於1分鐘,也就是configMap的內容更新後在1分鐘之內容器中的文件內容會隨之更新。

vi my-configmap-new.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  age: "12"
  name: |+
    firstName=Zhang
    lastName=San
    userName=zhangsan

kubectl apply -f configmap-pod-new.yaml

kubectl exec -it configmap-pod sh
cat /data/name
firstName=Zhang
lastName=San
userName=zhangsan

其它類型的存儲就不詳細介紹了。

發佈了49 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章