Kubernetes 提供的儲存方案屬於 POD 的資源,一個 POD 中的多個容器可以一同共享其儲存數據。而在容器中的數據可能會隨著 POD 生命週期而消失,在 Kubernetes 上的儲存方案可以實現出在 POD 生命週期外的儲存。接下來將會介紹 Kubernetes 上的儲存應用。
volume 概念
就像開頭講的 POD 的生命週期無法讓容器可以永久儲存數據。以 Docker 來說它可以支持網路檔案系統或是一般的檔案系統以掛載方式作永久性儲存,而在 Kubernetes 中也為 POD 提供相似的功能,這功能使得容器可以可掛載 POD 設定的外部儲存設備方案,也可達到跨節點的需求,至於是否要持久化儲存,取決於設定。示意圖如下。

Kubernetes 的儲存方案
Kubernete 所支援的儲存方案非常的多,儲存方面還支援了 ConfigMap、Secret 這些對於隱密資訊或一些變數的儲存。從官方可以看有很多儲存類型,不論是分散式或是雲端儲存。
官方中的 emptyDir 類型隨著 POD 生命週期變化;hostPath 則是以主機的目錄關聯至 POD,只要被重新調度,就無法使用,以持久化來看這兩個類型並非是持久的。相對的持久化的類型都要是網路儲存系統像是 NFS、Ceph 甚至是雲端的儲存方案。Kubernetes 中儲存有 PersistentVolume(PV) 和 persistentVolumeClaim(PVC) 的概念,PV 可借助管理者配置其儲存方案;PVC 則是去請求那些 PV,這過程簡化了配置儲存方案的複雜度。
前面有題到兩個特殊類型的 volume 分別是 ConfigMap 和 Secret,以下進行簡略介紹
- ConfigMap
- 為
POD寫入而外訊息,將數據定義至ConfigMap對象中。POD需要時則在資源清單中引用即可
- 為
- Secret
- 儲存較隱密的資訊用,需使用時在將其掛載至
POD中,這樣避免了在製作image時隱密資訊的寫入
- 儲存較隱密的資訊用,需使用時在將其掛載至
下面會先實作 emptyDir 與 hostPath,下一章節會在講 NFS 的實驗。
暫存儲存卷 emptyDir
可將它想成是一個暫時的目錄,它隨著 POD 的生命週期而的生命週期而變化,POD 運行則建立,POD 終至則刪除。透過 kubectl explain pod.spec.volumes.emptyDir 可查看該屬性有什麼,其屬性如下
- medium
- 儲存的媒介,預設是
default,另一個為Memory。Memory就是實現於RAM的tmpfs檔案系統。
- 儲存的媒介,預設是
- sizeLimit
- 使用的大小,預設是
nil不限制。medium為Memory應當要限制。
- 使用的大小,預設是
以下為範例的 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 對部署 POD 的 IP 進行請求,結果如下
$ 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 有兩個屬性分別是 path 和 type,其 type 有以下詳細內容查看官方即可
- DirectoryOrCreate
- Directory
- FileOrCreate
- File
- Socket
- CharDevice
- BlockDevice
實驗的 yaml,type 使用 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 觀察它是否是跟著節點的,原則上是。此類型的儲存對於會被調度的資源則不適用。