存儲卷介紹
一個容器的對文件系統的寫入都是發生在文件系統的可寫層的,一旦該容器運行結束,所有寫入數據都會被丟棄。在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
其它類型的存儲就不詳細介紹了。