Volume - 数据持久化
Volume - 数据持久化
为什么需要 Volume
容器中的文件系统是临时的:
- 容器重启:文件丢失
- Pod 删除:数据丢失
- 多容器共享:无法共享数据
Volume(存储卷) 解决了这些问题,提供持久化存储。
Volume 类型
Kubernetes 支持多种 Volume 类型:
临时存储
- emptyDir:Pod 生命周期内的临时存储
- hostPath:挂载节点文件系统
持久存储
- PersistentVolume:持久卷
- PersistentVolumeClaim:持久卷声明
网络存储
- nfs:NFS 网络文件系统
- cephfs:Ceph 文件系统
- glusterfs:GlusterFS
云存储
- awsElasticBlockStore:AWS EBS
- gcePersistentDisk:GCP 持久磁盘
- azureDisk:Azure 磁盘
配置存储
- configMap:ConfigMap 作为文件
- secret:Secret 作为文件
emptyDir - 临时存储
emptyDir 在 Pod 创建时创建,Pod 删除时清空。
基本用法
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: writer
image: busybox
command: ['sh', '-c', 'echo "Hello" > /data/message.txt; sleep 3600']
volumeMounts:
- name: shared-data
mountPath: /data
- name: reader
image: busybox
command: ['sh', '-c', 'while true; do cat /data/message.txt; sleep 5; done']
volumeMounts:
- name: shared-data
mountPath: /data
volumes:
- name: shared-data
emptyDir: {}
使用内存作为存储
volumes:
- name: cache-volume
emptyDir:
medium: Memory # 使用内存(tmpfs)
sizeLimit: 1Gi # 限制大小
使用场景
- 缓存数据
- 临时文件
- 容器间数据共享
- 数据处理管道
hostPath - 节点存储
hostPath 挂载节点上的文件或目录到 Pod。
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: host-volume
mountPath: /usr/share/nginx/html
volumes:
- name: host-volume
hostPath:
path: /data/web # 节点路径
type: DirectoryOrCreate
hostPath 类型
- DirectoryOrCreate:目录不存在则创建
- Directory:必须存在的目录
- FileOrCreate:文件不存在则创建
- File:必须存在的文件
- Socket:UNIX socket
- CharDevice:字符设备
- BlockDevice:块设备
⚠️ 注意事项
- 仅用于单节点测试
- 不适合生产环境(Pod 可能调度到其他节点)
- 存在安全风险
PersistentVolume (PV)
PV 是集群级别的存储资源,由管理员创建。
创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-example
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data
访问模式
- ReadWriteOnce (RWO):单节点读写
- ReadOnlyMany (ROX):多节点只读
- ReadWriteMany (RWX):多节点读写
回收策略
- Retain:保留数据,需手动清理
- Delete:自动删除
- Recycle:清空数据后重用(已废弃)
PersistentVolumeClaim (PVC)
PVC 是用户对存储的请求,类似 Pod 消费节点资源。
创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-example
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manual
在 Pod 中使用
apiVersion: v1
kind: Pod
metadata:
name: pod-with-pvc
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: pvc-example
PV 和 PVC 的关系
┌─────────────────┐ ┌─────────────────┐
│ PersistentVolume│◄──────│PersistentVolume │
│ (管理员创建) │ 绑定 │ Claim │
│ (集群资源) │ │ (用户请求) │
└─────────────────┘ └─────────────────┘
▲
│ 引用
│
┌────────┴────────┐
│ Pod │
└─────────────────┘
绑定过程
- 用户创建 PVC,指定存储需求
- Kubernetes 查找匹配的 PV
- 将 PV 绑定到 PVC
- Pod 通过 PVC 使用存储
StorageClass - 动态供应
StorageClass 提供动态创建 PV 的能力。
创建 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
allowVolumeExpansion: true
使用 StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-storage
resources:
requests:
storage: 10Gi
创建 PVC 后,StorageClass 会自动创建 PV。
默认 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/gce-pd
查看默认 StorageClass:
kubectl get storageclass
# 输出
NAME PROVISIONER RECLAIMPOLICY AGE
standard (default) kubernetes.io/gce-pd Delete 10d
fast-storage kubernetes.io/aws-ebs Delete 5d
实战示例
MySQL 持久化存储
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: standard
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
---
# Service
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
WordPress + MySQL
# MySQL PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
# WordPress PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
# MySQL Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpassword"
- name: MYSQL_DATABASE
value: "wordpress"
- name: MYSQL_USER
value: "wordpress"
- name: MYSQL_PASSWORD
value: "wordpresspassword"
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
---
# WordPress Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 2
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:latest
env:
- name: WORDPRESS_DB_HOST
value: mysql
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpresspassword
- name: WORDPRESS_DB_NAME
value: wordpress
ports:
- containerPort: 80
volumeMounts:
- name: wordpress-storage
mountPath: /var/www/html
volumes:
- name: wordpress-storage
persistentVolumeClaim:
claimName: wordpress-pvc
扩容 PVC
如果 StorageClass 支持扩容(allowVolumeExpansion: true):
# 编辑 PVC
kubectl edit pvc mysql-pvc
# 修改 storage 大小
spec:
resources:
requests:
storage: 30Gi # 从 20Gi 扩容到 30Gi
或使用 patch:
kubectl patch pvc mysql-pvc -p '{"spec":{"resources":{"requests":{"storage":"30Gi"}}}}'
常用命令
# PersistentVolume
kubectl get pv
kubectl describe pv <name>
kubectl delete pv <name>
# PersistentVolumeClaim
kubectl get pvc
kubectl describe pvc <name>
kubectl delete pvc <name>
# StorageClass
kubectl get storageclass
kubectl describe storageclass <name>
# 查看 PVC 绑定状态
kubectl get pvc -w
# 查看 Pod 使用的 Volume
kubectl describe pod <name> | grep -A5 Volumes
Volume 状态
PV 状态
- Available:可用,未绑定
- Bound:已绑定到 PVC
- Released:PVC 已删除,但资源未回收
- Failed:自动回收失败
PVC 状态
- Pending:等待绑定 PV
- Bound:已绑定到 PV
- Lost:关联的 PV 不存在
最佳实践
1. 生产环境使用动态供应
# 推荐:使用 StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-pvc
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
2. 选择合适的访问模式
- 数据库:ReadWriteOnce
- 共享文件:ReadWriteMany
- 只读数据:ReadOnlyMany
3. 设置合适的回收策略
# 生产环境
persistentVolumeReclaimPolicy: Retain
# 测试环境
persistentVolumeReclaimPolicy: Delete
4. 为 StatefulSet 使用 volumeClaimTemplate
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
5. 监控存储使用
# 查看 PVC 使用情况
kubectl get pvc -o custom-columns=NAME:.metadata.name,CAPACITY:.spec.resources.requests.storage,USED:.status.capacity.storage
# 查看 Pod 磁盘使用
kubectl exec <pod-name> -- df -h
6. 备份重要数据
即使使用持久化存储,也要定期备份:
# 使用 kubectl cp 备份
kubectl cp <namespace>/<pod-name>:/data ./backup
# 使用专业备份工具
# - Velero
# - Kasten K10
小结
Volume 是 Kubernetes 实现数据持久化的核心机制:
临时存储:
- emptyDir:Pod 生命周期内有效
- hostPath:挂载节点路径(测试用)
持久存储:
- PV:集群级存储资源
- PVC:用户存储请求
- StorageClass:动态供应
关键概念:
- PV/PVC 解耦存储和使用
- StorageClass 实现动态供应
- 选择合适的访问模式和回收策略
- 生产环境使用云存储或网络存储
下一章我们将进入进阶篇,学习 StatefulSet 管理有状态应用。