1. 程式人生 > 實用技巧 >kubernetes常用的幾個儲存卷

kubernetes常用的幾個儲存卷

Volume

  容器磁碟上的檔案的生命週期是短暫的,這就使得在容器中執行重要應用時會出現一些問題。首先,當容器崩潰時,kubelet會重啟它,但是容器中的檔案將丟失——容器以乾淨點狀態(映象最初點狀態)重新啟動。其次,在pod中同時執行多個容器時,這些容器之間通常需要共享檔案。Kubernetes中的volume就能很好的解決了這些問題。

1.背景

Docker中也有一個volume的概念,儘管它稍微寬鬆一些,管理也很少。在Docker中,卷就像是磁碟或是另一個容器中的一個目錄。它的生命週期不受管理,直到最近才有了local-disk-backed卷。Docker現在提供來卷驅動程式,但是功能還非常有限(例如Docker1.7只允許每個容器使用一個卷驅動,並且無法給卷傳遞引數)。

另一方面,Kubernetes中的卷有明確的壽命——與封裝他的Pod相同。所以,卷的生命比Pod中點所有容器都長,當這個容器重啟時資料仍然得以儲存。當然,當pod不再存在時,卷也將不復存在。也許更重要點的是,Kubernetes支援多種型別的卷,Pod可以同時使用任意數量的卷。

卷的核心是目錄,可能還包含了一些資料,可以通過pod中的容器來訪問。該目錄是如何形成的、支援該目錄的介質以及內容取決於所使用的特定卷型別。

要使用卷,需要為pod指定為卷(spec.volumes 欄位)以及將它掛載到容器的位置(spec.containers.volumeMounts 欄位)。

容器中的程序看到的是其由Docker映象和卷組成點檔案系統檢視。Docker映象位於檔案系統層次結構的根目錄,任何卷都被掛載在映象的指定路徑中。卷無法掛載到其他捲上或與其他卷有硬連結。Pod中的每個容器都必須獨立指定每個卷的掛載位置。

Pod使用Volume步驟:

  1、在Pod上定義儲存卷,並關聯至目標儲存服務上;

  2、在需要用到儲存卷的容器上,掛載其所屬Pod的儲存卷。

volume的資源清單詳解:

spec:
  volumes:
  - name <string> #儲存卷名稱標識,僅可使用DNS標籤格式的字元,在當前Pod中必須唯一
    VOL_TYPE <Object> #儲存卷外掛及具體的目標儲存供給方的相關配置
  containers:
  - name: ...
    image: ...
    volumeMounts:
    - name <string
> #要掛載的儲存卷的名稱,必須匹配儲存卷列表中的某項定義 mountPatch <string> #容器檔案系統上的掛載點路徑 readOnly <boolean> #是否掛載為只讀模式,預設為"" subPath <string> #掛載儲存捲上的一個子目錄至指定的掛載點 subPathExpr <string> #掛載由指定的模式匹配到的儲存卷的檔案或目錄至掛載點 mountPropagation <string> #掛載卷的傳播模式

2.卷的型別

Kubernetes 1.19.4 ⽀持以下型別的卷:

awsElasticBlockStore
azureDisk
azureFile
cephfs
cinder
configMap
csi
downwardAPI
emptyDir
ephemeral
fc
flexVolume
flocker
gcePersistentDisk
gitRepo
glusterfs
hostPath
iscsi
name
nfs
persistentVolumeClaim
photonPersistentDisk
portworxVolume
projected
quobyte
rbd
scaleIO
secret
storageos
vsphereVolume

上述型別中,emptyDir與hostPath屬於節點級別的卷型別,emptyDir的生命週期與Pod資源相同,而使用了hostPath卷的Pod一旦被重新排程至其他節點,那麼它將無法在使用此前的資料。因此,這兩張型別都不具有永續性。要想使用持久型別的儲存卷,就得使用網路儲存系統,如NFS,Ceph、GlusterFS等,或者雲端儲存,如awsElasticBlockStore、gcePersistentDisk。

然而,網路儲存系統通常都不太容易使用,有的甚至很複雜,以至於對大多數使用者來說它是一個難以逾越點障礙。Kubernetes為此專門設計了一種叢集級別的資源“persistentVolume”(簡稱PV),它藉由管理員配置儲存系統,而後由使用者通過“persistentVolumeClaim”(簡稱PVC)儲存卷直接申請使用的機制大大簡化了終端儲存使用者的配置過程,有效降低了使用難度。

再者,Secret和ConfigMap算得上是兩種特殊的卷型別。

  1)Secret用於向Pod傳遞敏感資訊,如密碼、私鑰、證書檔案等,這些資訊如果直接定義在映象中很容易導致洩露,有了Secre資源,使用者可以將這些資訊儲存於叢集中而後由Pod進行掛載,從而實現將敏感資料與系統解耦。

  2)ConfigMap資源則用於向Pod注入非敏感資料,使用時,使用者將資料直接儲存於ConfigMap物件中,而後直接在Pod中使用ConfigMap卷引用它即刻,它可以幫助實現容器配置檔案集中化定義和管理。

3.常用卷詳解

3.1 emptyDir(臨時儲存卷)

當 Pod 被分配給節點時,⾸先建立 emptyDir 卷,並且只要該 Pod 在該節點上運⾏,該卷就會存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以讀取和寫⼊ emptyDir 卷中的相同⽂件,儘管該卷可以掛載到每個容器中的相同或不同路徑上。當出於任何原因從節點中刪除 Pod 時, emptyDir 中的資料將被永久刪除。

注意:容器崩潰不會從節點中移除pod,因此emptyDir卷中的資料在容器崩潰時是安全的。

emptyDir的用法有:

  • 暫存空間,例如用於基於磁碟的合併排序

  • 用作長時間計算崩潰恢復時的檢查點

  • Web伺服器容器提供資料時,儲存內容管理器容器提取的檔案

總結:

  • emptyDir只是一個臨時掛載的檔案,pod刪除後,該目錄也會在node節點上被刪除;但是容器崩潰時,該檔案還存在.

範例:

#在master上編寫emptyDir資源清單
[root@ubuntu-200 ~]# cat volume-emptyDir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: emptydir-pod    #注意此處只能是小寫字母或數字,大寫字母會報錯
spec:
  containers:
  - name: nginx-test
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

#建立pod
[root@ubuntu-200 ~]# kubectl apply -f volume-emptyDir.yaml

#檢視Pod
[root@ubuntu-200 ~]# kubectl get pods -o wide
NAME                READY   STATUS             RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
emptydir-pod        1/1     Running            0          17s     10.244.2.62   ubuntu-210   <none>           <none>

#進入容器並在容器中建立檔案測試
[root@ubuntu-200 ~]# kubectl exec -it emptydir-pod -- bash
root@emptydir-pod:/# ls -l /cache/
total 0
root@emptydir-pod:/cache# touch /cache/nginx-emptydir.log
root@emptydir-pod:/cache# echo nginx-emptydir > /cache/nginx-emptydir.log 
root@emptydir-pod:/cache# cat /cache/nginx-emptydir.log 
nginx-emptydir

#在node節點上查詢臨時儲存的檔案位置
[root@ubuntu-210 ~]# find / -name nginx-emptydir.log
/var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/nginx-emptydir.log

#檢視檔案內容
[root@ubuntu-210 ~]# cat /var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/nginx-emptydir.log
nginx-emptydir

#在master節點上刪除pod
[root@ubuntu-200 ~]# kubectl delete -f volume-emptyDir.yaml 
pod "emptydir-pod" deleted

#在node節點上檢視臨時儲存是否還存在
[root@ubuntu-210 ~]# cat /var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/nginx-emptydir.log
cat: /var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/nginx-emptydir.log: No such file or directory
[root@ubuntu-210 ~]# ll /var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/
ls: cannot access '/var/lib/kubelet/pods/95dcb0a8-61f7-42e3-aa35-e7f1a1872b20/volumes/kubernetes.io~empty-dir/cache-volume/': No such file or directory

3.2 hostPath(節點儲存卷)

hostPath型別的儲存卷是指將工作節點上某個檔案系統的目錄或檔案掛載於Pod中的一種儲存卷,它獨立於Pod資源的生命週期,因而具有永續性。但它是工作節點本地的儲存空間,僅適用於特定情況下的儲存卷使用要求,例如,將工作節點上的檔案系統關聯為Pod的儲存卷,從而使得容器訪問節點檔案系統上的資料。這一點在執行有管理任務的系統級Pod資源需要訪問節點上的檔案時尤為有用。

hostPath的用途如如下:

  • 執行需要訪問Docker內部的容器;使用/var/lib/docker的hostPath

  • 在容器中執行cAdvisor;使用/dev/cgroups的hostPath

  • 執行pod指定給定的hostPath是否應該在pod執行之前存在,是否應該建立,以及它應該以什麼形式存在

配置hostPath儲存卷點巢狀欄位共有兩個:一個是用於指定工作節點上點目錄路徑的必須欄位path;另一個是指定儲存卷型別的type,它支援使用的卷型別包含以下幾種:

  • DirectoryCreate:如果在給定的路徑上沒有任何東⻄存在,那麼將根據需要在那⾥建立⼀個空⽬錄,許可權設定為 0755,與 Kubelet 具有相同的組和所有權

  • Directory:給定的路徑下必須存在⽬錄

  • FileOrCreate:如果在給定的路徑上沒有任何東⻄存在,那麼會根據需要建立⼀個空⽂件,許可權設定為0644,與 Kubelet 具有相同的組和所有權。

  • File:給定的路徑下必須存在⽂件

  • Socket:給定的路徑下必須存在 UNIX 套接字

  • CharDevice:給定的路徑下必須存在字元裝置

  • BlockDevice:給定的路徑下必須存在塊裝置

注意:

  • 如資源清單中未配置type欄位則為預設選項(預設的值為空字串),空字串(預設)⽤於向後相容,這意味著在掛載 hostPath 卷之前不會執⾏任何檢查;就是不檢查path的型別

  • 由於每個節點上點檔案都不同,具有相同配置(例如從podTemplate建立的)的pod在不同節點上的行為可能會有所不同

  • 當Kubetnetes安裝計劃新增資源感知排程時,將無法考慮hostPath使用的資源

  • 在底層主機上建立的檔案或目錄只能由root寫入。需要在特權容器中以root身份執行程序,或修改主機上的檔案許可權以便寫入hostPath卷。

範例:

[root@ubuntu-200 ~]# cat volume-hostPath-test.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test-pod
spec:
  containers:
  - name: nginx-hostpath
    image: nginx
    volumeMounts:
    - mountPath: /var/logs/
      name: data-log
  #定義hostPath卷名,和宿主機共享的路徑
  volumes:
  - name: data-log
    hostPath:
      path: /data/logs
      
#建立pod
[root@ubuntu-200 ~]# kubectl apply -f volume-hostPath-test.yaml
[root@ubuntu-200 ~]# kubectl get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
hostpath-test-pod   1/1     Running   0          66m   10.244.2.65   ubuntu-210   <none>           <none>

#在node節點上的共享目錄上建立檔案
[root@ubuntu-210 ~]# echo 111 > /data/logs/1.txt
[root@ubuntu-210 ~]# cat /data/logs/1.txt 
111

#在master節點上進入pod,檢視掛載卷的檔案內容
[root@ubuntu-200 ~]# kubectl exec -it hostpath-test-pod -- bash
root@hostpath-test-pod:/# cat /var/logs/1.txt 
111

#在pod內檢視和建立建立檔案
root@hostpath-test-pod:/# ls /var/logs/
1.txt
root@hostpath-test-pod:/# echo 222 > /var/logs/2.txt
root@hostpath-test-pod:/# cat /var/logs/2.txt 
222
root@hostpath-test-pod:/# ls /var/logs/
1.txt 2.txt

#在Node節點上檢視pod內部建立的檔案
[root@ubuntu-210 ~]# cat /data/logs/2.txt 
222

#刪除Pod
[root@ubuntu-200 ~]# kubectl delete -f  volume-hostPath-test.yaml 
pod "hostpath-test-pod" deleted

#Node節點上檢視檔案是否存在
[root@ubuntu-210 ~]# ll /data/logs/
total 16
drwxr-xr-x 2 root root 4096 Dec  8 08:46 ./
drwxr-xr-x 4 root root 4096 Dec  8 07:34 ../
-rw-r--r-- 1 root root    4 Dec  8 08:43 1.txt
-rw-r--r-- 1 root root    4 Dec  8 08:46 2.txt #可看到在Pod內部建立的檔案依然存在,沒有隨pod的刪除而刪除,已初步實現了持久化.
[root@ubuntu-210 ~]# cat /data/logs/2.txt 
222

3.3 nfs儲存卷

nfs卷能將NFS(網路檔案系統)掛載到你的pod中。不像emptyDir,當刪除Pod時,nfs卷的內容被保留,卷僅僅是被解除安裝。另外,NFS是檔案系統級共享服務,它支援同時存在的多路掛載請求。

注意:要使用nfs,必須先擁有自己的NFS伺服器,然後才能使用。

定義NFS儲存卷時,常用到以下欄位:

  • server<string>:NFS伺服器的IP地址或主機名,必選欄位。

  • path<string>:NFS伺服器匯出(共享)的檔案系統路徑,必選欄位。

  • readOnly<boolean>:是否以只讀方式掛載,預設為false。

範例:

#先配置nfs伺服器
[root@C8_58 test]# yum -y install nfs-utils
[root@C8_58 test]# cat /etc/exports
/data/test *(rw)
[root@C8_58 test]# mkdir -p /data/test
[root@C8_58 test]# echo nginx web > /data/test/index.html
[root@C8_58 test]# systemctl restart nfs-server

#測試能否正常掛載
[root@ubuntu-220 ~]# apt -y install nfs-server
[root@ubuntu-220 ~]# showmount -e 10.0.0.58
Export list for 10.0.0.58:
/data/test *
[root@ubuntu-220 ~]# mount -t nfs 10.0.0.58:/data/test /mnt/
[root@ubuntu-220 ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
udev                  955M     0  955M   0% /dev
tmpfs                 198M   13M  185M   7% /run
/dev/sda2              98G  5.4G   88G   6% /
tmpfs                 986M     0  986M   0% /dev/shm
tmpfs                 5.0M     0  5.0M   0% /run/lock
tmpfs                 986M     0  986M   0% /sys/fs/cgroup
/dev/loop1             97M   97M     0 100% /snap/core/9665
/dev/loop0             98M   98M     0 100% /snap/core/10444
/dev/sda3             976M  146M  764M  16% /boot
/dev/sda5              95G   61M   91G   1% /data
tmpfs                 198M     0  198M   0% /run/user/0
10.0.0.58:/data/test   50G  390M   50G   1% /mnt

#編寫資源清單
[root@ubuntu-200 ~]# cat volume-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-nfs
  labels:
    app: nginx-nfs
spec:
  containers:
  - name: nginx-nfs
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: my-nfs-volume
  volumes:
  - name: my-nfs-volume
    nfs:
      server: 10.0.0.58
      path: /data/test

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - name: http
    port: 81
    targetPort: 80
    nodePort: 30016
    protocol: TCP
  type: NodePort
  selector:
    app: nginx-nfs

#建立Pod及service
[root@ubuntu-200 ~]# kubectl apply -f volume-nfs.yaml
[root@ubuntu-200 ~]# kubectl get pods -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
nginx-nfs   1/1     Running   0          24m   10.244.2.67   ubuntu-210   <none>           <none>
[root@ubuntu-200 ~]# kubectl get service nginx-service -o wide
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE   SELECTOR
nginx-service   NodePort   10.104.177.209   <none>        81:30016/TCP   25m   app=nginx-nfs

#進入Pod檢視是否掛載成功
[root@ubuntu-200 ~]# kubectl exec -it nginx-nfs -- bash
root@nginx-nfs:/# cat /usr/share/nginx/html/index.html 
nginx web

訪問測試