K8s · 設定與儲存 · 第 27 課 · · 10min read

K8s PV PVC 是什麼?Pod 重啟資料不消失的存法

K8s PV 和 PVC 是什麼?Pod 重啟資料就消失,PV 是實體儲存、PVC 是申請單,兩個搭配讓資料活在 Pod 之外,附建立流程。

#Kubernetes #PV #PVC #儲存 #持久化
章節目錄 · 11

Pod 重啟資料就消失

到目前為止你部署的所有東西都是無狀態的:nginx、frontend、API server。Pod 死了再起一個一模一樣的就好。

資料庫不行

上一篇整合實作裡的 MySQL 一刪 Pod、資料就全部不見。為什麼?

容器的檔案系統是短暫的。Pod 刪除後,容器內所有寫入的檔案(包括 MySQL 的 /var/lib/mysql 資料)跟著消失。下次 Pod 重啟,是從乾淨的 Image 重新開始。

$ kubectl exec mysql-pod -- mysql -e "INSERT INTO users VALUES('Alice');"
$ kubectl delete pod mysql-pod
$ kubectl exec mysql-pod -- mysql -e "SELECT * FROM users;"
ERROR: Table 'users' doesn't exist     # ← 整個資料庫都沒了

要保住資料,需要 K8s 的持久化儲存:PV + PVC

PV vs PVC:停車位的比喻

K8s 把儲存拆成兩個資源:

  • PersistentVolume(PV) — 一塊實際的儲存空間(管理員建)
  • PersistentVolumeClaim(PVC) — Pod 對儲存的申請單(開發者寫)
用停車場當比喻:
┌── 停車場(K8s 管理員)─────────────────────┐
│  PV-1: 10GB SSD                           │
│  PV-2: 50GB HDD                           │
│  PV-3: 5GB SSD                            │
└──────────────────────────────────────────┘
                    ↑ 配對(Binding)
┌── 開發者 ─────────────────────────────────┐
│  PVC-A: "我要 5GB"     → 配到 PV-3        │
│  PVC-B: "我要 10GB"    → 配到 PV-1        │
└──────────────────────────────────────────┘
                    ↓
                Pod 用 PVC 掛載

為什麼要拆兩個? 職責分離。

  • 基礎架構團隊管 PV:「公司有幾台 NAS、幾顆 SSD、各自多大」
  • 應用開發團隊寫 PVC:「我的 App 要 10GB ReadWriteOnce」
  • 開發者不需要知道底層是 NFS 還是 SSD,反正 K8s 會配給你

對照 Docker

DockerKubernetes
docker volume create mydata建 PV
docker run -v mydata:/var/lib/mysqlPVC + Pod volumeMounts
自動建 + 掛載合在一起拆成兩步(PV / PVC 分離)
Docker 把建 volume 和掛 volume 合成一步,K8s 拆開是為了大型組織的職責分離。小團隊覺得麻煩 → 用 StorageClass 自動建 PV,下一篇講。

PV YAML

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
spec:
  capacity:
    storage: 2Gi                    # 這塊 PV 有 2GB
  accessModes:
    - ReadWriteOnce                 # 只允許一個 Node 同時讀寫
  persistentVolumeReclaimPolicy: Retain   # PVC 刪了資料保留
  storageClassName: manual          # 給 PVC 配對用的標籤
  hostPath:
    path: /tmp/k8s-pv-data          # 用 Node 本機目錄當儲存

幾個關鍵欄位:

accessModes:誰能讀寫?

Mode縮寫行為適合
ReadWriteOnceRWO一個 Node 可讀寫資料庫(最常用)
ReadOnlyManyROX多個 Node 可唯讀靜態檔案
ReadWriteManyRWX多個 Node 可讀寫共享檔案(需 NFS)
⚠️ RWO 是「同時間一個 Node」不是「一個 Pod」 — 同 Node 上的多個 Pod 可以共用。

persistentVolumeReclaimPolicy:PVC 刪了 PV 怎辦?

Policy行為適合
RetainPV 和資料保留,管理員手動處理生產環境(資料不能丟)
DeletePV 和資料一起砍開發環境 / 雲端(PVC 刪 EBS 磁碟也刪,省錢)
正式環境永遠用 Retainkubectl delete pvc 不小心打錯就 game over 了。

hostPath(學習用)

hostPath 用 Node 本機的某個目錄當儲存。只能在學習 / 單機叢集用,因為:

  • 多 Node 叢集,Pod 換到別台 Node 就找不到資料
  • Node 本身掛掉,資料就跟著沒
  • 沒做容錯 / 備份
正式環境用 NFS / Ceph / 雲商磁碟(GCE PD / AWS EBS)。

PVC YAML

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc
spec:
  accessModes:
    - ReadWriteOnce               # ← 跟 PV 對應
  resources:
    requests:
      storage: 1Gi                # 我要 1GB(PV 有 2GB,夠)
  storageClassName: manual        # ← 跟 PV 對應

PVC 不指定 PV 名字,K8s 會看 storageClassName + accessModes + 容量去自動配對

配對流程

$ kubectl apply -f pv.yaml
$ kubectl apply -f pvc.yaml

$ kubectl get pv,pvc
NAME CAPACITY ACCESS MODES STATUS CLAIM
pv/local-pv 2Gi RWO Bound default/local-pvc

NAME STATUS VOLUME CAPACITY
pvc/local-pvc Bound local-pv 2Gi

STATUS: Bound = 配對成功。注意:

  • PVC 申請 1GB,但配到的 PV 是 2GB → 整顆 PV 都歸 PVC 用(一夫一妻制)
  • 想要剛好 1GB?多建一個 1GB PV,K8s 自動挑最接近的

Pod 用 PVC

apiVersion: v1
kind: Pod
metadata:
  name: mysql-with-pvc
spec:
  containers:
    - name: mysql
      image: mysql:8.0
      env:
        - name: MYSQL_ROOT_PASSWORD
          value: rootpw
      volumeMounts:
        - name: data
          mountPath: /var/lib/mysql   # MySQL 資料目錄
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: local-pvc          # ← 掛 PVC

容器內的 /var/lib/mysql 寫入會落到 PVC 對應的 PV 實體儲存。

驗證資料持久化

# 1. 寫資料
$ kubectl exec mysql-with-pvc -- mysql -prootpw -e "
  CREATE DATABASE shop;
  USE shop;
  CREATE TABLE users(name VARCHAR(20));
  INSERT INTO users VALUES('Alice');
"

# 2. 砍 Pod
$ kubectl delete pod mysql-with-pvc

# 3. 重建 Pod(同一個 PVC)
$ kubectl apply -f mysql-pod.yaml

# 4. 看資料
$ kubectl exec mysql-with-pvc -- mysql -prootpw -e "USE shop; SELECT * FROM users;"
+-------+
| name |
+-------+
| Alice | # ← 資料還在!
+-------+

對照沒掛 PVC 的版本:資料消失 vs 資料還在。這就是 PV / PVC 的價值

排錯

STATUS意思怎麼處理
Bound配對成功正常
PendingPVC 找不到合適 PV檢查 storageClassName / 容量是否匹配
LostPV 不見了PV 被手動刪除?hostPath 目錄被砍?
Pending 最常見,看一下原因:
$ kubectl describe pvc local-pvc
Events:
  Warning  ProvisioningFailed  no available volume plugin matches

通常是:

  • storageClassName 跟 PV 對不上

  • 沒有 PV 容量夠大

  • accessModes 不一致
  • 重點整理

    • 容器是無狀態的,要保資料必須用 PV
    • PV = 實體儲存(管理員建),PVC = 申請單(開發者寫)
    • K8s 自動配對(看 storageClassName / accessModes / 容量)
    • accessModes 90% 用 ReadWriteOnce
    • 生產用 Retain永遠不要用 Delete
    • hostPath 只能學習用,正式用 NFS / 雲商磁碟

    下一步

    PV 是手動建的 — 但工作上資料庫要 5GB、API 服務要 10GB、Cache 要 2GB...每個都管理員手動建,太累了。

    下一篇:StorageClass + StatefulSet 自動建 PV

    📅 下一篇StorageClass + StatefulSet 自動建 PV
    手動建 PV 太累了——StorageClass 動態配置 + StatefulSet 跑 MySQL,一條龍做完。
    📚 完整系列總覽K8s 系列教學首頁(共 40 課,按學習路徑順序排)