目录
[一、先搞懂:K8s 基础存储卷(Volume)](#一、先搞懂:K8s 基础存储卷(Volume))
[1. emptyDir:Pod 级临时存储](#1. emptyDir:Pod 级临时存储)
[二、PV 与 PVC:存储资源的 "供需分离"](#二、PV 与 PVC:存储资源的 “供需分离”)
[1. 核心概念与角色分工](#1. 核心概念与角色分工)
[2. 关键配置详解](#2. 关键配置详解)
(2)回收策略(persistentVolumeReclaimPolicy)
[3. PV 与 PVC 的生命周期](#3. PV 与 PVC 的生命周期)
[三、实战:NFS + PV + PVC 静态存储部署](#三、实战:NFS + PV + PVC 静态存储部署)
[1. 前置准备:部署 NFS 服务端](#1. 前置准备:部署 NFS 服务端)
[2. 运维视角:创建 PV](#2. 运维视角:创建 PV)
[3. 开发者视角:创建 PVC 与 Pod](#3. 开发者视角:创建 PVC 与 Pod)
[四、进阶:StorageClass 动态存储方案](#四、进阶:StorageClass 动态存储方案)
[1. 核心原理](#1. 核心原理)
[2. 动态存储部署步骤](#2. 动态存储部署步骤)
[(1)配置 RBAC 权限](#(1)配置 RBAC 权限)
[(2)部署 NFS Provisioner](#(2)部署 NFS Provisioner)
[(3)创建 StorageClass](#(3)创建 StorageClass)
前言
在 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 提供访问:yamlapiVersion: 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服务和依赖组件:
bash
yum install -y nfs-utils rpcbind
启动并设置开机自启NFS和RPC服务:
bash
systemctl start rpcbind nfs
systemctl enable rpcbind nfs
创建共享目录
创建多个共享目录并设置权限:
bash
mkdir -p /data/volumes/v{1..5}
chmod 777 /data/volumes/*
配置NFS共享
编辑/etc/exports文件,配置集群网段访问权限:
bash
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配置使更改生效:
bash
exportfs -arv
2. 运维视角:创建 PV
定义 5 个 PV,分别对应 NFS 的 5 个共享目录,配置不同容量和访问模式:
yaml
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 状态:
bash
kubectl apply -f pv-demo.yaml
kubectl get pv
3. 开发者视角:创建 PVC 与 Pod
开发者无需关心 NFS 地址和路径,只需声明 "2Gi 容量、支持 RWX 访问" 的需求:
yaml
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:
bash
kubectl apply -f pod-vol-pvc.yaml
验证PVC状态(应显示Bound状态):
bash
kubectl get pvc
在NFS服务端创建测试文件(假设PV对应的路径是/data/volumes/v3):
bash
echo "welcome to use pv3" > /data/volumes/v3/index.html
验证数据持久化效果:
bash
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 管理存储资源:
yaml
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 服务端:
yaml
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
yaml
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:
yaml
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
bash
kubectl apply -f storageclass-nfs.yaml
验证PVC绑定状态
bash
kubectl get pvc test-nfs-pvc
# 预期输出显示STATUS为Bound,VOLUME为自动生成的PV名称
检查NFS服务端共享目录
bash
ls /opt/k8s/
# 应出现格式为{namespace}-{pvcName}-{pvName}的目录
测试数据共享功能
bash
kubectl exec -it test-storageclass-pod -- sh -c 'echo "dynamic storage test" > /mnt/test.txt'
验证NFS端数据同步
bash
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 的动态创建,完美适配大规模集群的存储需求。