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
觀察它是否是跟著節點的,原則上是。此類型的儲存對於會被調度的資源則不適用。