剛用 Kubernetes(K8s)部署應用時,我踩過一個致命坑:用默認存儲部署的數據庫 Pod 意外重啓後,數據全丟了。原來 K8s 的默認存儲是“臨時存儲”,Pod 銷燬後數據會跟着消失。後來才知道,要實現數據持久化,必須用 PV(PersistentVolume,持久卷)和 PVC(PersistentVolumeClaim,持久卷聲明)——它們就像 K8s 集羣的“共享硬盤”和“硬盤租用申請”,讓數據獨立於 Pod 生命週期,即使 Pod 重建,數據也能完好保留。這篇就從實戰角度,教你快速掌握 PV/PVC 的核心用法。
一、核心認知:為什麼需要 PV/PVC?
K8s 中 Pod 是臨時的,默認存儲(emptyDir、configMap 等)會隨 Pod 銷燬而丟失,無法滿足數據庫、文件存儲等場景的持久化需求。PV 和 PVC 正是為解決這個問題而生,它們的核心邏輯是“存儲與使用分離”:
- PV(持久卷):集羣級別的“共享存儲資源”,由管理員提前創建,獨立於 Pod 存在,支持本地磁盤、NFS、雲存儲等多種存儲類型;
- PVC(持久卷聲明):Pod 對存儲資源的“租用申請”,由開發人員創建,聲明需要的存儲大小、訪問模式等,K8s 會自動匹配符合條件的 PV 進行綁定;
- 核心價值:開發人員無需關心存儲底層實現(是 NFS 還是雲存儲),只需通過 PVC 申請資源;管理員統一管理 PV,實現存儲資源的複用和管控。
通俗理解:PV 是管理員提前備好的“硬盤”(比如 10GB、20GB 規格),PVC 是開發人員提交的“租硬盤申請”(比如“我要 5GB 可讀寫的硬盤”),K8s 負責匹配申請和硬盤,綁定後 Pod 就能使用這塊“硬盤”存儲數據。
二、基礎概念:PV 的核心屬性
創建 PV 時,需要指定兩個關鍵屬性,這也是 PVC 匹配 PV 的依據:
1. 訪問模式(Access Modes)
定義 PV 的使用權限,支持三種模式:
ReadWriteOnce(RWO):僅允許單個節點以讀寫方式掛載(最常用,適合數據庫等獨佔存儲場景);ReadOnlyMany(ROX):允許多個節點以只讀方式掛載(適合共享配置文件場景);ReadWriteMany(RWX):允許多個節點以讀寫方式掛載(適合多節點共享數據場景,需存儲類型支持)。
2. 存儲容量(Capacity)
指定 PV 的存儲大小(如 10Gi),PVC 申請的容量不能超過 PV 的容量。
3. 存儲類型(StorageClass)
用於對 PV 分類,支持動態供應(無需管理員手動創建 PV,K8s 按需自動創建),後續會詳細説明。
三、實戰步驟:手動創建 PV/PVC 並使用
以 NFS 存儲為例(需提前搭建 NFS 服務器,假設共享目錄為 192.168.1.100:/data/nfs),一步步實現數據持久化。
1. 步驟 1:創建 PV(管理員操作)
創建 pv-nfs.yaml 文件,定義一個 10GB 的 NFS 類型 PV:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-10g # PV 名稱
spec:
capacity:
storage: 10Gi # 存儲容量 10GB
accessModes:
- ReadWriteOnce # 訪問模式:單節點讀寫
persistentVolumeReclaimPolicy: Retain # 回收策略:PVC 釋放後保留 PV(不刪除數據)
storageClassName: "" # 不指定存儲類(手動創建 PV 時留空)
nfs:
path: /data/nfs # NFS 共享目錄
server: 192.168.1.100 # NFS 服務器 IP
應用配置創建 PV:
kubectl apply -f pv-nfs.yaml
# 查看 PV 狀態(STATUS 為 Available 表示可用)
kubectl get pv
# 輸出示例:
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
# pv-nfs-10g 10Gi RWO Retain Available 30s
2. 步驟 2:創建 PVC(開發人員操作)
創建 pvc-nfs.yaml 文件,申請 5GB 存儲(需與 PV 的訪問模式、存儲類型匹配):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-5g # PVC 名稱
spec:
accessModes:
- ReadWriteOnce # 與 PV 的訪問模式一致
resources:
requests:
storage: 5Gi # 申請 5GB 存儲(不超過 PV 的 10GB)
storageClassName: "" # 與 PV 保持一致(留空)
應用配置創建 PVC:
kubectl apply -f pvc-nfs.yaml
# 查看 PVC 狀態(STATUS 為 Bound 表示已綁定 PV)
kubectl get pvc
# 輸出示例:
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# pvc-nfs-5g Bound pv-nfs-10g 10Gi RWO 20s
此時 PV 的狀態會變為 Bound,表示已被 PVC 佔用。
3. 步驟 3:Pod 掛載 PVC(使用存儲)
創建 pod-with-pvc.yaml 文件,在 Pod 中掛載 PVC:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pvc-demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: data-volume # 卷名稱(與下方 volumes 對應)
mountPath: /usr/share/nginx/html # 容器內掛載路徑(Nginx 網站根目錄)
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: pvc-nfs-5g # 引用創建的 PVC 名稱
應用配置創建 Pod:
kubectl apply -f pod-with-pvc.yaml
# 查看 Pod 狀態(STATUS 為 Running 表示正常)
kubectl get pods
4. 驗證數據持久化
測試數據是否能持久化(即使 Pod 重建,數據也不丟失):
# 1. 進入 Pod,創建測試文件(寫入 Nginx 根目錄)
kubectl exec -it nginx-pvc-demo -- touch /usr/share/nginx/html/test-persist.txt
kubectl exec -it nginx-pvc-demo -- echo "K8s PV/PVC 持久化測試" > /usr/share/nginx/html/test-persist.txt
# 2. 手動刪除 Pod(模擬 Pod 故障重建)
kubectl delete pod nginx-pvc-demo
# 3. 重新創建 Pod(使用相同的 PVC)
kubectl apply -f pod-with-pvc.yaml
# 4. 驗證數據是否存在
kubectl exec -it nginx-pvc-demo -- cat /usr/share/nginx/html/test-persist.txt
# 輸出:K8s PV/PVC 持久化測試(數據成功保留)
四、進階用法:StorageClass 動態供應 PV
手動創建 PV 存在侷限性:需要管理員提前規劃存儲容量,無法按需創建。StorageClass 支持“動態供應”,讓 K8s 根據 PVC 自動創建 PV,無需人工干預。
1. 核心邏輯
- 管理員創建 StorageClass,定義存儲類型(如 NFS、雲存儲)、回收策略等;
- 開發人員創建 PVC 時,指定 StorageClass 名稱;
- K8s 會根據 StorageClass 的配置,自動創建對應的 PV 並與 PVC 綁定。
2. 實戰:NFS 動態供應(需安裝 NFS Provisioner)
NFS 本身不支持動態供應,需安裝 nfs-subdir-external-provisioner 插件(第三方提供的動態供應器)。
(1)安裝 NFS Provisioner(Helm 方式,簡單高效)
# 1. 添加 Helm 倉庫
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
# 2. 安裝 Provisioner(指定 NFS 服務器地址和共享目錄)
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=192.168.1.100 \
--set nfs.path=/data/nfs
(2)查看自動創建的 StorageClass
kubectl get sc
# 輸出示例(默認名稱為 nfs-client):
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
# nfs-client cluster.local/nfs-provisioner-nfs-subdir-external-provisioner Delete Immediate true 1m
(3)創建 PVC(指定 StorageClass)
創建 pvc-dynamic.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic-8g
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: nfs-client # 指定 StorageClass 名稱(動態供應)
應用配置:
kubectl apply -f pvc-dynamic.yaml
# 查看 PVC 和自動創建的 PV
kubectl get pvc # STATUS 為 Bound 表示成功
kubectl get pv # 會出現一個自動創建的 PV(名稱格式:pvc-xxx)
此時無需管理員手動創建 PV,K8s 已根據 PVC 需求自動創建,極大提升效率。
五、避坑指南:常見問題與解決方案
1. PVC 一直處於 Pending 狀態
- 原因:沒有匹配的 PV(訪問模式、存儲容量、StorageClass 不匹配);
- 解決:
- 檢查 PVC 和 PV 的
accessModes是否一致; - 確保 PVC 申請的存儲容量 ≤ PV 的容量;
- 若使用 StorageClass,檢查是否安裝了對應的 Provisioner。
2. Pod 掛載 PVC 失敗(狀態為 Error)
- 原因:NFS 服務器地址錯誤、共享目錄權限不足、存儲服務器不可達;
- 解決:
- 驗證 NFS 服務器是否正常:
mount -t nfs 192.168.1.100:/data/nfs /tmp/test; - 確保 NFS 共享目錄權限開放:
chmod 777 /data/nfs; - 查看 Pod 日誌排查:
kubectl logs nginx-pvc-demo。
3. PV 回收策略選擇不當
Retain:PVC 釋放後,PV 保留為Released狀態,數據不刪除(需手動清理);Delete:PVC 釋放後,PV 自動刪除(動態供應默認策略,適合臨時存儲);Recycle:PVC 釋放後,PV 自動清理數據並變為Available(已廢棄,不推薦使用);- 建議:生產環境用
Retain策略,避免誤刪數據;測試環境用Delete策略,自動清理資源。
4. 訪問模式使用錯誤
- 例如:多個節點的 Pod 掛載同一個 PVC,卻使用
ReadWriteOnce模式; - 解決:根據場景選擇正確的訪問模式,多節點共享需用
ReadWriteMany(需存儲類型支持,如 NFS、GlusterFS)。
總結
PV/PVC 是 K8s 持久化存儲的核心方案,核心優勢是“存儲與使用分離”——管理員統一管理存儲資源,開發人員只需專注於業務需求。實戰中:
- 小集羣或簡單場景,可手動創建 PV/PVC(適合本地磁盤、NFS 存儲);
- 中大型集羣或動態需求,優先使用 StorageClass 動態供應(減少人工干預);
- 生產環境需注意數據安全:選擇
Retain回收策略,定期備份存儲數據,避免存儲服務器單點故障。
掌握 PV/PVC 後,你可以輕鬆實現數據庫、文件服務等應用的數據持久化,再也不用擔心 Pod 重建導致數據丟失的問題。這是 K8s 部署有狀態應用的基礎技能,也是從“部署無狀態服務”到“管理有狀態應用”的關鍵一步。