K8s持久化存储:PV与PVC
在 Kubernetes(K8s)中,PV(PersistentVolume,持久卷) 和 PVC(PersistentVolumeClaim,持久卷声明) 是实现 “存储资源抽象与动态管理” 的核心组件,用于解耦 “存储资源供应”(管理员负责)和 “存储资源使用”(开发者负责),让容器化应用能便捷使用持久化存储(如数据在 Pod 重启后不丢失)。
一、核心概念
1. PV(PersistentVolume,持久卷)
- 本质:由 K8s 集群管理员创建的集群级持久化存储资源,是对底层存储(如本地磁盘、NFS、云存储 S3/EBS 等)的抽象,独立于 Pod 生命周期,Pod 删除后 PV 数据仍保留。
- 核心属性:
- 存储容量(capacity):如
storage: 10Gi
,定义 PV 可用存储空间。 - 访问模式(accessModes):规定 PV 可被如何访问,常见值:
ReadWriteOnce(RWO)
:仅允许单个节点以读写方式挂载(适合单 Pod 独占存储,如数据库)。ReadOnlyMany(ROX)
:允许多个节点以只读方式挂载(适合多 Pod 共享只读数据,如配置文件)。ReadWriteMany(RWX)
:允许多个节点以读写方式挂载(适合多 Pod 共享读写数据,如 NFS、GlusterFS)。
- 存储类别(storageClassName):关联 “存储类(StorageClass)”,用于动态创建 PV(若为
""
表示 “非动态供应”,需管理员手动创建 PV)。 - 回收策略(persistentVolumeReclaimPolicy):PV 释放后(PVC 删除)的处理方式:
Retain(保留)
:数据保留,需管理员手动清理 PV 和数据(安全,适合重要数据)。Delete(删除)
:自动删除 PV 及关联的底层存储(如云存储 EBS 卷,适合临时测试)。Recycle(回收)
:清除 PV 数据后重新变为 “可用” 状态(已废弃,推荐用Retain
或Delete
)。
- 存储容量(capacity):如
2. PVC(PersistentVolumeClaim,持久卷声明)
- 本质:由开发者创建的存储资源请求,类似 Pod 对 CPU / 内存的请求(
resources.requests
),开发者无需关心底层存储细节,只需声明 “需要多大容量、什么访问模式的存储”,K8s 会自动匹配满足条件的 PV 并绑定。 - 核心逻辑:
开发者 → 创建 PVC(请求 10Gi RWO 存储)→ K8s 匹配集群中 “容量≥10Gi、访问模式包含 RWO” 的 PV → 绑定 PVC 与 PV → Pod 通过 PVC 使用 PV 存储。
3. PV 与 PVC 的关系
维度 | PV(持久卷) | PVC(持久卷声明) |
---|---|---|
创建者 | 集群管理员(或 StorageClass 动态创建) | 应用开发者(或部署脚本) |
作用 | 提供 “存储资源” | 请求 “存储资源” |
生命周期 | 独立于 Pod,可跨 Pod 复用 | 与使用它的 Pod 关联,PVC 删除后 PV 释放 |
匹配逻辑 | 按 “容量、访问模式、存储类别” 匹配 PVC | 按 “需求” 匹配 PV,不直接关联底层存储 |
二、实战实例(以 NFS 存储为例)
前提:准备 NFS 服务
假设已搭建 NFS 服务器(IP:172.25.254.254
),并共享目录 /nfs/k8s-pv
(权限:chmod 777 /nfs/k8s-pv
),用于 PV 挂载底层存储。
1. 实例 1:手动创建 PV(静态供应)
管理员手动创建一个 NFS 类型的 PV(pv-nfs.yaml
),定义存储资源:
apiVersion: v1
kind: PersistentVolume
metadata:name: pv-nfs-10gi # PV 名称
spec:capacity:storage: 10Gi # 容量 10GiaccessModes:- ReadWriteOnce # 访问模式:单节点读写persistentVolumeReclaimPolicy: Retain # 回收策略:保留数据storageClassName: "" # 不关联存储类(静态供应)nfs: # 底层存储类型为 NFSpath: /nfs/k8s-pv # NFS 服务器共享目录server: 172.25.254.254 # NFS 服务器 IP
操作命令:
bash
# 创建 PV
kubectl apply -f pv-nfs.yaml# 查看 PV 状态(STATUS 为 Available,表示可被 PVC 绑定)
kubectl get pv pv-nfs-10gi
# 输出示例:
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
# pv-nfs-10gi 10Gi RWO Retain Available "" 30s
2. 实例 2:创建 PVC 并绑定 PV
开发者创建 PVC(pvc-nfs.yaml
),请求 10Gi RWO 存储,K8s 会自动匹配上述 PV:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc-nfs-10gi # PVC 名称namespace: default # 命名空间(PVC 是命名空间级资源)
spec:accessModes:- ReadWriteOnce # 与 PV 访问模式匹配resources:requests:storage: 10Gi # 请求容量(≤ PV 容量)storageClassName: "" # 与 PV 存储类别匹配(均为 "")
操作命令:
bash
# 创建 PVC
kubectl apply -f pvc-nfs.yaml# 查看 PVC 状态(STATUS 为 Bound,表示已与 PV 绑定)
kubectl get pvc pvc-nfs-10gi
# 输出示例:
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# pvc-nfs-10gi Bound pv-nfs-10gi 10Gi RWO "" 20s# 查看 PV 状态(STATUS 变为 Bound,CLAIM 显示绑定的 PVC)
kubectl get pv pv-nfs-10gi
# 输出示例:
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
# pv-nfs-10gi 10Gi RWO Retain Bound default/pvc-nfs-10gi "" 1min
3. 实例 3:Pod 使用 PVC 存储
创建 Pod(pod-use-pvc.yaml
),通过 volumeMounts
挂载 PVC,实现数据持久化
apiVersion: v1
kind: Pod
metadata:name: pod-with-pvcnamespace: default
spec:containers:- name: nginximage: nginx:1.21 # 用 Nginx 容器示例volumeMounts:- name: nfs-storage # 与下方 volumes.name 对应mountPath: /usr/share/nginx/html # 容器内挂载路径(Nginx 网页根目录)volumes:- name: nfs-storagepersistentVolumeClaim:claimName: pvc-nfs-10gi # 关联已绑定的 PVC 名称
验证数据持久化:
bash
# 创建 Pod
kubectl apply -f pod-use-pvc.yaml# 1. 向 Pod 挂载目录写入测试数据
kubectl exec -it pod-with-pvc -- /bin/bash
# 在容器内执行:
echo "Persistent Volume Test" > /usr/share/nginx/html/index.html
exit# 2. 删除 Pod(模拟 Pod 故障重启)
kubectl delete pod pod-with-pvc# 3. 重新创建 Pod(使用相同 PVC)
kubectl apply -f pod-use-pvc.yaml# 4. 验证数据是否保留
kubectl exec -it pod-with-pvc -- cat /usr/share/nginx/html/index.html
# 输出:Persistent Volume Test(数据未丢失,证明持久化生效)
三、核心总结
组件 | 角色 | 关键操作 |
---|---|---|
PV | 存储资源供应 | 管理员手动创建(静态)或 StorageClass 自动创建(动态),定义容量、访问模式 |
PVC | 存储资源请求 | 开发者创建,声明需求(容量、访问模式),K8s 自动匹配 PV 并绑定 |
Pod | 存储资源使用 | 通过 volumes 关联 PVC,volumeMounts 挂载到容器内路径 |
通过 PV/PVC,K8s 实现了 “存储与应用解耦”:管理员专注于存储资源管理,开发者专注于应用存储需求,同时保证数据持久化(不受 Pod 生命周期影响)。