為什麼需要 PV 與 PVC#
當服務需要保留狀態(Stateful)時,最關鍵的議題就是:「資料在哪裡?」Persistent Volume(PV)與 Persistent Volume Claim(PVC)的核心精神在於:
- PV 的生命週期獨立於 Pod 之外,所以 Pod 被銷毀或擴展時資料仍然保留。
- 資料儲存的位置從 Pod 中被「抽出來」,改放到 NFS、節點的本機磁碟、雲端儲存等獨立的儲存空間。
如此就能實作出資料的獨立生命週期。
Persistent Volume(PV)#
Kubernetes 透過 PV 提供一個抽象的儲存空間。PV 有靜態與動態兩種供應方式:
- 靜態:由系統管理者預先建立 PV,使用者再透過 PVC 取用。
- 動態:PVC 中指定 Storage Class 時,Kubernetes 會根據 Storage Class 的設定動態建立對應的 PV。
整體關係可以這樣理解:
- 系統管理者宣告/提供儲存空間(PV)。
- 應用使用者透過 PVC 來請求儲存空間。
Persistent Volume Claim(PVC)#
PVC 表示「使用者對儲存資源的請求」。如同 Pod 可以對 Node 請求 CPU 與記憶體,PVC 則可以請求:
- 儲存空間大小。
- 存取模式(Access Mode),例如 ReadWriteOnce、ReadOnlyMany、ReadWriteMany。
PVC 建立後,Kubernetes 會去找一個符合條件的 PV 來綁定(Bound);若一直找不到匹配的 PV,PVC 會無限期停在 Pending 狀態,直到出現符合條件的 PV 為止。
動手建立 PVC#
# pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-demo
spec:
accessModes:
- ReadWriteOnce
storageClassName: hostpath
resources:
requests:
storage: 1Gi這份 PVC 會搭配 hostpath 這個 Storage Class 動態產生對應的 PV。
存取模式(Access Mode)#
spec.accessModes 常見的選項:
ReadWriteOnce:可被單一節點以讀寫方式掛載;同節點上的多個 Pod 仍可共同存取。ReadOnlyMany:可被多個節點以唯讀方式掛載。ReadWriteMany:可被多個節點以讀寫方式掛載。ReadWriteOncePod:整個叢集內僅允許單一 Pod 以讀寫方式掛載。
這在多節點生產環境很重要(例如 Google GKE、AWS EKS)。當不同節點上的 Pod 需要共享同一份資料時,必須選擇支援 ReadOnlyMany 或 ReadWriteMany 的 Provisioner,這類 Provisioner 多半建立在雲端 NFS 服務之上。
即使是相同服務的 Pod,Kubernetes 的排程器也不一定會把它們放在同一個節點上,除非透過
nodeSelector等顯式設定。
Storage Class#
spec.storageClassName 決定動態製備時要使用哪個 Storage Class。在 docker-desktop 中預設的 Storage Class 是 hostpath,從名字就能看出它把儲存空間設置在節點的本機路徑上。因此在單節點的 docker-desktop 環境中,多個 Pod 之間仍可共享同一份資料。
幾個重點:
- 若 PVC 沒有指定 Storage Class,會使用叢集預設的 Storage Class。
- 若 PVC 指定
storageClassName: "",則代表「禁止」動態製備。 - 動態製備需要管理者已經建立並設定好對應的 Storage Class。
部署 PVC 並查看結果#
kubectl apply -f ./pvc.yamlkubectl get pvNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-c197c285-d314-43db-8cbc-6f912d8a9680 1Gi RWO Delete Bound default/pvc-demo hostpath 2skubectl get pvcNAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-demo Bound pvc-c197c285-d314-43db-8cbc-6f912d8a9680 1Gi RWO hostpath 11sPV 與 PVC 都已綁定(Bound)並設定了對應的資源。
PV 的狀態(STATUS)#
Available:PV 可用,尚未綁定。Bound:已被某個 PVC 綁定。Released:PVC 被刪除,但資源尚未回收。Failed:回收動作失敗。
回收策略(Reclaim Policy)#
Retain:手動回收,由管理員自行處理。Recycle(已棄用):原本透過rm -rf /thevolume/*清空,現在建議改用動態製備。Delete:刪除 PV 的同時也刪除背後的儲存資源。
用 PV/PVC 串接兩個獨立的 Pod#
把先前 EmptyDir 範例中的兩個容器拆成獨立的 Pod,並讓它們同時掛載同一個 PVC,藉此驗證 PV/PVC 的生命週期確實獨立於 Pod。
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: pvc-demo
readOnly: false# alpine-pod.yaml(結構與上方 nginx-pod 類似,差別在容器與寫入命令)
apiVersion: v1
kind: Pod
metadata:
name: alpine-pod
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh", "-c"]
args:
- while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done
volumeMounts:
- { name: html, mountPath: /html }
volumes:
- name: html
persistentVolumeClaim:
claimName: pvc-demo部署兩個 Pod:
kubectl apply -f ./nginx-pod.yaml ./alpine-pod.yaml透過 Nginx 驗證共享資料#
kubectl port-forward pod/nginx-pod 8080:80curl http://localhost:8080回傳內容會持續長出由 alpine-pod 寫入的時間戳記行,例如:
alpine-pod Sat Aug 6 03:31:50 UTC 2022
alpine-pod Sat Aug 6 03:32:00 UTC 2022
alpine-pod Sat Aug 6 03:32:10 UTC 2022證明兩個獨立 Pod 透過同一個 PVC 共享了同一份資料。
小結#
PV 與 PVC 涉及的觀念較廣,理解時最好搭配實務情境一起想:
- Stateless 服務交給 Kubernetes 動態調度;需要持久化的資料抽出來,由 PV/PVC(或外部 API 服務)統一管理。
- 在多節點雲端環境(例如 GKE、EKS)中,必須留意 Storage Class 與 Access Mode 的支援度,例如:
- Google FileStore 可實現 ReadWriteMany 的雲端 NFS。
- Google Compute Engine Disk 可實現 ReadWriteOnce 的雲端硬碟。
理解 PV、PVC、Storage Class、Access Mode 與 Reclaim Policy 之間的關係,是把 Kubernetes 帶入有狀態服務時的必要基礎。
原文出處#
- GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day20
- iThome:https://ithelp.ithome.com.tw/articles/10294662