深入理解 K8s 存储:PV、PVC 与 NFS
本文从 K8s 存储核心概念出发,通过 “本地目录 + PV+PVC” 和 “NFS+PV+PVC” 两个实践案例,详细讲解了 K8s 持久化存储的配置流程。重点介绍了 NFS 的跨节点共享优势,以及 StorageClass 动态 PV 的实现思路,帮助您理解 “Pod→PVC→PV→底层存储” 的层级关系。单节点存储需求:使用本地目录或 HostPath(适合测试环境)。多节点共享需求:使用
在 Kubernetes(简称 K8s)集群中,容器的临时性特点使得数据持久化存储成为关键需求。如果没有持久化存储,容器重启后数据会丢失,这对于数据库、日志存储等场景来说是无法接受的。本文将从 K8s 存储核心概念入手,结合实际操作案例,详细讲解持久卷(PV)、持久卷申领(PVC)的使用,以及如何通过 NFS 实现跨节点的数据共享,帮助您快速掌握 K8s 存储配置技巧。
一、K8s 存储核心概念解析
在开始实践前,我们需要先理清 PV、PVC、StorageClass 这三个核心组件的定义与关系,它们共同构成了 K8s 的持久化存储体系。
1. 持久卷(PersistentVolume,PV)
PV 是集群中的一块 “独立存储资源”,由管理员预先创建或通过 StorageClass 动态生成,不受 Pod 生命周期影响。它可以对应物理机目录、NFS 共享目录、云存储(如 AWS EBS、阿里云 OSS)等多种存储介质,相当于 “存储池” 中的具体存储单元。
核心属性:
- 容量(Capacity):指定 PV 的存储大小,如 10Gi、50Gi。
- 访问模式(Access Modes):定义 PV 的读写权限,共 4 种(实际常用前 3 种):
- RWO(ReadWriteOnce):仅允许单个节点挂载读写。
- ROX(ReadOnlyMany):允许多个节点挂载只读。
- RWX(ReadWriteMany):允许多个节点挂载读写(NFS、GlusterFS 等分布式存储支持)。
- RWOP(ReadWriteOncePod):仅允许单个 Pod 挂载读写(K8s 1.23 + 支持)。
- 回收策略(Reclaim Policy):PVC 删除后 PV 的处理方式:
- Retain(保留):PV 保留数据,需管理员手动清理(推荐生产环境使用)。
- Delete(删除):PV 随 PVC 删除,同时删除底层存储(如云存储卷)。
- Recycle(回收):清空 PV 数据后重新变为 “可用” 状态(已废弃,不推荐)。
- 存储类(StorageClass):PV 的 “分类标签”,PVC 通过指定 StorageClass 名称与 PV 绑定,实现存储资源的按需匹配。
2. 持久卷申领(PersistentVolumeClaim,PVC)
PVC 是用户对存储资源的 “请求单”,类似 Pod 对 CPU、内存的资源申请。用户无需关心底层存储细节(如存储介质、服务器地址),只需通过 PVC 声明所需的存储容量、访问模式和存储类,K8s 会自动匹配符合条件的 PV 并完成绑定。
核心逻辑:Pod 不直接使用 PV,而是通过 PVC 间接引用存储资源,层级关系为:Pod → Volume(引用PVC) → PVC → PV → 底层存储(如NFS、本地目录)
3. 存储类(StorageClass)
StorageClass 的核心作用是 “分类管理存储资源”,解决 PV 手动创建的繁琐问题。管理员可以为不同类型的存储(如高性能 SSD、普通机械硬盘、NFS)定义不同的 StorageClass,用户通过 PVC 指定 StorageClass,即可自动获取对应的存储资源(需搭配 Provisioner 实现动态 PV 创建)。
二、基础实践:本地目录 + PV+PVC 配置
首先从最简单的 “本地目录存储” 开始,演示 PV、PVC 的创建与使用,理解存储资源的绑定流程。
1. 环境准备
假设已拥有一个 K8s 集群(1 个 Master 节点 + 2 个 Node 节点),需在所有节点执行以下操作,创建存储目录并准备测试文件:
# 创建本地存储目录
mkdir -p /web/data
# 新建测试首页文件(用于后续验证数据挂载)
sh -c "echo 'Hello from Kubernetes storage' > /web/data/index.html"
# 验证文件是否创建成功
cat /web/data/index.html
执行结果应显示:Hello from Kubernetes storage
,表示本地目录和测试文件已准备完成。
2. 创建 PV(持久卷)
PV 的配置通过 YAML 文件定义,需指定存储类型、容量、访问模式和本地目录路径。在 Master 节点创建pv-volume.yaml
文件,内容如下:
vim pv-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume # PV名称,需唯一
labels:
type: local # 自定义标签,用于筛选
spec:
storageClassName: sanchuang # 存储类名称,PVC需匹配此值
capacity:
storage: 10Gi # PV容量
accessModes:
- ReadWriteOnce # 访问模式:单节点读写
hostPath:
path: "/web/data" # 关联本地存储目录
执行以下命令创建 PV:
bash
# 应用YAML文件创建PV
kubectl apply -f pv-volume.yaml
# 查看PV状态(STATUS为Available表示可用)
kubectl get pv
# 查看PV详细信息(验证配置是否正确)
kubectl describe pv task-pv-volume
预期输出中,PV 的STATUS
应为Available
,StorageClass
为sanchuang
,Source
字段显示HostPath: /web/data
,表示 PV 创建成功且可。
3. 创建 PVC(持久卷申领)
PVC 需与 PV 的storageClassName
和accessModes
保持一致,否则会绑定失败(状态为 Pending)。在 Master 节点创建pvc-claim.yaml
文件:
vim pvc-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim # PVC名称,需唯一
spec:
storageClassName: sanchuang # 匹配PV的存储类名称
accessModes:
- ReadWriteOnce # 匹配PV的访问模式
resources:
requests:
storage: 3Gi # 申领3Gi存储(需小于PV容量10Gi)
执行命令创建 PVC 并验证绑定状态:
# 应用YAML文件创建PVC
kubectl apply -f pvc-claim.yaml
# 查看PVC状态(STATUS为Bound表示绑定成功)
kubectl get pvc
若输出中 PVC 的STATUS
为Bound
,VOLUME
字段显示task-pv-volume
(对应 PV 名称),说明 PVC 与 PV 已成功绑定。
4. 创建 Pod 使用 PVC 存储
创建一个 Nginx Pod,将 PVC 挂载到容器的 Nginx 网页目录(/usr/share/nginx/html
),实现数据持久化。创建pod-pv-pvc.yaml
文件:
vim pod-pv-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod # Pod名称
spec:
nodeName: node1 # 定向调度到node1节点(可选)
volumes:
- name: task-pv-storage # 卷名称,需与volumeMounts对应
persistentVolumeClaim:
claimName: task-pv-claim # 引用PVC名称
containers:
- name: task-pv-container
image: nginx # 使用Nginx镜像
imagePullPolicy: IfNotPresent # 本地有镜像则不重新拉取
ports:
- containerPort: 80 # Nginx默认端口
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html" # 容器内挂载路径(Nginx网页目录)
name: task-pv-storage # 关联卷名称
执行命令创建 Pod 并验证:
bash
# 应用YAML文件创建Pod
kubectl apply -f pod-pv-pod.yaml
# 查看Pod状态(STATUS为Running表示正常运行)
kubectl get pod -o wide
待 Pod 运行后,通过 Pod 的 IP 地址访问 Nginx 服务,验证存储是否生效:
bash
# 替换为实际Pod的IP(从kubectl get pod -o wide输出中获取)
curl 10.224.166.180
预期输出Hello from Kubernetes storage
,表示本地目录的测试文件已通过 PV/PVC 挂载到 Pod 中。
5. 验证数据持久化
修改 Node1 节点本地目录的index.html
文件,模拟数据更新:
bash
# 在Node1节点执行,修改测试文件
vim /web/data/index.html
# 添加内容:welcome to sanchuang
# 保存后再次访问Pod
curl 10.224.166.180
预期输出包含welcome to sanchuang
,说明容器内的数据随本地目录更新而同步,验证了数据持久化效果。
三、进阶实践:NFS+PV+PVC 实现跨节点共享
本地目录存储的局限性在于 “仅能在单个节点使用”,若 Pod 调度到其他节点,会无法访问存储。而 NFS(网络文件系统)支持跨节点共享目录,是 K8s 集群中常用的分布式存储方案。以下演示如何通过 NFS 实现多节点存储共享。
1. 搭建 NFS 服务器
步骤 1:选择 NFS 服务器节点
可在 K8s 集群内选择一个节点(如 Master 节点,IP:192.168.203.128)作为 NFS 服务器,也可使用独立服务器。
步骤 2:安装 NFS 服务
在NFS 服务器节点和所有 K8s 节点安装 NFS 工具(否则节点无法挂载 NFS 目录):
bash
# 安装NFS工具(CentOS/RHEL系统)
yum install nfs-utils -y
步骤 3:配置 NFS 共享目录
在 NFS 服务器节点执行以下操作,创建共享目录并配置权限:
bash
# 创建NFS共享目录
mkdir -p /web/data
# 准备测试文件(与本地存储一致,便于后续验证)
echo "welcome to sanchuang !!" > /web/data/index.html
# 配置NFS共享(编辑/etc/exports文件)
vim /etc/exports
# 添加以下内容(允许192.168.203.0/24网段访问,权限为读写)
/web/data 192.168.203.0/24(rw,sync,all_squash)
# 刷新NFS配置(使共享生效)
exportfs -rv
# 启动NFS服务并设置开机自启
systemctl start nfs-server
systemctl enable nfs-server
# 关闭防火墙(避免端口拦截,生产环境可配置白名单)
systemctl stop firewalld
systemctl disable firewalld
步骤 4:验证 NFS 共享
在任意 K8s 节点(如 Node1)测试 NFS 挂载,确保跨节点访问正常:
bash
# 创建临时挂载目录
mkdir -p /pv_pvc_nfs
# 挂载NFS共享目录
mount 192.168.203.128:/web/data /pv_pvc_nfs/
# 查看挂载结果(若显示NFS目录,则挂载成功)
df -Th | grep nfs
# 验证文件访问(应显示NFS服务器上的测试内容)
cat /pv_pvc_nfs/index.html
若输出welcome to sanchuang !!
,说明 NFS 服务器搭建成功,跨节点共享正常。
2. 创建 NFS 类型的 PV
与本地目录 PV 不同,NFS 类型的 PV 需指定 NFS 服务器地址和共享目录。在 Master 节点创建nfs-pv.yaml
文件:
vim nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: sc-nginx-pv # PV名称
labels:
type: sc-nginx-pv # 自定义标签
spec:
capacity:
storage: 50Gi # PV容量(需大于PVC申领容量)
accessModes:
- ReadWriteMany # 支持多节点读写(NFS核心优势)
storageClassName: nfs # 存储类名称(PVC需匹配)
nfs:
path: "/web/data" # NFS共享目录
server: 192.168.203.128 # NFS服务器IP
readOnly: false # 非只读模式
执行命令创建 PV 并验证:
# 创建PV
kubectl apply -f nfs-pv.yaml
# 查看PV状态(STATUS为Available表示可用)
kubectl get pv
3. 创建 NFS 类型的 PVC
创建pvc-nfs.yaml
文件,申领 NFS 类型的存储资源:
vim pvc-nfs.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sc-nginx-pvc # PVC名称
spec:
accessModes:
- ReadWriteMany # 匹配PV的多节点读写模式
resources:
requests:
storage: 40Gi # 申领40Gi存储(小于PV的50Gi)
storageClassName: nfs # 匹配NFS类型PV的存储类
执行命令创建 PVC 并验证绑定:
# 创建PVC
kubectl apply -f pvc-nfs.yaml
# 查看PVC状态(STATUS为Bound表示绑定成功)
kubectl get pvc
4. 部署多副本 Pod 使用 NFS 存储
为了体现 NFS “多节点共享” 的优势,使用 Deployment 创建 4 个 Nginx 副本,所有 Pod 共享 NFS 存储。创建pod-nfs.yaml
文件:
vim pod-nfs.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-nfs # Deployment名称
labels:
app: nginx-nfs
spec:
replicas: 4 # 4个Pod副本
selector:
matchLabels:
app: nginx-nfs
template:
metadata:
labels:
app: nginx-nfs
spec:
volumes:
- name: sc-pv-storage-nfs # 卷名称
persistentVolumeClaim:
claimName: sc-nginx-pvc # 引用NFS类型的PVC
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html" # 容器内挂载路径
name: sc-pv-storage-nfs # 关联卷名称
执行命令部署 Pod 并验证:
# 部署Deployment
kubectl apply -f pod-nfs.yaml
# 查看Pod状态(4个Pod均为Running表示正常)
kubectl get pod -o wide
5. 验证多节点共享效果
通过 Pod 的 IP 地址访问 Nginx 服务,验证所有 Pod 均能读取 NFS 存储的文件:
# 替换为实际Pod的IP(从kubectl get pod -o wide输出中获取)
curl 10.224.166.136
curl 10.224.104.2
所有请求均应输出welcome to sanchuang !!
,说明多节点 Pod 成功共享 NFS 存储。
6. 暴露 Service 供外部访问
为了让外部机器(如 Windows)能访问 NFS 存储的 Nginx 服务,创建 NodePort 类型的 Service。创建service-pod-nfs.yaml
文件:
apiVersion: v1
kind: Service
metadata:
name: my-service-pod-nfs # Service名称
spec:
type: NodePort # NodePort类型(外部可通过节点IP+端口访问)
selector:
app: nginx-nfs # 匹配Deployment的Pod标签
ports:
- port: 80 # Service内部端口
targetPort: 80 # Pod内部端口
nodePort: 30007 # 外部访问端口(范围:30000-32767)
执行命令创建 Service 并验证:
# 创建Service
kubectl apply -f service-pod-nfs.yaml
# 查看Service信息(确认NodePort端口)
kubectl get svc
在 Windows 浏览器中输入http://K8s节点IP:30007
(如http://192.168.203.130:30007
),即可访问 Nginx 服务,显示 NFS 存储的网页内容。
7. 验证 NFS 数据同步更新
在 NFS 服务器节点修改index.html
文件,模拟数据更新:
bash
# 在NFS服务器执行
vim /web/data/index.html
# 添加内容:welcome to sanchuang teacherfeng nfs pv pvc
再次通过浏览器或curl
访问 Pod/Service,所有请求均会显示更新后的内容,验证了 NFS 数据的实时同步效果。
四、高级技巧:StorageClass 与动态 PV
在前面的实践中,PV 需要管理员手动创建,当集群规模较大时,手动管理 PV 效率低下。StorageClass 结合 Provisioner 可以实现 PV 的 “动态创建”,用户只需创建 PVC,K8s 会自动生成对应的 PV。
1. StorageClass 核心作用
- 简化存储管理:管理员无需手动创建 PV,只需定义 StorageClass 模板。
- 按需分配资源:用户通过 PVC 指定 StorageClass,自动获取匹配的存储资源。
- 支持多存储类型:可为不同存储介质(如 SSD、NFS、云存储)定义不同的 StorageClass。
2. NFS 动态 PV 实现思路
要实现 NFS 动态 PV,需部署nfs-client-provisioner
(NFS 客户端自动配置程序),它会监听 PVC 请求,自动在 NFS 服务器上创建目录并生成 PV。核心流程如下:
- 部署 NFS 服务器(已完成)。
- 部署
nfs-client-provisioner
(通过 Pod 运行)。 - 创建 StorageClass,关联
nfs-client-provisioner
。 - 用户创建 PVC,Provisioner 自动创建 PV 并绑定。
3. 动态 PV 优势
- 无需手动创建 PV,减少管理员工作量。
- PVC 删除后,PV 和 NFS 目录可自动清理(根据回收策略配置)。
- 支持按 PVC 需求动态调整存储容量(需 NFS 服务器支持)。
五、常见问题与解决方案
在 K8s 存储配置过程中,容易遇到各种问题,以下是常见问题及解决方法:
1. PVC 状态一直为 Pending
- 原因 1:PVC 的
storageClassName
与 PV 不匹配。- 解决:确保 PVC 的
storageClassName
与 PV 一致。
- 解决:确保 PVC 的
- 原因 2:PVC 的
accessModes
与 PV 不匹配(如 PVC 请求 RWX,PV 仅支持 RWO)。- 解决:调整 PVC 或 PV 的
accessModes
,确保一致。
- 解决:调整 PVC 或 PV 的
- 原因 3:PVC 申领容量超过 PV 容量。
- 解决:减小 PVC 的
storage
请求,或增大 PV 容量。
- 解决:减小 PVC 的
2. Pod 挂载 NFS 失败(状态为 CrashLoopBackOff)
- 原因 1:K8s 节点未安装
nfs-utils
工具。- 解决:在所有节点执行
yum install nfs-utils -y
。
- 解决:在所有节点执行
- 原因 2:NFS 服务器防火墙未关闭,端口被拦截。
- 解决:在 NFS 服务器执行
systemctl stop firewalld
,或配置 NFS 端口白名单。
- 解决:在 NFS 服务器执行
- 原因 3:NFS 共享目录权限不足(如未设置 777 权限)。
- 解决:在 NFS 服务器执行
chmod 777 /web/data
,确保节点可读写。
- 解决:在 NFS 服务器执行
3. 数据更新后 Pod 未同步
- 原因:NFS 客户端缓存导致数据延迟。
- 解决:在 PV 配置中添加 NFS 挂载参数(如
mountOptions: ["noatime", "nodiratime"]
),或重启 Pod 刷新缓存。
- 解决:在 PV 配置中添加 NFS 挂载参数(如
六、总结
本文从 K8s 存储核心概念出发,通过 “本地目录 + PV+PVC” 和 “NFS+PV+PVC” 两个实践案例,详细讲解了 K8s 持久化存储的配置流程。重点介绍了 NFS 的跨节点共享优势,以及 StorageClass 动态 PV 的实现思路,帮助您理解 “Pod→PVC→PV→底层存储” 的层级关系。
在实际生产环境中,需根据业务需求选择合适的存储方案:
- 单节点存储需求:使用本地目录或 HostPath(适合测试环境)。
- 多节点共享需求:使用 NFS、GlusterFS 等分布式存储(适合生产环境)。
- 大规模集群:结合 StorageClass 与 Provisioner,实现 PV 动态创建,提升管理效率。
通过本文的实践,相信您已能熟练配置 K8s 存储,为容器应用提供稳定、可靠的持久化支持。

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。
更多推荐
所有评论(0)