kubernetes(k8s)-pv、pvc
Kubernetes 中 PV(持久卷)和 PVC(持久卷声明)是实现有状态应用持久化存储的核心。
一、PV 和 PVC 是什么?为什么需要它们?
核心问题:Pod 的存储难题
1、Pod 是短暂的:Pod 及其内部数据会在重启、迁移或销毁时丢失。
2、存储需求是持久的:数据库(如 MySQL)、消息队列(如 Kafka)、配置文件等都需要数据在 Pod 生命周期之外依然存在。
3、存储与 Pod 的解耦:我们不希望 Pod 的定义和底层存储细节(如如何配置 NFS、云盘等)强耦合。
PV 和 PVC 的引入
为了解决上述问题,Kubernetes 引入了两个 API 资源:
PV(PersistentVolume,持久卷)
- 是什么:集群中的一块网络存储资源,由集群管理员预先创建和配置好。它就像是集群中的一块“物理”硬盘。
- 类比:就像是 IT 部门采购回来的一整块移动硬盘或云盘。
- 生命周期:独立于使用它的 Pod。
PVC(PersistentVolumeClaim,持久卷声明)
- 是什么:用户(开发者)对存储的请求。它指定了需要多大的存储空间、访问模式等。
- 类比:就像是公司员工向 IT 部门提交的申请单,写明“我需要一块 10GB 的、可以读写的高速硬盘”。
- 作用:Pod 通过 PVC 来消费 PV 资源。PVC 让用户无需关心底层存储的具体实现。
核心思想:分离“存储的供应”和“存储的使用”
- 管理员 负责创建和管理 PV(提供存储资源)。
- 开发者 负责创建 PVC 来申请存储资源,并在 Pod 中引用 PVC(使用存储资源)。
二、核心概念和作用
1、PV 的生命周期
PV 会处于以下四个阶段之一:
- Available(可用):空闲状态,尚未被任何 PVC 绑定。
- Bound(已绑定):已经被一个 PVC 绑定。
- Released(已释放):绑定的 PVC 已被删除,但资源还未被集群回收。
- Failed(失败):自动回收失败。
2、访问模式
PVC 可以请求 PV 以特定的访问模式挂载:
- ReadWriteOnce(RWO):可被单个节点以读写模式挂载。
- ReadOnlyMany(ROX):可被多个节点以只读模式挂载。
- ReadWriteMany(RWX):可被多个节点以读写模式挂载。
- ReadWriteOncePod(RWOP):可被单个 Pod 以读写模式挂载。(v1.22+,Alpha)这是确保单个 Pod 独占访问的关键模式,非常适合数据库。
注意:具体的模式支持取决于底层的存储插件(如 NFS 支持 RWX,而大多数块存储如 AWS EBS 只支持 RWO)。
3、存储类别
- StorageClass(存储类):允许管理员描述他们提供的存储的“类别”(如
fast-ssd,slow-hdd)。 - 动态配置:通过 StorageClass,可以实现 PV 的动态创建。当用户创建 PVC 时,如果找不到合适的静态 PV,系统会根据 PVC 中指定的 StorageClass 自动创建一个 PV。这是生产环境中最常用的方式。
4、回收策略
当 PVC 被删除后,其绑定的 PV 如何处理:
- Retain(保留):默认策略。保留数据和 PV 对象,需要管理员手动清理和回收。
- Delete(删除):自动删除 PV 以及后端存储(如云硬盘)。
- Recycle(回收)(已弃用):基本擦除(如
rm -rf /volume/*),然后使其变为 Available。
三、经典案例
我们将通过两个场景来演示:
-
静态配置:管理员预先创建好 PV。
-
动态配置:使用 StorageClass 动态创建 PV(生产环境推荐)。
场景准备:使用 NFS 作为后端存储
假设我们有一个 NFS 服务器,地址为 192.168.1.100,导出了路径 /data/nfs。
案例 1:静态配置 PV/PVC
步骤 1:集群管理员创建 PV
pv-static.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-static-1
labels:
type: nfs # 可以打上标签,方便 PVC 选择
spec:
capacity:
storage: 5Gi # PV 的容量
volumeMode: Filesystem
accessModes:
- ReadWriteMany # NFS 支持多节点读写
persistentVolumeReclaimPolicy: Retain # 回收策略:保留
storageClassName: manual # 可选的存储类名,如果这里指定了,PVC 也必须指定相同的才能匹配
nfs: # 指定存储后端为 NFS
path: /data/nfs/pv1 # NFS 服务器上的子路径
server: 192.168.1.100
创建 PV:
kubectl apply -f pv-static.yaml
步骤 2:开发者创建 PVC
pvc-static.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-static
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi # 申请 5GB 空间
# storageClassName: manual # 如果 PV 指定了,这里也必须指定相同的。如果 PV 没指定,这里也不能指定(或设为"")才能匹配。
# 可以选择器来匹配特定 PV
# selector:
# matchLabels:
# type: nfs
创建 PVC:
kubectl apply -f pvc-static.yaml
步骤 3:在 Pod 中使用 PVC
pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod-static
spec:
containers:
- name: nginx-container
image: nginx:alpine
volumeMounts:
- name: storage-volume
mountPath: /usr/share/nginx/html # 将卷挂载到容器内的路径
volumes:
- name: storage-volume
persistentVolumeClaim:
claimName: pvc-static # 使用之前创建的 PVC
验证:
1、查看 PV 和 PVC 状态应为 Bound:
kubectl get pv pv-static-1
kubectl get pvc pvc-static
2、进入 Pod,在挂载点 /usr/share/nginx/html 创建一个文件:
kubectl exec -it test-pod-static -- sh
cd /usr/share/nginx/html
echo "Hello from Static PV!" > index.html
3、即使删除 Pod test-pod-static,然后重新创建一个使用相同 PVC 的新 Pod,之前创建的文件依然存在。
案例 2:动态配置 PV/PVC(生产环境推荐)
动态配置无需管理员预先创建 PV,而是通过 StorageClass 来实现
步骤 1:创建 StorageClass
首先,需要确保你的 Kubernetes 集群有对应的 CSI(容器存储接口)驱动。这里我们以 NFS 为例(通常需要先部署 NFS CSI 驱动,如 nfs-subdir-external-provisioner)。
storageclass-nfs.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client # StorageClass 的名称
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 必填,指定用于动态创建的 provisioner
parameters:
archiveOnDelete: "false" # 删除 PVC 时是否归档数据
reclaimPolicy: Delete # PVC 删除后,PV 的处理策略
volumeBindingMode: Immediate
创建 SC:
kubectl apply -f storageclass-nfs.yaml
步骤 2:开发者创建 PVC(并指定 StorageClass)
pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic
spec:
storageClassName: nfs-client # 关键:指定使用哪个 StorageClass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi # 申请 10GB 空间
创建 PVC:
kubectl apply -f pvc-dynamic.yaml
神奇的事情发生了:
1、当你创建这个 PVC 后,Kubernetes 会自动根据 nfs-client 这个 StorageClass 的配置,创建一个对应的 PV!
2、查看 PV 和 PVC,它们会自动绑定:
kubectl get pvc pvc-dynamic # 状态应为 Bound
kubectl get pv # 你会看到一个自动创建的 PV,名称类似 `pvc-<random-id>`
步骤 3:在 Pod 中使用动态创建的 PVC
pod-with-dynamic-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod-dynamic
spec:
containers:
- name: nginx-container
image: nginx:alpine
volumeMounts:
- name: storage-volume
mountPath: /usr/share/nginx/html
volumes:
- name: storage-volume
persistentVolumeClaim:
claimName: pvc-dynamic # 使用动态创建的 PVC
创建 Pod:
kubectl apply -f pod-with-dynamic-pvc.yaml
验证:
与静态配置类似,在挂载点创建的文件会被持久化。当你删除 PVC pvc-dynamic 时,由于回收策略是 Delete,对应的 PV 和后端存储空间也会被自动清理。
四、StatefulSet 与 PV/PVC
对于有状态应用(如 MySQL、Redis、Kafka),我们通常使用 StatefulSet 控制器,它与 PV/PVC 有更紧密的集成:
-
稳定的网络标识:Pod 名称是有序、稳定的。
-
稳定的专用存储:每个 Pod 实例都会根据模板自动创建自己独有的 PVC 和 PV。
-
Pod
mysql-0-> PVCdata-mysql-0-> PV-1 -
Pod
mysql-1-> PVCdata-mysql-1-> PV-2
-
-
即使 Pod 被重新调度,它也会重新绑定到原来专属的 PV 上,保证了数据的持久性和一致性。
总结
| 特性 | PV (PersistentVolume) | PVC (PersistentVolumeClaim) |
|---|---|---|
| 角色 | 集群资源(如“硬盘”) | 用户请求(如“申请单”) |
| 创建者 | 集群管理员 / StorageClass(动态) | 应用开发者 / StatefulSet |
| 目的 | 提供存储 | 消费存储 |
| 关注点 | 底层存储细节(NFS, EBS 等) | 存储需求(大小、访问模式) |
核心价值:
-
解耦:将存储的供应与使用分离,开发者无需关心底层存储实现。
-
自动化:通过 StorageClass 实现动态配置,极大简化了存储管理。
-
持久化:为有状态应用提供了可靠的数据持久化方案,是运行数据库、中间件等关键服务的基础。
更多推荐
所有评论(0)