本章目录(点击跳转):
- 6.1 为什么需要存储
- 6.2 三件套:PV、PVC、StorageClass
- 6.2.1 读写模式(AccessModes)
- 6.2.2 回收策略(reclaimPolicy)
- 6.3 动手实验 1:用 StorageClass 动态创建存储(推荐方式)
- 6.3.1 先看现有的 StorageClass
- 6.3.2 创建 PVC + 使用它的 Pod
- 6.3.3 扩容 PVC
- 6.4 动手实验 2:手动创建 PV + PVC(理解绑定过程)
- 6.5 动手实验 3:NFS 存储(多节点共享,RWX)
- 6.6 在 Deployment / StatefulSet 中使用 PVC
- 6.6.1 Deployment 挂载
- 6.6.2 StatefulSet 自动创建 PVC
- 6.6.3 emptyDir(临时存储)
- 6.7 备份与恢复的基本思路
- 6.8 排障速查
- 6.9 本章小结
本章目标:理解 K8s 存储三件套 PV / PVC / StorageClass,会挂本地存储、NFS,理解读写模式,知道数据备份的基本思路。
6.1 为什么需要存储
Pod 是"一次性的":重启、迁移、重建都会发生。容器文件系统里写的东西,Pod 一没就全没了。
但现实里很多应用有数据要保存:
- 数据库文件(MySQL、PostgreSQL)
- 上传的用户文件、图片
- 应用生成的报表、日志归档
K8s 的解决方案:存储与 Pod 解耦——数据放在"卷(Volume)"上,卷的生命周期独立于 Pod。
Pod ──(挂载)──> Volume ──> 后端存储(本地盘 / NFS / 云盘 / S3...)
▲
PVC(申请单)──┘ PV(实际资源) StorageClass(自动造 PV 的模板)
6.2 三件套:PV、PVC、StorageClass
用"租房"类比秒懂:
| 概念 | 类比 | 说明 |
|---|---|---|
| PV(PersistentVolume,持久卷) | 一套具体的房子 | 集群级存储资源,由管理员或 StorageClass 动态创建 |
| PVC(PersistentVolumeClaim,声明) | 租房申请单 | 用户(应用)声明"我要 10G 读写存储",K8s 给它绑定一个 PV |
| StorageClass | 房源中介规则 | 定义"什么样的 PV 可以被自动创建"(存储后端、默认大小等) |
绑定规则:PVC 申请 10G,就绑定一个容量 ≥10G、读写模式兼容的 PV。PVC 被 Pod 引用后,即使 Pod 删除重建,数据还在(删除 PVC 才释放数据,且要看 reclaimPolicy)。
6.2.1 读写模式(AccessModes)
| 模式 | 缩写 | 含义 |
|---|---|---|
| ReadWriteOnce | RWO | 只能被一个节点上的 Pod 读写(最常用;多副本 Pod 必须在同一节点) |
| ReadOnlyMany | ROX | 多个节点可读 |
| ReadWriteMany | RWX | 多个节点可读写(需要 NFS 等支持) |
| ReadWriteOncePod | RWOP | 只允许一个 Pod(1.22+) |
6.2.2 回收策略(reclaimPolicy)
Retain:PVC 删除后 PV 数据保留,需人工清理(数据重要选这个)。Delete:PVC 删除后 PV 及其数据一起删。Recycle:已废弃,别用。
6.3 动手实验 1:用 StorageClass 动态创建存储(推荐方式)
minikube 自带 standard StorageClass(底层是本地路径 local-path 插件)。
6.3.1 先看现有的 StorageClass
kubectl get storageclass
# 输出示例:
# NAME PROVISIONER RECLAIMPOLICY ALLOWVOLUMEEXPANSION AGE
# standard rancher.io/local-path Delete true 1d
6.3.2 创建 PVC + 使用它的 Pod
# pvc-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi # StorageClass 会自动创建 PV 并绑定
# storageClassName: standard # 不写则用默认 SC
---
apiVersion: v1
kind: Pod
metadata:
name: data-demo
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "while true; do echo \"$(date) hello\" >> /data/test.log; sleep 2; done"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
kubectl apply -f pvc-demo.yaml
kubectl get pvc # STATUS 应为 Bound
kubectl get pod data-demo -o wide
# 看数据真的写进去了
kubectl exec data-demo -- cat /data/test.log
# 关键实验:删掉 Pod,数据还在吗?
kubectl delete pod data-demo
# 重建(apply 整个文件即可)
kubectl apply -f pvc-demo.yaml
kubectl exec data-demo -- cat /data/test.log
# 之前的内容还在!这就是持久化
6.3.3 扩容 PVC
kubectl patch pvc app-data -p '{"spec":{"resources":{"requests":{"storage":"2Gi"}}}}'
kubectl get pvc
# 很多存储支持在线扩容;local-path 支持
6.4 动手实验 2:手动创建 PV + PVC(理解绑定过程)
# manual-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
local:
path: /tmp/pv-data # 节点上的本地目录
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- minikube # 本地 PV 只能被指定节点使用
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-manual
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeName: pv-manual # 指定绑定这个 PV
# 先在 minikube 节点上准备目录
minikube ssh -- mkdir -p /tmp/pv-data
kubectl apply -f manual-pv.yaml
kubectl get pv pvc
# PV 状态:Bound;PVC 状态:Bound
kubectl delete -f manual-pv.yaml
# PV 变 Released(Retain 策略,数据还在),PVC 删除
kubectl get pv
6.5 动手实验 3:NFS 存储(多节点共享,RWX)
生产中小规模集群常用 NFS。这里演示 YAML 结构(需要一台 NFS 服务器,可用 minikube 自身模拟):
# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany # NFS 支持多节点读写
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.1.100 # 你的 NFS 服务器 IP
path: /exports/k8s
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
云环境则用各家的"云盘 StorageClass"(阿里云盘、EBS、PD 等),原理与 local 一样:SC 动态创建云盘 PV。注意云盘一般是 RWO,多副本写要用 NFS 类 RWX 存储或数据库自身的副本方案。
6.6 在 Deployment / StatefulSet 中使用 PVC
6.6.1 Deployment 挂载
# 与 6.3 的 Pod 一样,把 spec 放进 Deployment 的 template 即可
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-data
spec:
replicas: 1 # RWO 存储多副本要注意:Pod 必须在同一节点
selector:
matchLabels:
app: app-with-data
template:
metadata:
labels:
app: app-with-data
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
6.6.2 StatefulSet 自动创建 PVC
第 4 章讲过 volumeClaimTemplates:StatefulSet 会为每个副本自动创建独立 PVC(data-xxx-0/1/2),这是数据库集群的标准做法。
6.6.3 emptyDir(临时存储)
Pod 内多容器共享、Pod 删除即清空——第 3 章边车示例用过:
volumes:
- name: cache
emptyDir: {}
6.7 备份与恢复的基本思路
K8s 存储负责"挂载",不负责"备份"。生产备份思路:
# 1) 数据库类:用应用自身工具做逻辑备份,结果写到 RWX 存储或对象存储
kubectl exec -it mysql-0 -- sh -c "mysqldump -uroot -pPASS mydb > /backup/dump.sql"
# 2) 通用方案:起一个一次性 Pod 挂载同一个 PVC 查看/拷贝
kubectl run backup-tmp --rm -it \
--image=busybox:1.36 \
-- sh -c "ls /data"
更完整的方案是 Velero(集群级备份工具,含 PV 快照),本书不展开,生产搜索"Velero 教程"即可。
6.8 排障速查
| 症状 | 原因/处理 |
|---|---|
PVC 一直 Pending |
没有满足条件的 PV;kubectl describe pvc xxx 看 Events(容量/访问模式/SC 不对) |
Pod Pending 且事件有 volume ... failed to bind |
同上看 PVC |
| Pod 里挂载点是空的 | 检查 mountPath、PV 的 nodeAffinity 是否匹配所在节点 |
| 删了 PVC 数据消失 | reclaimPolicy: Delete 或 SC 默认 Delete;重要数据用 Retain |
| 多副本起不来 + RWO 存储 | RWO 只允许单节点挂载,检查 Pod 分布或改用 RWX |
6.9 本章小结
- 三件套心智模型:PVC 是申请单,PV 是房源,StorageClass 是中介规则。
- 优先用 StorageClass 动态供给,少手写 PV。
- RWO 最常用;多节点读写要 RWX(NFS/云 NAS)。
- 数据在 Pod 之外:删 Pod 数据在,删 PVC 按策略走。
- 备份是应用/工具的事(mysqldump、Velero),K8s 只负责挂载。
下一章:配置与密钥——让代码和配置分离。
原文链接:第6章 存储,转载请注明来源!
