為什麼需要 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 需要共享同一份資料時,必須選擇支援 ReadOnlyManyReadWriteMany 的 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.yaml
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
pvc-c197c285-d314-43db-8cbc-6f912d8a9680   1Gi        RWO            Delete           Bound    default/pvc-demo   hostpath                2s
kubectl get pvc
NAME       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-demo   Bound    pvc-c197c285-d314-43db-8cbc-6f912d8a9680   1Gi        RWO            hostpath       11s

PV 與 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:80
curl 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