学习目标
- 理解容器存储的临时性问题
- 掌握 PV/PVC 的绑定机制
- 理解 StorageClass 动态供给
- 完成 hostPath 存储的实际挂载与数据持久化验证
1. 为什么需要持久化存储?
容器的文件系统是临时的(OverlayFS 可写层)。当容器重启或 Pod 被重新调度到其他节点时,所有写入的数据都会丢失。对于数据库、文件上传等有状态应用,这是不可接受的。
Volume 是 K8s 提供的存储抽象,它的生命周期独立于容器,可以在容器重启后保留数据。
2. PV 与 PVC
PV(PersistentVolume)—— 存储资源
由管理员创建,代表集群中的一块实际存储(NFS 共享目录、云盘、本地磁盘等)。
yamlapiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 5Gi # 存储容量
accessModes:
- ReadWriteMany # 多节点读写
persistentVolumeReclaimPolicy: Retain # PVC 删除后保留数据
nfs:
server: 172.16.11.104
path: /data/nfs-share
参数解读:
capacity.storage: 5Gi:声明这块存储的总容量为 5 GiBaccessModes:定义这块存储支持的访问方式(详见下表)persistentVolumeReclaimPolicy: Retain:当绑定的 PVC 被删除后,PV 中的数据保留不动,需要管理员手动清理。生产环境强烈推荐 Retainnfs:存储后端的具体实现(这里是 NFS),不同的存储后端有不同的配置字段
PVC(PersistentVolumeClaim)—— 存储需求
由用户创建,描述需要多大、什么类型的存储。K8s 自动匹配合适的 PV 进行绑定。
yamlapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi # 申请 5Gi 存储
设计哲学:PV 和 PVC 是一种生产者-消费者模式。管理员负责"供货"(创建 PV),开发者负责"下单"(创建 PVC)。两者通过
accessModes和storage大小自动匹配。这种解耦让开发者不需要关心底层存储是 NFS、Ceph 还是云盘。
绑定流程
bash用户创建 PVC("我需要 5Gi 的 RWX 存储")
↓
K8s 匹配满足条件的 PV → 绑定(Bound)
↓
Pod 挂载 PVC 使用存储
访问模式
| 模式 | 缩写 | 含义 |
|---|---|---|
| ReadWriteOnce | RWO | 单节点读写(最常见,如本地磁盘、云盘) |
| ReadOnlyMany | ROX | 多节点只读 |
| ReadWriteMany | RWX | 多节点读写(NFS、CephFS 支持) |
回收策略(PVC 被删除后 PV 的数据怎么办)
| 策略 | 行为 |
|---|---|
| Retain | 保留数据,需手动清理(生产推荐) |
| Delete | 自动删除后端存储数据 |
| Recycle | 清空数据后重新可用(已弃用) |
3. StorageClass 动态供给
手动创建 PV 无法应对大量存储需求。StorageClass 定义了存储的"模板",当用户创建 PVC 时,K8s 自动创建对应的 PV。
yaml# StorageClass 示例(NFS 动态供给需要额外部署 NFS Provisioner)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: nfs-provisioner # 供给器名称
parameters:
archiveOnDelete: "true" # 删除 PVC 时归档而非直接删除
参数解读:
provisioner:指定由哪个存储驱动来自动创建 PV。不同的存储方案有不同的 provisioner(如 AWS EBS 用ebs.csi.aws.com,NFS 用自定义的 provisioner)parameters:传递给 provisioner 的参数,不同 provisioner 支持的参数不同
yaml# PVC 引用 StorageClass(不再需要手动创建 PV)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: auto-pvc
spec:
storageClassName: nfs-storage # 指定 StorageClass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
工作流程:PVC 指定了
storageClassName: nfs-storage→ K8s 找到对应的 StorageClass → 调用 provisioner 自动创建一个 10Gi 的 PV → 自动绑定。整个过程无需管理员介入。
动手实验
实验 13.1:验证容器存储的临时性
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 创建一个 Pod,写入数据到容器文件系统
kubectl run temp-data --image=docker.m.daocloud.io/library/busybox --restart=Never -- sh -c \
"echo 'important data' > /tmp/data.txt && sleep 3600"
sleep 3
# 验证数据存在
kubectl exec temp-data -- cat /tmp/data.txt
预期输出:
bashimportant data
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 删除并重建同名 Pod
kubectl delete pod temp-data --wait=true
kubectl run temp-data --image=docker.m.daocloud.io/library/busybox --restart=Never -- sh -c 'cat /tmp/data.txt 2>&1; sleep 3600'
sleep 3
# 查看日志——数据还在吗?
kubectl logs temp-data
预期输出:
bashcat: can't open '/tmp/data.txt': No such file or directory
输出解读:数据丢失了!
/tmp/data.txt不存在。这就是容器存储的临时性——Pod 被删除重建后,之前写入的所有数据都会消失,因为新 Pod 拿到的是一个全新的、干净的可写层。
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
kubectl delete pod temp-data
实验 13.2:使用 PV/PVC 持久化数据
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 创建 PV 和 PVC(使用 hostPath 做简单演示)
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv
spec:
capacity:
storage: 1Gi
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /tmp/k8s-pv-demo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
volumeName: demo-pv
EOF
预期输出:
bashpersistentvolume/demo-pv created
persistentvolumeclaim/demo-pvc created
YAML 解读:
hostPath.path: /tmp/k8s-pv-demo:使用节点本地目录作为存储(仅适合实验,生产环境应使用 NFS/Ceph/云盘等分布式存储)volumeName: demo-pv:PVC 明确指定要绑定的 PV 名称,跳过自动匹配。这在集群没有配置 StorageClass 时是必需的
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 验证绑定状态
kubectl get pv,pvc
预期输出:
bashNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/demo-pv 1Gi RWO Retain Bound default/demo-pvc <unset> 2s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/demo-pvc Bound demo-pv 1Gi RWO <unset> 2s
输出解读:
- PV 的
STATUS: Bound+CLAIM: default/demo-pvc—— PV 已经被 default 命名空间的 demo-pvc 绑定- PVC 的
STATUS: Bound+VOLUME: demo-pv—— PVC 成功绑定到了 demo-pvRECLAIM POLICY: Retain—— 即使删除 PVC,数据也会保留
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 创建 Pod 挂载 PVC,写入数据
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pv-writer
spec:
containers:
- name: writer
image: docker.m.daocloud.io/library/busybox
command: ['sh', '-c', 'echo persistent-data-12345 > /data/test.txt && echo wrote data && sleep 3600']
volumeMounts:
- name: my-storage
mountPath: /data
volumes:
- name: my-storage
persistentVolumeClaim:
claimName: demo-pvc
EOF
kubectl wait --for=condition=Ready pod/pv-writer --timeout=60s
预期输出:
bashpod/pv-writer created
pod/pv-writer condition met
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 验证数据已写入
kubectl exec pv-writer -- cat /data/test.txt
预期输出:
bashpersistent-data-12345
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 删除 Pod(模拟 Pod 故障/重建)
kubectl delete pod pv-writer --wait=true
# 重新创建 Pod 挂载同一个 PVC
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pv-reader
spec:
containers:
- name: reader
image: docker.m.daocloud.io/library/busybox
command: ['sh', '-c', 'cat /data/test.txt && sleep 3600']
volumeMounts:
- name: my-storage
mountPath: /data
volumes:
- name: my-storage
persistentVolumeClaim:
claimName: demo-pvc
EOF
kubectl wait --for=condition=Ready pod/pv-reader --timeout=60s
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 数据还在吗?
kubectl logs pv-reader
预期输出:
bashpersistent-data-12345
输出解读:数据完好无损!与实验 13.1 形成鲜明对比——使用 PVC 挂载的存储,数据的生命周期独立于 Pod。即使 Pod 被删除重建,只要挂载同一个 PVC,之前写入的数据就可以继续读取。这就是持久化存储解决的核心问题。
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 清理
kubectl delete pod pv-reader
kubectl delete pvc demo-pvc
kubectl delete pv demo-pv
预期输出:
bashpod "pv-reader" deleted
persistentvolumeclaim "demo-pvc" deleted
persistentvolume "demo-pv" deleted
检查点
- 能解释为什么容器需要持久化存储(实验 13.1 的对比体验)
- 能创建 PV 和 PVC 并验证绑定关系(STATUS: Bound)
- 理解三种访问模式和三种回收策略
- 能解释 StorageClass 动态供给的工作原理
Comments NOTHING