kubernetes - day21

September 11, 2020

Kubernetes 提供的儲存方案屬於 POD 的資源,一個 POD 中的多個容器可以一同共享其儲存數據。而在容器中的數據可能會隨著 POD 生命週期而消失,在 Kubernetes 上的儲存方案可以實現出在 POD 生命週期外的儲存。接下來將會介紹 Kubernetes 上的儲存應用。

volume 概念

就像開頭講的 POD 的生命週期無法讓容器可以永久儲存數據。以 Docker 來說它可以支持網路檔案系統或是一般的檔案系統以掛載方式作永久性儲存,而在 Kubernetes 中也為 POD 提供相似的功能,這功能使得容器可以可掛載 POD 設定的外部儲存設備方案,也可達到跨節點的需求,至於是否要持久化儲存,取決於設定。示意圖如下。

Kubernetes 的儲存方案

Kubernete 所支援的儲存方案非常的多,儲存方面還支援了 ConfigMapSecret 這些對於隱密資訊或一些變數的儲存。從官方可以看有很多儲存類型,不論是分散式或是雲端儲存。

官方中的 emptyDir 類型隨著 POD 生命週期變化;hostPath 則是以主機的目錄關聯至 POD,只要被重新調度,就無法使用,以持久化來看這兩個類型並非是持久的。相對的持久化的類型都要是網路儲存系統像是 NFSCeph 甚至是雲端的儲存方案。Kubernetes 中儲存有 PersistentVolume(PV) 和 persistentVolumeClaim(PVC) 的概念,PV 可借助管理者配置其儲存方案;PVC 則是去請求那些 PV,這過程簡化了配置儲存方案的複雜度。

前面有題到兩個特殊類型的 volume 分別是 ConfigMapSecret,以下進行簡略介紹

下面會先實作 emptyDirhostPath,下一章節會在講 NFS 的實驗。

暫存儲存卷 emptyDir

可將它想成是一個暫時的目錄,它隨著 POD 的生命週期而的生命週期而變化,POD 運行則建立,POD 終至則刪除。透過 kubectl explain pod.spec.volumes.emptyDir 可查看該屬性有什麼,其屬性如下

以下為範例的 yaml 檔,定義一個名稱為 html 的儲存卷(volume),掛載至容器的 /data/web/html/data/ 下,注意定義了 volumes 不代表說容器就會有資訊,volumes 屬於 POD 級別因此只是建立關聯,容器要使用則需要定義 volumeMounts

# pod-emptydir-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-volume-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: nginx:1.18
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: html
      mountPath: /data/web/html
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data/
    command: ["bin/sh", "-c", "sleep 3600"]
  volumes:
  - name: html
    emptyDir: {} # {} 表示不提供任何需求

同樣的使用 apply 放式部署,之後藉由 exec 與容器進行交互並觀察是否有掛載本機建立的 data 目錄,結果如下。

$ kubectl exec -it pod-volume-demo -c busybox -- /bin/sh # -c 是指定 POD 中的哪個容器
/ # mount | grep data
/dev/sda1 on /data type ext4 (rw,relatime,commit=30)
/ # echo "$(date)-busybox" >> /data/index.html
/ # cat /data/index.html
Thu Sep 17 12:36:19 UTC 2020-busybox
/ # exit
$ kubectl exec -it pod-volume-demo -c myapp -- /bin/sh
# cat /data/web/html/index.html
Thu Sep 17 12:36:19 UTC 2020-busybox
#

接著這邊來驗證容器是否能夠一讀一寫,yaml 如下,busybox 容器負責寫,每兩秒寫一次,myapp 負責讀。

apiVersion: v1
kind: Pod
metadata:
  name: pod-volume-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: nginx:1.18
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data/
    command: ["bin/sh", "-c", "while true; do echo $(date)-busybox >> /data/index.html; sleep 2; done"]
  volumes:
  - name: html
    emptyDir: {}

GKE 為了方便驗證部分請至任一節點使用 curl 對部署 PODIP 進行請求,結果如下

$ kubectl exec -it pod-volume-demo -c busybox -- /bin/sh
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # ls -Rl data
data:
total 12
-rw-r--r--    1 root     root         11914 Sep 17 13:07 index.html
$ kubectl get pods pod-volume-demo -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
pod-volume-demo   2/2     Running   0          13m   10.4.3.119   gke-cluster-1-default-pool-7dc8b11b-8kvr   <none>           <none>
@gke-cluster-1-default-pool-7dc8b11b-8kvr$ curl 10.4.3.119 # this
Thu Sep 17 12:57:16 UTC 2020-busybox
Thu Sep 17 12:57:18 UTC 2020-busybox
Thu Sep 17 12:57:20 UTC 2020-busybox
Thu Sep 17 12:57:22 UTC 2020-busybox

hostPath 儲存卷

簡單的說就是將節點主機上的某個檔案系統掛載至 POD 上,相較於 emptyDir 它能夠保存資訊至節點主機上,進而不被 POD 生命週期所影響。不過只要 POD 被調度至其它節點則會導致資訊無法取得。同樣的 hostPath 有兩個屬性分別是 pathtype,其 type 有以下詳細內容查看官方即可

實驗的 yamltype 使用 DirectoryOrCreate 掛載目錄不存在時就創建。

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath-demo
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx:1.18
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/
  volumes:
  - name: html
    hostPath:
      path: /home/cchong0124/data/pod/volume1
      type: DirectoryOrCreate

下面是從他佈署到的節點觀察,確實在 …r7rg 節點下沒定義我們的掛載目錄,但因為選擇的 type 是會假設不存在掛載目錄時會幫我們建立。

$ kubectl get pods pod-hostpath-demo -o wide                                                                                                        
NAME                READY   STATUS    RESTARTS   AGE     IP          NODE                                       NOMINATED NODE   READINESS GATES
pod-hostpath-demo   1/1     Running   0          3m13s   10.4.2.55   gke-cluster-1-default-pool-7dc8b11b-r7rg   <none>           <none>
gke-cluster-1-default-pool-7dc8b11b-r7rg ~ $ ls -Rl
.:
total 4
drwxr-xr-x 3 root root 4096 Sep 17 13:41 data

./data:
total 4
drwxr-xr-x 3 root root 4096 Sep 17 13:41 pod

./data/pod:
total 4
drwxr-xr-x 2 root root 4096 Sep 17 13:41 volume1

./data/pod/volume1:
total 0

大家可以嘗試將 yaml 改成 Deployment 並在每個節點的掛載目錄下建立 index.html 觀察它是否是跟著節點的,原則上是。此類型的儲存對於會被調度的資源則不適用。

參考資源