昨天我們學會了用 PV 和 PVC 把儲存從 Pod 中抽離,讓資料在 Pod 被重建後依然可以保留。
但整個流程有一個很現實的問題:
每個 PV 都要管理員手動建立。
如果只有 3 個應用程式,手動建立 3 個 PV 還可以接受。但如果有 100 個、500 個呢?每次開發者提出儲存需求,管理員都要手動準備一個 PV,管理成本會變得非常高。
Kubernetes 的解法就是 StorageClass —— 定義一套「動態供應儲存的規則」,讓 Kubernetes 能根據 PVC 的需求自動建立對應的 PV。
這種機制稱為 Dynamic Provisioning(動態供應)
今天會學:
以下操作皆在 master 節點 執行。
先回顧昨天的流程(Static Provisioning):
管理員手動建立 PV → 開發者建立 PVC → Kubernetes 配對 → Pod 使用
這個流程的問題在於:每一個新的儲存需求都需要管理員介入準備 PV,當需求變多時,管理成本也會跟著增加。
Dynamic Provisioning 的流程則是:
管理員事先定義 StorageClass(規則)→ 開發者建立 PVC 並指定 StorageClass → Kubernetes 自動建立 PV 並完成綁定 → Pod 使用
用 Day 9 的停車場比喻:
| 比較項目 | Static Provisioning | Dynamic Provisioning |
|---|---|---|
| PV 建立方式 | 管理員手動建立 | Provisioner 依 PVC 需求自動建立 |
| StorageClass | 可選 | 通常透過 StorageClass 指定供應方式 |
| 擴展性 | PV 數量多時管理成本較高 | 適合大量、頻繁的儲存需求 |
| 管理員負擔 | 需要事先建立與管理 PV | 先設定供應規則,之後由系統自動供應 |
| 適用場景 | 特殊儲存需求、固定資源 | 一般生產環境常見的儲存供應方式 |
StorageClass 是**叢集層級(Cluster-scoped)**的資源,不屬於任何 Namespace,用來定義動態供應儲存的規則。
先看一個 StorageClass 的範例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ran-local-path
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
欄位說明:
| 欄位 | 說明 | 範例中的值 |
|---|---|---|
| provisioner | 指定由哪個 Provisioner 負責建立儲存資源 | rancher.io/local-path |
| volumeBindingMode | 決定何時進行 Volume Provisioning 與 Binding | WaitForFirstConsumer(等到有 Pod 使用 PVC 時再處理) |
| annotations | 透過 is-default-class: "true" 標記為預設 StorageClass |
storageclass.kubernetes.io/is-default-class: "true" |
💡 Provisioner 就是「誰來蓋車位」
不同的儲存後端會使用不同的 Provisioner,例如:
rancher.io/local-path:在 Node 本地建立儲存目錄,適合學習與測試ebs.csi.aws.com:在 AWS 上建立 EBS Volumeefs.csi.aws.com:透過 AWS EFS CSI Driver 提供 EFS 儲存pd.csi.storage.gke.io:在 GCP 上建立 Persistent DiskStorageClass 本身不儲存資料,它比較像一張「規格表」,告訴 Kubernetes 收到 PVC 後,要透過哪個 Provisioner、用什麼方式來供應儲存
💡
volumeBindingMode該選哪個?
Immediate:PVC 一建立就馬上建立 PV 並綁定。如果儲存只能在特定 Node 或 Zone 使用,就可能出現「PV 建在 A,但 Pod 被排程到 B」的問題。WaitForFirstConsumer:等到有 Pod 要使用這個 PVC 時,再根據 Pod 所在的位置建立並綁定 PV。對 Local Storage 或有 Topology 限制的儲存,通常建議使用
WaitForFirstConsumer。
我們的叢集是使用 kubeadm 建立的,預設不會自帶 Dynamic Provisioner。
為了實際體驗 Dynamic Provisioning,我們先安裝一個輕量的 Provisioner —— local-path-provisioner。
它會在 Node 的本機磁碟上建立儲存目錄,適合用在學習與測試環境。
它的原理很簡單:當 PVC 需要儲存空間時,會自動在對應的 Node 上建立目錄,並建立 PV 與 PVC 綁定。
概念上跟 Day 9 的 hostPath 很像,但這次 PV 不需要手動建立,而是由 Provisioner 自動完成。
Step 1:安裝 local-path-provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.30/deploy/local-path-storage.yaml
Step 2:確認安裝成功
kubectl -n local-path-storage get pod
應該會看到一個 local-path-provisioner 的 Pod 在 Running:

kubectl get storageclass
安裝完成後,會自動建立一個名為 local-path 的 StorageClass。

💡 local-path-provisioner 的儲存位置
預設情況下,local-path-provisioner 會把資料存放在 Node 的
/opt/local-path-provisioner/目錄下。每個動態建立的 PV,都會在這個目錄下建立對應的子目錄來保存資料。
現在來實際體驗一次:不用手動建立 PV,讓 Kubernetes 自動完成儲存供應
Step 1:建立 PVC,指定 StorageClass
vim pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
kubectl apply -f pvc-dynamic.yaml
注意這裡跟 Day 9 的差別:我們多了一個 storageClassName: local-path。
這代表這個 PVC 要使用 local-path StorageClass,並由對應的 Provisioner 自動建立 PV。
查看 PVC 狀態:
kubectl get pvc pvc-dynamic
會看到 PVC 的 STATUS 是 Pending,因為 local-path 的 volumeBindingMode 設為 WaitForFirstConsumer,也就是要等到真的有 Pod 使用這個 PVC 時,才會建立並綁定 PV。

💡 為什麼是
Pending?因為
local-path使用volumeBindingMode: WaitForFirstConsumer。這表示 Kubernetes 會等到有 Pod 真的要使用這個 PVC 時,才根據 Pod 要被排程到的 Node 建立 PV。
這樣就能避免「PV 在 A 節點,但 Pod 卻需要跑到 B 節點」的問題。
Step 2:建立 Pod 掛載 PVC
vim pod-dynamic.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-dynamic-demo
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: my-storage
mountPath: /usr/share/nginx/html
volumes:
- name: my-storage
persistentVolumeClaim:
claimName: pvc-dynamic
kubectl apply -f pod-dynamic.yaml
Step 3:觀察自動產生的 PV
kubectl get pvc pvc-dynamic
kubectl get pv
現在會看到:

STATUS 變成 Bound,表示已成功綁定到 PVPV 的名稱通常會由系統自動產生,例如 pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,和 Day 9 手動建立並自行命名的 pv-demo 不同。
Step 4:寫入資料並驗證
kubectl exec pod-dynamic-demo -- sh -c 'echo "Dynamic Provisioning 自動建的!時間:$(date)" > /usr/share/nginx/html/data.txt'
kubectl exec pod-dynamic-demo -- cat /usr/share/nginx/html/data.txt

跟 Day 9 一樣,你也可以刪掉 Pod 重建來驗證資料持久化:
kubectl delete pod pod-dynamic-demo
kubectl apply -f pod-dynamic.yaml
kubectl exec pod-dynamic-demo -- cat /usr/share/nginx/html/data.txt
資料依然存在!而且這次 完全不需要手動建立 PV,PV 會由 Provisioner 自動建立!

⚠️ Dynamic Provisioning 的 PV 預設回收策略通常是
Delete如果 StorageClass 沒有另外指定
reclaimPolicy,動態建立的 PV 預設會使用Delete。這表示 PVC 被刪除後,對應的 PV 與底層儲存資源通常也會一起被刪除。
如果希望保留資料,可以在 StorageClass 中設定:
reclaimPolicy: Retain
如果 PVC 沒有指定 storageClassName,Kubernetes 會使用叢集中被標記為 Default 的 StorageClass 來處理這個 PVC
查看哪個 StorageClass 是 default:
kubectl get storageclass

如果名稱後面有 (default),就代表這個 StorageClass 是叢集的預設 StorageClass。
目前我們的 local-path 還沒有被設定成 default,所以這裡暫時看不到 (default) 標記。
接下來,我們把 local-path 設定成預設 StorageClass。
設定某個 StorageClass 為 default:
kubectl patch storageclass local-path -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "true"}}}'

設定完後,PVC 就算不寫 storageClassName,也會自動使用 local-path:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-default-sc
spec:
accessModes:
- ReadWriteOnce
# 沒有指定 storageClassName,會用 default StorageClass
resources:
requests:
storage: 512Mi
⚠️ 提醒:沒有 Default StorageClass,又沒有指定
storageClassNamePVC 可能會一直停在
Pending,因為 Kubernetes 找不到可以供應或配對的儲存資源。可以用以下指令排查:
kubectl describe pvc <pvc-name>查看
Events,確認是否有找不到可用 PV、StorageClass 或 Provisioner 相關的錯誤訊息。
💡
storageClassName: ""與不寫的差別
- 不寫
storageClassName:如果叢集中有 Default StorageClass,Kubernetes 會使用它進行 Dynamic ProvisioningstorageClassName: "":明確表示不使用 StorageClass,不會觸發 Dynamic Provisioning,只會尋找符合條件、且沒有 StorageClass 的既有 PV這個差異很容易被忽略,但在排查 PVC 一直停在
Pending時非常重要。
今天我們學會了用 StorageClass 實現 Dynamic Provisioning,讓 PV 的建立完全自動化:
| 概念 | 重點 | 說明 |
|---|---|---|
| Static Provisioning | 手動建 PV | 管理員逐一建立,適合特殊需求或學習 |
| Dynamic Provisioning | 自動建 PV | PVC 提出需求後,由 Provisioner 自動建立 PV |
| StorageClass | 動態供應的規則 | 定義 Provisioner、回收策略、綁定模式等設定 |
| Default StorageClass | 預設規則 | PVC 沒指定 StorageClass 時自動使用 |
| volumeBindingMode | 綁定時機 | WaitForFirstConsumer 可避免儲存位置與 Pod 排程位置不匹配 |
到這裡,我們已經把 Kubernetes 的儲存三兄弟(PV、PVC、StorageClass)都學完了!
回顧一下整個儲存的學習路線:
有了這些基礎,接下來我們就可以處理更進階的場景。
如果應用程式不只需要「持久化儲存」,還需要「穩定的網路身份」和「固定的啟動順序」呢?
明天我們來學 StatefulSet —— Kubernetes 專門用來管理有狀態應用程式的控制器!