K8s持久化存储实战:从PV/PVC到StorageClass的动态供给
1. 为什么需要K8s持久化存储
在Kubernetes中运行有状态服务时,数据持久化是个绕不开的话题。想象一下这样的场景:你部署了一个MySQL数据库,运行几天后Pod因为节点故障被重新调度到其他节点,结果发现所有数据都丢失了——这就是典型的没有配置持久化存储的后果。
传统Volume确实能解决部分数据持久化问题,但它存在两个致命缺陷:一是生命周期与Pod绑定,Pod删除后数据就没了;二是管理耦合度高,开发人员需要了解底层存储细节。我曾经在一个电商项目中就踩过这个坑,当时为了快速上线直接用了emptyDir,结果促销活动时Pod崩溃导致订单数据全部丢失,教训相当深刻。
PV和PVC的引入完美解决了这些问题。PV(PersistentVolume)是集群中的一块存储空间,由管理员预先配置;PVC(PersistentVolumeClaim)则是用户对存储资源的申请。这种解耦设计让开发人员只需声明"我需要多少存储空间",而不必关心存储的具体实现方式。就像我们去酒店入住时,只需要说明需要大床房还是双床房,而不必了解房间的具体位置和构造。
2. 静态供给实战:NFS示例
2.1 环境准备
我们先从最经典的NFS方案开始。在我的实验环境中,使用1台CentOS 7作为NFS服务器(IP:192.168.1.100),两个工作节点已安装nfs-utils:
# 所有节点执行
yum install -y nfs-utils rpcbind
在NFS服务器上创建共享目录并配置权限:
mkdir /nfs_share
chmod 777 /nfs_share
echo "/nfs_share *(rw,no_root_squash,sync)" > /etc/exports
systemctl enable --now nfs-server
2.2 创建PV
接下来定义PV资源,这里有个小技巧:accessModes的选择很关键。ReadWriteOnce适合单Pod读写场景,ReadWriteMany则支持多Pod同时挂载。我们创建一个1Gi的PV:
# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-demo
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
nfs:
path: /nfs_share
server: 192.168.1.100
应用配置后,可以用kubectl get pv查看状态,应该显示Available:
kubectl apply -f nfs-pv.yaml
kubectl get pv
2.3 创建PVC
PVC就像存储资源的"申请单",这里要注意storageClassName必须与PV匹配:
# nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc-demo
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: manual
创建后检查绑定状态,正常情况PVC会Bound到我们创建的PV:
kubectl apply -f nfs-pvc.yaml
kubectl get pvc
2.4 在Pod中使用
现在可以在Deployment中引用这个PVC了。这里演示一个NGINX示例:
# nginx-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-with-pvc
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc-demo
创建Pod后,我们可以测试数据持久化:
kubectl exec -it nginx-with-pvc-xxx -- sh -c "echo 'Hello PV' > /usr/share/nginx/html/test.txt"
# 删除Pod后重新创建,检查文件是否仍然存在
3. 动态供给:StorageClass方案
3.1 静态供给的局限性
虽然静态供给能工作,但在实际生产环境中会暴露明显问题:每次新增存储都需要管理员手动创建PV。我曾管理过一个50节点的集群,每周要处理数十个存储申请,手动操作不仅效率低下还容易出错。
动态供给通过StorageClass实现了"按需分配"的自动化流程。当用户创建PVC时,系统会自动创建对应的PV。这就像云硬盘服务,用户只需要指定容量和类型,系统会自动在后端完成资源分配。
3.2 部署NFS Provisioner
由于Kubernetes原生不支持NFS动态供给,我们需要先部署nfs-subdir-external-provisioner(社区维护的方案):
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=192.168.1.100 \
--set nfs.path=/nfs_share \
--set storageClass.name=nfs-sc
这会自动创建StorageClass,可以通过以下命令验证:
kubectl get sc
# 应该能看到名为nfs-sc的StorageClass
3.3 动态PVC实战
现在我们可以创建直接使用StorageClass的PVC了:
# dynamic-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc-demo
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: nfs-sc
创建这个PVC后,神奇的事情发生了——系统会自动创建对应的PV:
kubectl apply -f dynamic-pvc.yaml
kubectl get pvc # 查看PVC状态
kubectl get pv # 会自动出现一个2Gi的PV
3.4 StatefulSet中的应用
对于有状态服务,动态供给的优势更加明显。下面是一个MySQL StatefulSet示例:
# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
volumeMounts:
- mountPath: /var/lib/mysql
name: data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs-sc"
resources:
requests:
storage: 5Gi
StatefulSet会为每个Pod实例自动创建独立的PVC和PV,极大简化了有状态服务的部署流程。
4. 生产环境最佳实践
4.1 存储选型建议
根据多年经验,不同场景下的存储选择很有讲究:
- 开发测试环境:NFS是最经济的选择,但要注意性能问题
- 云环境:直接使用云厂商提供的StorageClass(如AWS EBS、Azure Disk)
- 高性能需求:考虑Ceph RBD或Local PV方案
- 多读场景:ReadWriteMany模式的存储如CephFS
4.2 性能调优技巧
在某个电商项目中,我们曾遇到NFS性能瓶颈,通过以下优化手段将IOPS提升了3倍:
- 调整NFS挂载参数,添加
noatime,nodiratime选项 - 为关键业务PVC设置
volumeMode: Block使用块设备 - 合理设置PV的回收策略,频繁读写的PVC建议用Retain
4.3 监控与运维
完善的监控是存储稳定的关键。建议:
- 使用Prometheus监控PV/PVC使用率
- 设置StorageClass的
allowVolumeExpansion: true以便后期扩容 - 定期检查PV的
VolumeHealth状态
# PVC扩容示例(需要StorageClass支持)
kubectl patch pvc dynamic-pvc-demo -p '{"spec":{"resources":{"requests":{"storage":"5Gi"}}}}'
4.4 常见故障排查
遇到PVC一直处于Pending状态时,可以按以下步骤排查:
- 检查StorageClass是否存在且配置正确
- 查看Provisioner Pod日志:
kubectl logs -f nfs-provisioner-xxx - 确认NFS服务器导出目录权限
- 检查kube-controller-manager日志中的存储相关错误
记得有一次我们的PVC无法绑定,最后发现是StorageClass的provisioner名称拼写错误,这种小细节往往最容易忽视。
更多推荐


所有评论(0)