iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
Kubernetes

從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作系列 第 10

Day 10|StorageClass — Kubernetes 如何自動建立儲存?

  • 分享至 

  • xImage
  •  

前言

昨天我們學會了用 PV 和 PVC 把儲存從 Pod 中抽離,讓資料在 Pod 被重建後依然可以保留。

但整個流程有一個很現實的問題:

每個 PV 都要管理員手動建立。

如果只有 3 個應用程式,手動建立 3 個 PV 還可以接受。但如果有 100 個、500 個呢?每次開發者提出儲存需求,管理員都要手動準備一個 PV,管理成本會變得非常高。

Kubernetes 的解法就是 StorageClass —— 定義一套「動態供應儲存的規則」,讓 Kubernetes 能根據 PVC 的需求自動建立對應的 PV。

這種機制稱為 Dynamic Provisioning(動態供應)

今天會學:

  1. Static vs Dynamic Provisioning — 手動建立 PV 跟自動供應有什麼差別?
  2. StorageClass 是什麼? — 理解它的角色與核心欄位
  3. 安裝 local-path-provisioner — 讓學習環境也能使用 Dynamic Provisioning
  4. 實作 Dynamic Provisioning — 建立 PVC,讓 StorageClass 自動產生 PV
  5. Default StorageClass — PVC 沒指定 StorageClass 時會發生什麼事?

以下操作皆在 master 節點 執行。


一、Static vs Dynamic — 為什麼需要 StorageClass?

先回顧昨天的流程(Static Provisioning):

管理員手動建立 PV → 開發者建立 PVC → Kubernetes 配對 → Pod 使用

這個流程的問題在於:每一個新的儲存需求都需要管理員介入準備 PV,當需求變多時,管理成本也會跟著增加。

Dynamic Provisioning 的流程則是:

管理員事先定義 StorageClass(規則)→ 開發者建立 PVC 並指定 StorageClass → Kubernetes 自動建立 PV 並完成綁定 → Pod 使用

用 Day 9 的停車場比喻:

  • Static Provisioning:管理員事先準備好車位,有停車需求時再進行配對
  • Dynamic Provisioning:管理員先定義「建立車位的規則」,有人提出需求時再自動建立新的車位
比較項目 Static Provisioning Dynamic Provisioning
PV 建立方式 管理員手動建立 Provisioner 依 PVC 需求自動建立
StorageClass 可選 通常透過 StorageClass 指定供應方式
擴展性 PV 數量多時管理成本較高 適合大量、頻繁的儲存需求
管理員負擔 需要事先建立與管理 PV 先設定供應規則,之後由系統自動供應
適用場景 特殊儲存需求、固定資源 一般生產環境常見的儲存供應方式

二、StorageClass 是什麼?

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 Volume
  • efs.csi.aws.com:透過 AWS EFS CSI Driver 提供 EFS 儲存
  • pd.csi.storage.gke.io:在 GCP 上建立 Persistent Disk

StorageClass 本身不儲存資料,它比較像一張「規格表」,告訴 Kubernetes 收到 PVC 後,要透過哪個 Provisioner、用什麼方式來供應儲存

💡 volumeBindingMode 該選哪個?

  • Immediate:PVC 一建立就馬上建立 PV 並綁定。如果儲存只能在特定 Node 或 Zone 使用,就可能出現「PV 建在 A,但 Pod 被排程到 B」的問題。
  • WaitForFirstConsumer:等到有 Pod 要使用這個 PVC 時,再根據 Pod 所在的位置建立並綁定 PV。

Local Storage 或有 Topology 限制的儲存,通常建議使用 WaitForFirstConsumer


三、安裝 local-path-provisioner

我們的叢集是使用 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:

https://ithelp.ithome.com.tw/upload/images/20260812/201819286aHFdCWyo9.png

kubectl get storageclass

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

https://ithelp.ithome.com.tw/upload/images/20260812/20181928HZ3sRJwc5l.png

💡 local-path-provisioner 的儲存位置

預設情況下,local-path-provisioner 會把資料存放在 Node 的 /opt/local-path-provisioner/ 目錄下。

每個動態建立的 PV,都會在這個目錄下建立對應的子目錄來保存資料。


四、實作 Dynamic Provisioning

現在來實際體驗一次:不用手動建立 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 的 STATUSPending,因為 local-pathvolumeBindingMode 設為 WaitForFirstConsumer,也就是要等到真的有 Pod 使用這個 PVC 時,才會建立並綁定 PV。

https://ithelp.ithome.com.tw/upload/images/20260812/20181928Gj1GsDdAaa.png

💡 為什麼是 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

現在會看到:

https://ithelp.ithome.com.tw/upload/images/20260812/20181928r6i8ISEXZS.png

  • PVC 的 STATUS 變成 Bound,表示已成功綁定到 PV
  • 同時會多出一個沒有手動建立的 PV,代表 Dynamic Provisioning 已經成功自動建立儲存資源

PV 的名稱通常會由系統自動產生,例如 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

https://ithelp.ithome.com.tw/upload/images/20260812/20181928Uuneb7hdGk.png

跟 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 自動建立!

https://ithelp.ithome.com.tw/upload/images/20260812/20181928LHH6ZvoFB2.png

⚠️ Dynamic Provisioning 的 PV 預設回收策略通常是 Delete

如果 StorageClass 沒有另外指定 reclaimPolicy,動態建立的 PV 預設會使用 Delete

這表示 PVC 被刪除後,對應的 PV 與底層儲存資源通常也會一起被刪除。

如果希望保留資料,可以在 StorageClass 中設定:

reclaimPolicy: Retain

五、Default StorageClass — 不指定時會怎樣?

如果 PVC 沒有指定 storageClassName,Kubernetes 會使用叢集中被標記為 Default 的 StorageClass 來處理這個 PVC

查看哪個 StorageClass 是 default:

kubectl get storageclass

https://ithelp.ithome.com.tw/upload/images/20260812/20181928verjAMdsDa.png

如果名稱後面有 (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"}}}'

https://ithelp.ithome.com.tw/upload/images/20260812/20181928836ZHoJP24.png

設定完後,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,又沒有指定 storageClassName

PVC 可能會一直停在 Pending,因為 Kubernetes 找不到可以供應或配對的儲存資源。

可以用以下指令排查:

kubectl describe pvc <pvc-name>

查看 Events,確認是否有找不到可用 PV、StorageClass 或 Provisioner 相關的錯誤訊息。

💡 storageClassName: "" 與不寫的差別

  • 不寫 storageClassName:如果叢集中有 Default StorageClass,Kubernetes 會使用它進行 Dynamic Provisioning
  • storageClassName: "":明確表示不使用 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)都學完了!

回顧一下整個儲存的學習路線:

  • Day 9:發現問題(Pod 資料會消失)→ 解法:PV + PVC(Static Provisioning)
  • Day 10:發現問題(手動建 PV 太麻煩)→ 解法:StorageClass(Dynamic Provisioning)

有了這些基礎,接下來我們就可以處理更進階的場景。

如果應用程式不只需要「持久化儲存」,還需要「穩定的網路身份」和「固定的啟動順序」呢?

明天我們來學 StatefulSet —— Kubernetes 專門用來管理有狀態應用程式的控制器!


參考資源


上一篇
Day 09|PV 與 PVC — Kubernetes 如何讓「無狀態 Pod」支援「有狀態資料」
下一篇
Day 11|StatefulSet — 讓有狀態應用穩定運行
系列文
從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言