主線

KAI K8s之路 06|Volume / PersistentVolume 是什麼?Pod 會換,但資料不能跟著消失

ConfigMap 和 Secret 把設定從 image 拆出去,但 workload 還會產生資料。這篇把 Volume、PersistentVolume、PersistentVolumeClaim 和 StorageClass 串起來:Volume 是 Pod 的掛載位置,PV/PVC 是把需要留下的資料從 Pod 生命週期裡拆出來。

中文 主線 第 6 篇
先抓住

Volume 是 Pod 裡可掛載的資料位置;PV/PVC 是把需要長住的資料,從 Pod 生命週期裡拆出來。

讀完帶走

先分清 emptyDir 和 PersistentVolume:前者跟著 Pod 走,後者透過 PVC 被 workload 申請和掛載。排查時先看 PVC 是否 Bound,再看 StorageClass、access mode、Events 和 mount 錯誤。

上一篇我們把 ConfigMap / Secret 想清楚了:

image 應該裝程式,不應該裝環境決策和秘密。

但 workload 不只會讀設定。

它也可能會寫東西:

  • 上傳檔案
  • cache
  • log buffer
  • queue checkpoint
  • database data

這時候問題就來了:

Pod 會被重建,container 會被重啟,node 也可能會換。

如果資料只是寫在 container 自己的 filesystem 裡,它跟這個 container 的命運綁得太死。

我會先記這句:

Volume 是 Pod 裡可掛載的資料位置;PV/PVC 是把需要長住的資料,從 Pod 生命週期裡拆出來。

KAI notebook style diagram showing that a Pod is temporary while data should have its own lifecycle through Volumes, PVs, and PVCs.
Pod 會被重建,container 會重啟;真正有業務意義的資料,不應該只活在某個 container 的可寫層裡。

先不要把 container filesystem 當硬碟

container image 給你一個乾淨的起點。

process 跑起來之後,當然可以在 filesystem 裡寫檔。

但這不代表你得到了一顆穩定硬碟。

container crash 後重新拉起來,container 自己那層可寫狀態不應該被拿來當可靠資料層。

Pod 被刪掉、搬走、重建時,更不能期待寫在原本位置的資料自然跟著回來。

這也是為什麼 Kubernetes 要有 Volume。

它先解決一個很基本的問題:

這個 Pod 裡的 container 要把資料寫到哪個被明確掛載的位置?

然後 PersistentVolume / PersistentVolumeClaim 再解決下一個問題:

如果 Pod 不在了,這份資料還要不要存在?由誰提供?由誰申請?

用租倉庫想 Volume / PV / PVC

我會用租迷你倉來記這章。

Pod 像一個臨時工作間。

你今天進去工作,桌子、椅子、工具都在裡面;做完可能整個工作間會被清掉,明天換一間。

如果你只是臨時放草稿紙,放在桌面就可以。

這有點像 emptyDir:Pod 存在時可以用,container 重啟時還在,但 Pod 從 node 上被移走後,裡面的東西就沒了。

如果你放的是重要材料,就不能只放臨時工作間。

你會去租一格倉庫。

倉庫本身像 PersistentVolume:cluster 裡真正存在的一塊儲存能力。

租倉庫的申請單像 PersistentVolumeClaim:workload 不需要知道倉庫後面是什麼牌子的硬碟、哪個雲供應商、哪個儲存系統,它只說自己需要多少容量、什麼讀寫模式。

倉庫套餐像 StorageClass:標準倉、快速倉、跨區倉,不同 class 背後有不同 provisioner 和策略。

這個例子不要背太滿。

只要記住一件事:

Pod 是工作間;PVC 是租倉庫的單;PV 是真的倉庫。

Volume 先是一個掛載位置

Kubernetes 的 Volume,不一定都是「永久硬碟」。

它的第一層意思很樸素:

Volume 是 Pod spec 裡宣告的一份資料來源,container 再把它 mount 到自己的路徑。

所以一個 Pod 裡會看到兩個地方:

  • .spec.volumes:這個 Pod 有哪些 volume
  • .spec.containers[*].volumeMounts:每個 container 要把哪些 volume 掛到哪個路徑

同一個 volume 可以被同一個 Pod 裡多個 container 使用。

但每個 container 要不要掛、掛到哪裡,是各自宣告的。

這點很重要。

Volume 不是神秘背景設定。

它是一個被 mount 進 container filesystem 的明確路徑。

emptyDir 解決的是 Pod 內臨時共享

emptyDir 很適合拿來建立第一個直覺。

它在 Pod 被分配到 node 時建立,一開始是空的。

Pod 裡的 container 可以一起讀寫它。

container crash 不會清掉 emptyDir,因為 Pod 還在。

但 Pod 從 node 上被移除時,emptyDir 裡的資料會被永久刪掉。

所以它適合:

  • 暫存檔
  • cache
  • container 之間共享一段工作資料
  • 可重算的中間結果

它不適合:

  • 用戶上傳檔案的唯一副本
  • database data
  • 不能重建的 queue 狀態
  • 任何你明天還需要的真資料

一句話:

emptyDir 可以跨 container restart,不可以跨 Pod 消失。

PV / PVC 解決的是資料生命週期

如果資料要比 Pod 活得久,就要進到 PersistentVolume 這層。

我不會把 PV/PVC 記成兩個很像的縮寫。

我會把它們分工記清楚:

  • PersistentVolume:cluster 裡的一塊儲存資源,可以是管理員預先建立,也可以由 StorageClass 動態建立
  • PersistentVolumeClaim:namespace 裡的申請,描述 workload 想要多少容量、哪種 access mode、哪個 storage class
  • StorageClass:儲存套餐和 provisioner,決定動態建立 PV 時用什麼後端和策略

Pod 不會直接說:「我要那顆雲硬碟。」

Pod 會說:「我要使用這個 PVC。」

Kubernetes 再透過 PVC 找到已綁定的 PV,然後把它 mount 給 Pod。

這個抽象很 Kubernetes:

workload 描述需求。

cluster 負責把需求綁到實際資源。

一個簡化 PVC 例子

先看 PVC。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: web-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 10Gi

這段 YAML 的意思不是「建立一個資料夾」。

它是在說:

我需要一份 10Gi 的儲存,使用 standard 這個 StorageClass,讀寫模式是 ReadWriteOnce

然後 Pod 或 Deployment 可以引用這個 claim:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: ghcr.io/example/web:1.0.0
          volumeMounts:
            - name: web-data
              mountPath: /var/lib/web
      volumes:
        - name: web-data
          persistentVolumeClaim:
            claimName: web-data

這裡的重點是責任切分:

  • PVC 描述要什麼 storage
  • Deployment 描述 workload 要把它 mount 到哪裡
  • app 只知道自己寫 /var/lib/web
  • storage 後面的細節交給 cluster 和 StorageClass
KAI notebook style PVC, StorageClass, and PV binding flow: a workload requests storage with a PVC, the StorageClass provisions, and the PV becomes Bound and mounted.
PVC 是申請單,StorageClass 是套餐,PV 是真的儲存能力。workload 描述需求,cluster 負責綁到實際資源。

Kubernetes 實際在做什麼

這裡給一個夠用的版本。

  1. 你建立 PVC,說明容量、access mode、StorageClass。
  2. control plane 會找符合條件的 PV,或透過 StorageClass 動態 provision 一個新的 PV。
  3. PVC 和 PV 綁定後,PVC 狀態會變成 Bound
  4. Pod 引用 PVC 作為 volume。
  5. kubelet 在 Pod 被排到某個 node 後,請儲存後端把 volume attach / mount 到 container 指定路徑。
  6. Pod 消失後,PV 如何處理資料,取決於 reclaim policy、storage backend,以及你有沒有先做備份。

這裡有幾個邊界要講清楚。

第一,PVC Bound 不代表 app 一定能寫。

後面還可能卡在 node topology、attach、mount permission、filesystem、driver、quota。

第二,ReadWriteOnce 不是「只能一個 Pod 用」的萬能鎖。

它的意思是 volume 可以被單一 node 以 read-write 掛載;如果你真的要限制成整個 cluster 只有一個 Pod 讀寫,要看 ReadWriteOncePod 以及 CSI driver 支援。

第三,reclaim policy 不是備份策略。

很多動態建立的 PV 會跟著 StorageClass 的 reclaim policy 走;常見預設可能是 Delete

刪 PVC 前,先知道背後資料會被保留還是被刪。

新手最常踩的坑

1) 以為用了 Volume 就一定永久

Volume 是抽象,不是永久保證。

emptyDirconfigMapsecret、PVC-backed volume,生命週期完全不同。

先問 volume type,再談資料會不會留下來。

2) 把 PVC 當成資料本身

PVC 是 claim,不是資料內容。

它是 workload 對儲存資源的申請和引用點。

真正的資料在背後的 PV / storage backend。

3) Deployment 多副本共用同一份 RWO PVC

這很容易變成調度或 mount 問題。

就算某些情況下多個 Pod 落在同一個 node 能碰到同一份 RWO volume,也不代表 app 層資料一致性就安全。

如果每個副本都需要穩定身份和自己的資料,下一章就要看 StatefulSet。

4) 只看 Pod,不看 PVC

Pod 卡在 ContainerCreating,不一定是 image 問題。

可能是 PVC 還在 Pending,或 volume attach / mount 失敗。

5) 刪 PVC 前沒看 reclaim policy

有些資料刪了就是真的刪了。

Kubernetes 會幫你管理 storage object 的生命週期,但不會替你承擔備份策略。

我會怎麼 inspect

如果有人說「Pod 起不來,好像卡住了」,而這個 workload 有 volume,我不會只看 logs。

我會先查這幾層:

kubectl get pvc -n <ns>
kubectl describe pvc <pvc-name> -n <ns>
kubectl get pv
kubectl describe pv <pv-name>
kubectl get storageclass
kubectl describe pod <pod-name> -n <ns>
kubectl get events -n <ns> --sort-by=.lastTimestamp

我會重點看:

  • PVC 是 Pending 還是 Bound
  • PVC request 的 capacity / access mode / StorageClass 是否合理
  • PV 的 reclaim policy 是 Delete 還是 Retain
  • Pod Events 裡有沒有 attach / mount / permission 錯誤
  • storage backend 是否有 topology 限制
  • 多副本 workload 是否共用了一份不適合共用的 PVC
KAI notebook style Kubernetes storage troubleshooting path: check PVC, PV, StorageClass, Events, and Pod mount or attach errors.
Pod 卡住時,不要先怪 image。先看 PVC 是否 Bound,再沿著 PV、StorageClass、Events 和 mount 錯誤走一圈。

一句話版本:

看到 storage 問題,不要只查 Pod;先沿著 PVC -> PV -> StorageClass -> Events 走一圈。

我對 Volume 的記法

我不會把這章記成「Kubernetes 有很多種 volume type」。

那樣會很快變成背清單。

我會這樣記:

Volume 是 workload 和資料之間的接縫;PV/PVC 是把這條接縫從 Pod 壽命裡拆出來。

Pod 可以重建。

container 可以重啟。

node 可以替換。

但資料如果有業務意義,就不能只是「剛好寫在某個 container 裡」。

它要有自己的生命週期、申請方式、掛載位置、回收策略和備份邊界。

這件事後面會反覆出現。

因為只要你開始跑 stateful workload,就會發現 Kubernetes 最難的不是「起一個 process」。

難的是:process 可以換,身份和資料不能亂。

這篇先記住三件事

  1. Volume 是 Pod 內明確掛載的資料位置,不等於永久儲存
  2. emptyDir 跟 Pod 走;PV/PVC 才是把資料生命週期從 Pod 裡拆出來
  3. 排查 storage 問題時,先看 PVC 是否 Bound,再查 PV、StorageClass、Events 和 mount 錯誤

下一篇會接著看:StatefulSet 是什麼?當每個 Pod 都需要穩定名字和自己的資料,Deployment 就不再是最舒服的工具。

技術校對參考: