K8S - Volume 与 PersistentVolume - 容器存储与数据持久化
一、引言
在 Kubernetes 中,容器默认采用临时存储(Ephemeral Storage),这意味着 Pod 一旦重启或重建,其内部数据将完全丢失。
然而,在生产环境中,许多关键应用(如数据库、日志系统、内容管理平台)都需要保证数据持久化与高可用性。为此,Kubernetes 提供了以下核心机制:
• Volume:为 Pod 提供临时或持久的存储挂载能力。
• PersistentVolume (PV):集群级别的存储资源抽象,独立于 Pod 生命周期。
• PersistentVolumeClaim (PVC):用户对存储资源的声明,可实现动态或静态绑定 PV。
二、核心原理
2.1 Volume:Pod 级别的存储机制
Volume 是 Pod 内部的存储抽象,其生命周期与 Pod 一致。常见类型如下:
局限性:
• 临时性存储无法满足数据持久化需求
• 配置复杂,缺乏资源解耦能力
2.2 PV 与 PVC:存储资源的解耦模型
Kubernetes 通过 PV/PVC 模型实现应用与底层存储的解耦:
PersistentVolume(PV)
• 静态供给:管理员预创建,指定容量、访问模式、类型等
• 动态供给:通过 StorageClass自动按需创建(如 AWS EBS)
关键字段:
• capacity: 存储容量,如 1Gi
• accessModes: 访问模式(ReadWriteOnce、ReadOnlyMany等)
• persistentVolumeReclaimPolicy: 回收策略(Retain、Delete)
PersistentVolumeClaim(PVC)
• 用户声明对存储的需求,K8s 自动匹配合适的 PV
StorageClass
• 定义动态供给模板(云存储参数、文件系统类型等)
• 实现 PV 的按需自动化创建
模型关系图:
# 静态供给流程
┌─────────────┐ 绑定 ┌─────────────┐
│ PV (预创建) │ ◄──────────────────── │ PVC │
└──────┬──────┘ └──────┬──────┘│ ││ 挂载 │▼ ▼
┌───────────────────────────────────────────────────┐
│ Pod │
│ (通过 volumes 字段引用 PVC) │
└───────────────────────────────────────────────────┘
# 动态供给流程
┌─────────────┐ 动态创建 ┌─────────────┐
│ StorageClass │ ───────────────────────►│ PV │
└───────┬──────┘ └──────┬──────┘│ 自动触发 │▼ ▼
┌─────────────┐ 绑定 ┌─────────────┐
│ PVC │ ◄────────────────────── │ Pod │
└─────────────┘ 挂载 └─────────────┘
2.3 核心参数详解
Access Modes(访问模式)
• ReadWriteOnce (RWO):单节点读写(如云硬盘)
• ReadOnlyMany (ROX):多节点只读
• ReadWriteMany (RWX):多节点读写(需 NFS 或分布式存储支持)
Reclaim Policy(回收策略)
• Retain:PVC 删除后保留数据(需手动清理)
• Delete:PVC 删除后自动销毁数据(云平台默认)
• Recycle(已废弃):清空数据再重用(不推荐)
三、实战演示:MySQL 数据持久化
3.1 环境准备(以 Minikube 为例)
# 安装工具
brew install kubectl minikube# 启动集群
minikube start --driver=docker
3.2 创建 PersistentVolume(静态供给)
步骤 1:定义 PV(测试用 hostPath)
mysql-pv.yaml:apiVersion: v1
kind: PersistentVolume # 资源类型
metadata:name: mysql-pv # PV名称,集群内唯一标识
spec:capacity:storage: 1Gi # 存储容量(GiB单位)accessModes:- ReadWriteOnce # 访问模式:单节点读写挂载persistentVolumeReclaimPolicy: Retain # 回收策略:删除PVC后保留数据(需手动清理)hostPath:path: /data/mysql # 节点本地存储路径(仅适合单节点测试环境)
注:ReadWriteOnce 表示同时只能被一个 Pod 挂载
步骤 2:部署并查看状态
kubectl apply -f mysql-pv.yaml
kubectl get pv # 状态应为 Available
输出:NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mysql-pv 1Gi RWO Retain Available 5s# 注意 STATUS 应为 Available
3.3 创建 PersistentVolumeClaim
步骤 1:定义 PVC
mysql-pvc.yaml:apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: mysql-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1Gi
步骤 2:部署并绑定 PV
kubectl apply -f mysql-pvc.yaml
kubectl get pvc # 应为 Bound
kubectl get pv # 状态变为 Bound
输出:# 部署 PVC 后查看绑定状态
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pvc Bound mysql-pv 1Gi RWO 10s$ kubectl get pv # PV 状态变化
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE
mysql-pv 1Gi RWO Retain Bound default/mysql-pvc 2m
3.4 部署 MySQL 并挂载 PVC
步骤 1:定义 Deployment
mysql-deployment.yaml:apiVersion: apps/v1
kind: Deployment
metadata:name: mysql
spec:selector:matchLabels:app: mysql template:metadata:labels:app: mysql spec:containers:- name: mysqlimage: mysql:8.0 # 使用官方MySQL 8.0镜像env:- name: MYSQL_ROOT_PASSWORDvalue: "password" # 明文密码(生产环境应用Secret替代)ports:- containerPort: 3306 # 暴露MySQL默认端口volumeMounts:- name: mysql-storagemountPath: /var/lib/mysql # 挂载数据库存储路径(数据持久化)volumes:- name: mysql-storagepersistentVolumeClaim:claimName: mysql-pvc # 绑定预先创建的PVC(需确保存在)
步骤 2:部署并查看 Pod
kubectl apply -f mysql-deployment.yaml
kubectl get pods -l app=mysql
输出:# 查看 Pod 启动过程(展示完整生命周期)
$ kubectl get pods -l app=mysql --watch
NAME READY STATUS RESTARTS AGE
mysql-8c6b66d88-7w2vs 0/1 ContainerCreating 0 10s
mysql-8c6b66d88-7w2vs 1/1 Running 0 25s # 最终状态
3.5 数据持久化验证
步骤 1:写入测试数据
kubectl exec -it $(kubectl get pod -l app=mysql -o jsonpath="{.items[0].metadata.name}") -- \mysql -uroot -ppassword -e "CREATE DATABASE test;"
步骤 2:模拟 Pod 故障并验证数据
kubectl delete pod -l app=mysql
kubectl get pods -l app=mysql # 等待新 Pod 启动# 验证是否存在 test 数据库
kubectl exec -it $(kubectl get pod -l app=mysql -o jsonpath="{.items[0].metadata.name}") -- \mysql -uroot -ppassword -e "SHOW DATABASES;"
预期结果:应包含 test数据库,验证数据持久化成功。# 写入测试数据后检查(显示完整输出)
$ kubectl exec -it mysql-8c6b66d88-7w2vs -- mysql -uroot -ppassword -e "SHOW DATABASES;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test | # 新增的测试数据库
+--------------------+# 删除 Pod 后观察新实例
$ kubectl delete pod mysql-8c6b66d88-7w2vs
pod "mysql-8c6b66d88-7w2vs" deleted$ kubectl get pods -l app=mysql
NAME READY STATUS RESTARTS AGE
mysql-8c6b66d88-kk9xp 1/1 Running 0 45s # 新 Pod# 二次验证数据
$ kubectl exec -it $(kubectl get pod -l app=mysql -o name) -- mysql -uroot -ppassword -e "SHOW DATABASES;"
# 若出现错误"Error from server (NotFound): pods not found",说明 Pod 尚未完全启动
四、生产环境最佳实践
StorageClass 示例(AWS EBS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: ebs-gp3
provisioner: kubernetes.io/aws-ebs
parameters:type: gp3 # 通用型 SSDfsType: ext4 # 文件系统类型iops: "3000" # 性能配置(可选)encrypted: "true" # 启用加密
生产环境必须项:
-
使用 CSI 驱动程序(非树内驱动)
-
启用存储加密(AWS KMS)
-
设置合理容量阈值(避免资源浪费)
五、总结
5.1 核心重点
• Volume:适用于 Pod 内部共享临时数据,但无法跨生命周期持久化。
• PV/PVC 模型:解耦存储供应与消费,支持动态供给与多类型后端存储。
• 核心选型建议:
• 根据业务需要选择 accessModes与 reclaimPolicy
• 生产环境优先使用动态供给 + 云存储方案
5.2 常见问题排查表