讲解
emptyDir 跟着 Pod 死,hostPath 跟着节点跑——真正的持久化存储要同时摆脱这两者。Kubernetes 的解法是把「存储的提供」和「存储的使用」拆成两个资源:PersistentVolume(PV)是集群里的一块存储(谁来提供、多大、什么访问模式),由管理员或系统自动创建;PersistentVolumeClaim(PVC)是应用对存储的「申请单」(我要多大、什么访问模式)。Pod 挂 PVC,PVC 绑定 PV——应用只面对「申请单」,完全不关心背后是本地盘、NFS 还是云盘。
现代集群里 PV 几乎不手写,靠的是动态供给(dynamic provisioning):集群装着一个 Provisioner(云厂商的盘、本地 path provisioner 等),注册为 StorageClass(存储类)。你创建 PVC 时写上 storageClassName,Provisioner 看到后就自动创建对应的 PV 并完成绑定,全程无人工。kind 集群自带 standard 这个默认 StorageClass(rancher.io/local-path),所以 PVC 建好就会 Bound。绑定后 PVC 的三个关键字段:accessModes(ReadWriteOnce 单节点读写,最常用)、resources.requests.storage(容量)、storageClassName。
数据持久性的验证逻辑很简单:Pod 挂 PVC → 写数据 → 删掉 Pod → 新 Pod 挂同一个 PVC → 数据还在。PV/PVC 的生命周期独立于任何 Pod,删除 Pod 甚至删除 PVC 后 PV 怎么办由 persistentVolumeReclaimPolicy 决定:Delete(动态供给的默认,连数据一起删)或 Retain(保留数据,人工处理)。生产数据库场景要对这个策略心里有数——删错 PVC 且策略是 Delete,数据就真的没了。
示例
一份 PVC(申请 100Mi)加一个挂载它的 Pod,StorageClass 用 kind 自带的默认 standard:
# wait: pod/ohmydocs-verify-data
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ohmydocs-verify-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Mi
---
apiVersion: v1
kind: Pod
metadata:
name: ohmydocs-verify-data
labels:
app: ohmydocs-verify-data
spec:
containers:
- name: busybox
image: busybox:1.36
command: ["sleep", "3600"]
volumeMounts:
- name: store
mountPath: /data
volumes:
- name: store
persistentVolumeClaim:
claimName: ohmydocs-verify-data
动态供给已生效:PVC 状态 Bound,背后自动创建了 PV。往里写一份数据:
kubectl get pvc ohmydocs-verify-data
kubectl get pv | grep ohmydocs-verify-data | head -2
kubectl exec ohmydocs-verify-data -- sh -c 'echo "第一批数据 $(date +%T)" > /data/keeper.txt'
kubectl exec ohmydocs-verify-data -- cat /data/keeper.txt
删掉 Pod、原地重建(apply 同一份清单),数据原样还在——这就是「持久」二字的含义:
kubectl delete pod ohmydocs-verify-data --wait
kubectl apply -f manifest1.yaml
kubectl wait --for=condition=ready pod/ohmydocs-verify-data --timeout=120s
kubectl exec ohmydocs-verify-data -- cat /data/keeper.txt
手写静态 PV 的样子(需要管理员预置存储,现代集群基本被动态供给取代,仅做客户端校验):
# 仅示意:静态 PV 需要管理员预置节点存储,演示字段结构即可
apiVersion: v1
kind: PersistentVolume
metadata:
name: ohmydocs-verify-static-pv
spec:
capacity:
storage: 1Gi
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data
常见坑
- PVC 一直 Pending:没有匹配的 StorageClass 或 Provisioner 没工作;kubectl describe pvc 的 Events 会直接说原因,kind/minikube 自带默认类,自建集群要装。
- Pod 删了数据没了:多半挂的是 emptyDir 而不是 PVC,或者 PV 回收策略是 Delete 且 PVC 被删了——删 PVC 前三思。
- accessModes 与后端不匹配:ReadWriteMany(多节点读写)需要 NFS/文件类存储支持,普通云盘不支持,PVC 绑不上。
- 跨命名空间挂 PVC:PVC 是命名空间资源,Pod 只能挂同命名空间的 PVC。
小结
PV = 存储本身,PVC = 存储申请单,Pod 挂 PVC;动态供给(StorageClass + Provisioner)让 PV 自动创建;PVC 生命周期独立于 Pod,重建 Pod 数据不丢。下一章换个维度:用命名空间和配额做隔离。