目录
前言
一、先搞懂:K8s 基础存储卷(Volume)
1. emptyDir:Pod 级临时存储
二、PV 与 PVC:存储资源的 “供需分离”
1. 核心概念与角色分工
2. 关键配置详解
(1)访问模式(accessModes)
(2)回收策略(persistentVolumeReclaimPolicy)
3. PV 与 PVC 的生命周期
三、实战:NFS + PV + PVC 静态存储部署
1. 前置准备:部署 NFS 服务端
创建共享目录
配置NFS共享
生效配置
2. 运维视角:创建 PV
3. 开发者视角:创建 PVC 与 Pod
实现PVC和Pod的创建与测试
操作步骤
四、进阶:StorageClass 动态存储方案
1. 核心原理
2. 动态存储部署步骤
(1)配置 RBAC 权限
(2)部署 NFS Provisioner
(3)创建 StorageClass
(4)测试动态存储
操作流程
五、不同存储方案对比与选型建议
选型核心建议:
六、总结
前言
在 Kubernetes(K8s)集群中,容器的文件系统默认是临时性的 —— 容器崩溃重启后数据会丢失,同一 Pod 内的多个容器也无法直接共享文件。为了解决这两个核心问题,K8s 引入了Volume抽象,而PV(Persistent Volume,持久化存储卷)与PVC(Persistent Volume Claim,持久化存储请求)则是在Volume基础上进一步优化的持久化存储方案,让存储管理更灵活、更符合生产环境需求。本文将从基础存储卷入手,逐步深入 PV/PVC 的核心机制、实战配置与动态存储方案。
一、先搞懂:K8s 基础存储卷(Volume)
在学习 PV/PVC 之前,先了解 K8s 原生的三种基础存储卷,它们是 PV/PVC 的技术基础,各自适用于不同场景:
1. emptyDir:Pod 级临时存储
- 核心特点:Pod 调度到节点时自动创建,Pod 删除后数据随之销毁,仅支持同一 Pod 内的容器共享。
- 适用场景:临时缓存、容器间临时数据交换(如日志转发、临时计算结果存储)。
- 简单示例:两个容器共享
emptyDir卷,一个写入日期,一个通过 Nginx 提供访问:apiVersion: v1 kind: Pod metadata: name: pod-emptydir spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ - name: busybox image: busybox:latest volumeMounts: - name: html mountPath: /data/ command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done'] volumes: - name: html emptyDir: {}二、PV 与 PVC:存储资源的 “供需分离”
基础存储卷虽然解决了临时共享和单节点持久化问题,但在生产环境中存在明显短板:
- 开发者需要关心底层存储细节(如 NFS 服务器地址、挂载路径),耦合度高;
- 存储资源无法统一管理,权限控制复杂;
- 无法动态分配存储,扩展性差。
PV 与 PVC 的核心设计理念是 **“供需分离”**:运维人员统一管理底层存储资源(定义 PV),开发者只需声明存储需求(创建 PVC),K8s 自动完成 PVC 与 PV 的绑定,开发者无需关注底层存储实现。
1. 核心概念与角色分工
| 组件 | 作用 | 维护者 | 核心配置 |
|---|---|---|---|
| PV | 定义底层存储资源(如 NFS 路径、容量、访问模式) | 运维工程师 | 存储类型(nfs/hostPath)、容量(capacity)、访问模式(accessModes)、回收策略(persistentVolumeReclaimPolicy) |
| PVC | 声明存储需求(如容量、访问模式) | 应用开发者 | 申请容量(storage)、访问模式、存储类(storageClassName) |
| StorageClass | 提供动态创建 PV 的模板(可选) | 运维工程师 | 关联存储分配器(Provisioner)、默认回收策略 |
2. 关键配置详解
(1)访问模式(accessModes)
定义存储卷的访问权限,PV 与 PVC 的访问模式必须匹配(PVC 的访问模式需是 PV 的子集):
ReadWriteOnce(RWO):可读可写,仅支持单个 Pod 挂载(所有存储类型均支持);ReadOnlyMany(ROX):只读,支持多个 Pod 挂载(NFS、GlusterFS 等支持);ReadWriteMany(RWX):可读可写,支持多个 Pod 共享(仅 NFS、Ceph 等分布式存储支持)。
(2)回收策略(persistentVolumeReclaimPolicy)
PV 被 PVC 释放后的处理方式:
Retain(保留):保留数据,需手动清理(适合重要数据,避免误删);Delete(删除):自动删除底层存储资源(仅云存储如 AWS EBS、Azure Disk 支持);Recycle(回收):清空数据后重新变为可用(仅 NFS/HostPath 支持,已逐步废弃)。
3. PV 与 PVC 的生命周期
- Provisioning(配置):运维手动创建 PV(静态),或通过 StorageClass 动态创建;
- Binding(绑定):K8s 根据 PVC 的需求(容量、访问模式)匹配可用 PV,绑定后 PV 状态变为
Bound; - Using(使用):Pod 通过 PVC 挂载存储卷,持续使用直至 Pod 销毁;
- Releasing(释放):删除 PVC 后,PV 状态变为
Released,数据保留; - Recycling(回收):根据回收策略处理 PV(保留 / 删除 / 清空),处理后状态变为
Available可重新绑定。
三、实战:NFS + PV + PVC 静态存储部署
下面通过 “NFS 作为底层存储,手动创建 PV,开发者通过 PVC 申请” 的流程,实现集群级持久化存储。
1. 前置准备:部署 NFS 服务端
假设 NFS 服务端地址为stor01(192.168.10.13),创建 5 个共享目录供 PV 使用:
使用以下命令安装NFS服务和依赖组件:
yum install -y nfs-utils rpcbind启动并设置开机自启NFS和RPC服务:
systemctl start rpcbind nfs systemctl enable rpcbind nfs创建共享目录
创建多个共享目录并设置权限:
mkdir -p /data/volumes/v{1..5} chmod 777 /data/volumes/*配置NFS共享
编辑/etc/exports文件,配置集群网段访问权限:
vim /etc/exports文件内容示例:
/data/volumes/v1 192.168.10.0/24(rw,no_root_squash) /data/volumes/v2 192.168.10.0/24(rw,no_root_squash) /data/volumes/v3 192.168.10.0/24(rw,no_root_squash) /data/volumes/v4 192.168.10.0/24(rw,no_root_squash) /data/volumes/v5 192.168.10.0/24(rw,no_root_squash)生效配置
重新加载NFS配置使更改生效:
exportfs -arv2. 运维视角:创建 PV
定义 5 个 PV,分别对应 NFS 的 5 个共享目录,配置不同容量和访问模式:
apiVersion: v1 kind: PersistentVolume metadata: name: pv003 spec: nfs: path: /data/volumes/v3 server: stor01 accessModes: ["ReadWriteOnce"] capacity: storage: 2Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 spec: nfs: path: /data/volumes/v4 server: stor01 accessModes: ["ReadWriteMany"] capacity: storage: 4Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv005 spec: nfs: path: /data/volumes/v5 server: stor01 accessModes: ["ReadWriteOnce"] capacity: storage: 5Gi persistentVolumeReclaimPolicy: Retain执行创建命令并查看 PV 状态:
kubectl apply -f pv-demo.yaml kubectl get pv3. 开发者视角:创建 PVC 与 Pod
开发者无需关心 NFS 地址和路径,只需声明 “2Gi 容量、支持 RWX 访问” 的需求:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc spec: accessModes: ["ReadWriteMany"] storageClassName: "standard" # 指定存储类,动态分配存储 resources: requests: storage: 2Gi --- apiVersion: v1 kind: Pod metadata: name: pod-vol-pvc spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html command: ["/bin/sh", "-c"] # 添加启动命令 args: - while true; do echo "$(date) - Hello from PVC" > /usr/share/nginx/html/index.html; sleep 10; done volumes: - name: html persistentVolumeClaim: claimName: mypvc执行部署并验证:
实现PVC和Pod的创建与测试
以下是使用Kubernetes YAML文件创建PVC和Pod,并测试数据持久化的完整代码示例:
操作步骤
执行以下命令部署PVC和Pod:
kubectl apply -f pod-vol-pvc.yaml验证PVC状态(应显示Bound状态):
kubectl get pvc在NFS服务端创建测试文件(假设PV对应的路径是/data/volumes/v3):
echo "welcome to use pv3" > /data/volumes/v3/index.html验证数据持久化效果:
kubectl get pods -o wide curl <Pod_IP>四、进阶:StorageClass 动态存储方案
静态 PV 方案需要运维手动创建大量 PV,不适用于大规模集群。StorageClass通过 “存储分配器(Provisioner)” 实现 PV 的动态创建 —— 开发者创建 PVC 后,K8s 自动生成对应的 PV 和 NFS 共享目录,大幅降低运维成本。
1. 核心原理
StorageClass:定义动态 PV 的模板(关联 Provisioner、回收策略等);Provisioner:存储分配器(如nfs-client-provisioner),负责与 NFS 服务端交互,自动创建共享目录和 PV;RBAC权限:授权 Provisioner 操作 PV、PVC、StorageClass 等资源的权限。
2. 动态存储部署步骤
(1)配置 RBAC 权限
创建 ServiceAccount 和集群角色,授权 Provisioner 管理存储资源:
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-client-provisioner-clusterrole rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: nfs-client-provisioner-clusterrolebinding subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-clusterrole apiGroup: rbac.authorization.k8s.io(2)部署 NFS Provisioner
Provisioner 是动态创建 PV 的核心组件,需关联 NFS 服务端:
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner spec: replicas: 1 selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: quay.io/external_storage/nfs-client-provisioner:latest env: - name: PROVISIONER_NAME value: nfs-storage - name: NFS_SERVER value: stor01 - name: NFS_PATH value: /opt/k8s volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes volumes: - name: nfs-client-root nfs: server: stor01 path: /opt/k8s(3)创建 StorageClass
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-client annotations: storageclass.kubernetes.io/is-default-class: "false" # 设为false避免冲突 provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: archiveOnDelete: "false" # true保留PVC数据,false自动删除 pathPattern: "${.PVC.namespace}/${.PVC.name}" # 存储路径命名规则 onDelete: "delete" # 保留策略:delete/retain/archive mountOptions: - nfsvers=4.1 # 指定NFS协议版本 - noatime # 禁用访问时间更新 allowVolumeExpansion: true # 允许动态扩容 reclaimPolicy: Delete # 回收策略:Delete/Retain volumeBindingMode: Immediate # 绑定模式:Immediate/WaitForFirstConsumer(4)测试动态存储
创建 PVC 时指定storageClassName,K8s 自动生成 PV:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-nfs-pvc spec: accessModes: ["ReadWriteMany"] storageClassName: nfs-client-storageclass resources: requests: storage: 1Gi --- apiVersion: v1 kind: Pod metadata: name: test-storageclass-pod spec: containers: - name: busybox image: busybox:latest command: ["/bin/sh", "-c", "sleep 3600"] volumeMounts: - name: nfs-pvc mountPath: /mnt volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-nfs-pvc验证动态创建结果:
操作流程
部署存储类和PVC
kubectl apply -f storageclass-nfs.yaml验证PVC绑定状态
kubectl get pvc test-nfs-pvc # 预期输出显示STATUS为Bound,VOLUME为自动生成的PV名称检查NFS服务端共享目录
ls /opt/k8s/ # 应出现格式为{namespace}-{pvcName}-{pvName}的目录测试数据共享功能
kubectl exec -it test-storageclass-pod -- sh -c 'echo "dynamic storage test" > /mnt/test.txt'验证NFS端数据同步
cat /opt/k8s/default-test-nfs-pvc-*/test.txt # 应显示"dynamic storage test"内容五、不同存储方案对比与选型建议
| 存储类型 | 生命周期 | 共享性 | 持久性 | 适用场景 |
|---|---|---|---|---|
| emptyDir | Pod 级 | 同 Pod 内容器 | ❌ | 临时缓存、容器间临时数据交换 |
| hostPath | 节点级 | 单节点 | ✅ | 本地测试、节点日志存储 |
| NFS | 独立服务 | 多节点 | ✅ | 集群共享存储(无动态需求) |
| PV + PVC | 集群级 | 按需分配 | ✅ | 生产环境(静态存储管理) |
| StorageClass | 动态创建 | 多节点动态分配 | ✅ | 大规模集群、云原生生产环境 |
选型核心建议:
- 开发 / 测试环境:用
emptyDir(临时)或hostPath(简单持久化); - 小规模生产环境:
NFS + PV + PVC(静态管理,运维成本低); - 大规模生产环境:
StorageClass + NFS/Ceph(动态分配,适配弹性扩缩容)。
六、总结
PV 与 PVC 通过 “供需分离” 的设计,让运维专注于底层存储管理,开发者专注于业务存储需求,大幅提升了 K8s 存储的灵活性和可维护性。而 StorageClass 则进一步实现了 PV 的动态创建,完美适配大规模集群的存储需求。