Kubernetes 的卷是 pod 的 一 個組成部分,並和 pod 共享相同的生命週期。
一、通過卷在容器之間共享數據
1.1. 使用 emptyDir 卷
創建一個 pod 中有兩個共用同一個卷的容器:
# fortune-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: luksa/fortune
name: html-generator
volumeMounts: # 名爲 html 的卷掛載在容器的 /var/htdocs 中
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts: # 名爲 html 的卷掛載在容器的 /usr/share/nginx/html 中,並且只讀
- name: html
mountPath: /usr/share/nginx/html # Nginx 服務的默認服務文件目錄
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html # 一個名爲 html 的單獨 emptyDir 卷,掛載在上面的兩個容器中
emptyDir: {}
luksa/fortune 每隔 10 秒鐘隨機寫入一段文字到 /var/htdocs/index.html 中;nginx:alpine 是一個 Nginx 鏡像。
kubectl create -f fortune-pod.yaml
使用端口轉發查看 pod 狀態:
kubectl port-forward fortune 8080:80
作爲捲來使用的 emptyDir 是在承載 pod 的工作節點的實際磁盤上創建的,因此其性能取決於節點的磁盤類型。我們可以通知 Kubernetes 在 tmfs 文件系統(存在內存而非硬盤)上創建 emptyDir:
volumes:
- name: html
emptyDir:
medium: Memory
1.2. 使用 Git 倉庫作爲存儲卷
gitRepo 卷本質上也是 emptyDir 卷,它通過克隆 Git 倉庫並在 pod 啓動時(但在創建容器之前)檢出特定版本來填充數據。
apiVersion: v1
kind: Pod
metadata:
name: gitrepo-volume-pod
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
gitRepo:
repository: https://github.com/luksa/kubia-website-example.git
revision: master
directory: . # 將 repo 克隆到卷的根目錄,如果不設置將會被克隆到 kubia-website-example 目錄
二、訪問工作節點文件系統上的文件
大多數 pod 應該忽略它們的主機節點,因此它們不應該訪問節點文件系統上的任何文件。但是某些系統級別的 pod (切記,這些通常由 DaemonSet 管理)確實需要讀取節點的文件或使用節點文件系統來訪問節點設備。Kubernetes 通過 hostPath 卷實現了這一點。
hostPath 卷是我們介紹的第一種類型的持久性存儲,因爲gitRepo 和 emptyDir 卷的內容都會在 pod 被刪除時被刪除,而 hostPath 卷的內容則不會被刪除。如果你正在考慮使用 hostPath 卷作爲存儲數據庫數據的目錄,請重新考慮。因爲卷的內容存儲在特定節點的文件系統中,所以當數據庫 pod 被重新安排在另一個節點時,會找不到數據。這解釋了爲什麼對常規 pod 使用 hostPath 卷不是一個好主意,因爲這會使 pod 對預定規劃的節點很敏感。
三、使用持久化存儲
當運行在一個 pod 中的應用程序需要將數據保存到磁盤上,並且即使該 pod 重新調度到另一個節點時也要求具有相同的數據可用。這就不能使用到目前爲止我們提到的任何卷類型,由於這些數據需要可以從任何集羣節點訪問,因此必須將其存儲在某種類型的網絡存儲 (NAS) 中。
如果使用 Google Kubernetes Engine 可以使用 GCE (Google Compute Engine) 卷;如果使用 AWS EC2 可以使用 awsElasticBlockStore 卷;如果使用 Azure 可以使用 azureFile 或者 azureDisk 卷。
四、從底層存儲技術解耦 pod
到目前爲止,我們探索過的所有待久卷類型都要求 pod 的開發人員瞭解集羣中可用的真實網絡存儲的基礎結構。例如,要創建支持 NFS 協議的卷,開發人員必須知道 NFS 節點所在的實際服務器。這違背了 Kubernetes 的基本理念,這個理念旨在嚮應用程序及其開發人員隱藏真實的基礎設施,使他們不必擔心基礎設施的具體狀
態,並使應用程序可在大量雲服務商和數據企業之間進行功能遷移。
4.1. 持久卷、持久卷聲明
在 Kubernetes 集羣中爲了使應用能夠正常請求存儲資源,同時避免處理基礎設施細節,引入了兩個新的資源, 分別是持久卷(PersistentVolume 簡稱 PV)和持久卷聲明(PersistentVolumeClaim 簡稱 PVC),這名字可能有點誤導,因爲正如在前面幾節中看到的,甚至常規的 Kubernetes 卷也可以用來存儲持久性數據。
當集羣用戶需要在其 pod 中使用持久化存儲時,他們首先創建持久卷聲明清單,指定所需要的最低容量要求和訪問模式,然後用戶將持久卷聲明清單提交給 Kubernetes API 服務器,Kubernetes 將找到可匹配的持久卷並將其綁定到持久卷聲明。
4.1.1. 創建持久卷
本文使用的依然是 Minikube:
# mongodb-pv-hostpath.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodb-pv
spec:
capacity:
storage: 1Gi # 定義 PersistentVolume de 大小
accessModes: # 可以被單個客戶端掛在爲讀寫模式或者被多個客戶端掛在爲只讀模式
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain # 當聲明被釋放後,PersistentVolume 將會被保留(不清理和刪除)
hostPath:
path: /tmp/mongodb
kubectl create -f mongodb-pv-hostpath.yaml
查看:
kubectl get pv
注意:持久卷不屬於任何命名空間,它跟節點一樣是集羣層面的資源。
4.1.2. 創建持久卷聲明
假設現在需要部署一個需要持久化存儲的 pod,將要用到之前創建的持久卷,但是不能直接在 pod 內使用,需要先聲明一個。聲明一個持久卷和創建一個 pod 是相對獨立的過程,因爲即使 pod 被重新調度(重新調度意味着先前的 pod 被刪除並且創建了一個新的 pod),我們也希望通過相同的持久卷聲明來確保可用。
# mongodb-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc # 聲明的名稱,稍後將聲明當作 pod 的卷使用時需要用到
spec:
resources:
requests:
storage: 1Gi # 申請 1 GiB 的存儲空間
accessModes:
- ReadWriteOnce # 允許單個客戶端訪問(同時支持讀取和寫入操作)
storageClassName: "" # 將空字符串指定爲存儲類名可確保 PVC 綁定到預先配置的 PV, 而不是動態配置新的 PV
kubectl create -f mongodb-pvc.yaml
查看:
kubectl get pvc
可以看到 PVC 已經與持久卷的 mongodb-pv 綁定。
訪問模式:
- RWO: ReadWriteOnce - 僅允許單個節點掛載讀寫
- ROX: ReadOnlyMany - 允許多個節點掛載只讀
- RWX: ReadWriteMany - 允許多個節點掛載讀寫
訪問模式涉及可以同時使用卷的工作節點的數量而非 pod 的數量。
持久卷是集羣範圍的,因此不能在特定的命名空間中創建,但是持久卷聲明又只能在特定的命名空間創建,所以持久卷和持久卷聲明只能被同一命名空間內的 pod 創建使用。
4.1.3. 在 pod 中使用持久卷聲明
# mongodb-pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
containers:
- image: mongo
name: mongodb
volumeMounts:
- name: mongodb-data
mountPath: /data/db
ports:
- containerPort: 27017
protocol: TCP
volumes:
- name: mongodb-data
persistentVolumeClaim:
claimName: mongodb-pvc # 在 pod 卷中通過名稱引用持久卷聲明
五、持久卷的動態卷配置
5.1. 通過 StorageClass 資源定義可用存儲類型
# storageclass-fast-hostpath.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: k8s.io/minikube-hostpath # 用戶配置持久卷的卷插件
parameters:
type: pd-ssd
kubectl create -f storageclass-fast-hostpath.yaml
5.2. 請求持久卷聲明中的存儲類
創建一個請求特定存儲類的 PVC 定義
# mongodb-pvc-dp.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc-fast
spec:
storageClassName: fast # 請求自定義存儲類
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnce
kubectl create -f mongodb-pvc-dp.yaml
查看:
kubectl get pvc mongodb-pvc-fast
VOLUME 列顯示了與此聲明綁定的持久卷,執行命令,可以看到自動創建了一個新的 PV:
kubectl get pv
5.3. 不指定存儲類的動態配置
列出存儲類:
kubectl get sc # sc 是 storageclass 的簡寫
查看默認存儲類:
kubectl get sc standard -o yaml
創建一個沒有指定存儲類別的持久卷聲明:
# mongodb-pvc-dp-nostorageclass.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc2
spec:
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnce
kubectl create -f mongodb-pvc-dp-nostorageclass.yaml
查看:
kubectl get pvc mongodb-pvc2