当前位置: 首页 > news >正文

K8S(九)—— Kubernetes持久化存储深度解析:从Volume到PV/PVC与动态存储

文章目录

  • 前言
  • 一、容器存储的核心挑战与K8s Volume解决方案
    • 1.1 容器存储的短暂性问题
    • 1.2 K8s Volume的核心价值
  • 二、K8s基础存储卷实践
    • 2.1 emptyDir:Pod内临时共享存储
      • 2.1.1 核心特点
      • 2.1.2 实战配置(YAML示例)
      • 2.1.3 验证与结果
    • 2.2 hostPath:节点级持久化存储
      • 2.2.1 核心特点
      • 2.2.2 实战配置(YAML示例)
      • 2.2.3 验证与结果
    • 2.3 NFS:跨节点共享存储
      • 2.3.1 核心特点
      • 2.3.2 实战配置(分服务端与客户端)
        • 步骤1:部署NFS服务端(独立节点,如stor01)
        • 步骤2:K8s节点安装NFS客户端(所有节点)
        • 步骤3:编写NFS Volume测试Pod配置
        • 步骤4:验证与结果
  • 三、PV与PVC:K8s持久化存储的核心机制
    • 3.1 PV与PVC的核心概念
    • 3.2 PV与PVC的生命周期
      • 3.2.1 生命周期流程
      • 3.2.2 PV的核心状态
      • 3.2.3 一个PV从创建到销毁的具体流程如下
    • 3.3 PV的核心配置项
      • 3.3.1 访问模式(accessModes)
      • 3.3.2 容量(capacity)
      • 3.3.3 回收策略(persistentVolumeReclaimPolicy)
    • 3.4 PV 的示例
  • 四、NFS + PV + PVC 实战演练
    • 4.1 前置准备(NFS服务端配置)
    • 4.2 手动创建PV(静态配置)
    • 4.3 创建PVC并绑定PV
      • 4.3.1 编写PVC与Pod配置
      • 4.3.2 部署与验证
  • 五、StorageClass:实现NFS动态PV创建
    • 5.1 StorageClass的核心组件
    • 5.2 部署NFS动态存储(全流程)
      • 5.2.1 前置修复(K8s 1.20版本)
      • 5.2.2 在stor01节点上安装nfs,并配置nfs服务
      • 5.2.3 配置RBAC权限
      • 5.2.4 部署 NFS Provisioner
      • 5.2.5 创建StorageClass
      • 5.2.6 测试动态PV创建
  • 六、存储方案对比与选型建议
    • 选型建议
  • 总结

前言

在K8s集群中,容器的临时性本质带来了两个关键问题:一是容器崩溃重启后数据丢失,二是同一Pod内多容器无法直接共享文件。

为解决这些问题,K8s设计了Volume抽象,并在此基础上衍生出PV(Persistent Volume)与PVC(Persistent Volume Claim)机制,进一步通过StorageClass实现动态存储管理。本文将从基础存储卷实践出发,逐步深入PV/PVC核心原理,并结合NFS存储进行实战演练,帮助大家掌握K8s持久化存储的选型与配置。

一、容器存储的核心挑战与K8s Volume解决方案

在深入技术细节前,我们首先明确容器存储的核心痛点,以及K8s Volume抽象如何初步解决这些问题。

1.1 容器存储的短暂性问题

容器的文件系统基于镜像层和可写层,其本质是临时性的,主要体现在两个方面:

  • 数据易失性:容器因故障重启后,可写层的数据会完全丢失(例如Nginx容器内的日志文件、应用配置等);
  • 共享困难:同一Pod内的多个容器(如应用容器+日志收集容器)无法直接访问彼此的文件系统,需额外机制实现数据共享。

1.2 K8s Volume的核心价值

K8s通过Volume抽象打破了容器的存储边界,其核心设计是通过Pause容器(Pod的基础容器)实现:

  • 所有容器挂载同一个Volume,实现Pod内多容器的数据共享;
  • Volume的生命周期独立于单个容器(但默认与Pod生命周期绑定),解决容器重启数据丢失问题;
  • 支持多种存储类型(本地存储、网络存储等),适配不同场景需求。

二、K8s基础存储卷实践

K8s提供了多种基础Volume类型,适用于不同场景。本节将重点讲解emptyDirhostPathNFS三种常用类型的特点与实战。

2.1 emptyDir:Pod内临时共享存储

emptyDir是最基础的Volume类型,其生命周期与Pod完全绑定,适合临时缓存或Pod内多容器数据共享场景。

2.1.1 核心特点

  • 自动创建:Pod调度到节点时,K8s自动在节点本地创建emptyDir目录;
  • 随Pod销毁:Pod删除时,emptyDir内的数据也会被彻底清理;
  • 适合场景:仅适合临时缓存或容器间数据共享。
  • 存储介质:默认使用节点的本地磁盘,也可配置为内存(medium: Memory,适合高性能临时缓存,但需注意内存溢出风险)。

2.1.2 实战配置(YAML示例)

# 创建目录用于存放YAML文件
mkdir /opt/volumes && cd /opt/volumesvim pod-emptydir.yaml 
apiVersion: v1
kind: Pod
metadata:name: pod-emptydirnamespace: defaultlabels:app: myapptier: frontend
spec:containers:# 应用容器:Nginx,挂载emptyDir到/html目录- name: myappimage: ikubernetes/myapp:v1imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80#定义容器挂载内容volumeMounts:#使用的存储卷名称,如果跟下面volume字段name值相同,则表示使用volume的这个存储卷- name: html#挂载至容器中哪个目录mountPath: /usr/share/nginx/html/# 辅助容器:BusyBox,定期写入日期到共享目录- name: busyboximage: busybox:latestimagePullPolicy: IfNotPresentvolumeMounts:- name: html#在容器内定义挂载存储名称和挂载路径mountPath: /data/command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']#定义存储卷volumes:#定义存储卷名称  - name: html#定义存储卷类型emptyDir: {}

2.1.3 验证与结果

1、部署Pod并查看状态:

kubectl apply -f pod-emptydir.yaml
# 确认Pod的2个容器均正常运行(READY为2/2)
kubectl get pods pod-emptydir -o wide

在这里插入图片描述

2、访问Nginx验证数据共享:

# 在上面定义了2个容器,其中一个容器是输入日期到index.html中,然后验证访问nginx的html是否可以获取日期。以验证两个容器之间挂载的emptyDir实现共享。
# 获取Pod的IP(从上方命令的IP列获取)
kubectl get pod -o wide
# 访问Nginx,可见时间持续追加(证明两容器共享数据)
curl POD_IP

在这里插入图片描述

3、小结:emptyDir仅适合临时数据场景,若Pod删除,数据会丢失,无法用于持久化存储。

2.2 hostPath:节点级持久化存储

hostPath将节点(宿主机)的本地目录挂载到容器,实现节点级别的数据持久化(Pod删除后,节点目录数据仍保留)。

2.2.1 核心特点

  • 绑定节点目录:直接使用宿主机的目录,数据不随Pod删除而销毁;
  • 跨Pod持久化:同一节点上的多个Pod可通过挂载相同hostPath共享数据;
  • 局限性
    • 数据绑定到特定节点,若Pod调度到其他节点,无法访问原节点数据;
    • 节点故障会导致数据丢失。

2.2.2 实战配置(YAML示例)

1、提前在所有节点创建目录(避免Pod调度到无目录的节点报错):

1)在 node01 节点上创建挂载目录
mkdir -p /data/pod/volume1
echo 'node01.simon.com' > /data/pod/volume1/index.html2)在 node02 节点上创建挂载目录
mkdir -p /data/pod/volume1
echo 'node02.simon.com' > /data/pod/volume1/index.html

2、 编写hostPath测试Pod配置

vim pod-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-hostpathnamespace: default
spec:containers:- name: myappimage: ikubernetes/myapp:v1#定义容器挂载内容volumeMounts:#使用的存储卷名称,如果跟下面volume字段name值相同,则表示使用volume的这个存储卷- name: html#挂载至容器中哪个目录mountPath: /usr/share/nginx/html#读写挂载方式,默认为读写模式falsereadOnly: false#volumes字段定义了paues容器关联的宿主机或分布式文件系统存储卷volumes:#存储卷名称- name: html#路径,为宿主机存储路径hostPath:#在宿主机上目录的路径path: /data/pod/volume1#定义类型,这表示如果宿主机没有此目录则会自动创建type: DirectoryOrCreate

2.2.3 验证与结果

1、部署Pod并查看调度节点:

kubectl apply -f pod-hostpath.yaml
# 查看Pod调度到的节点(NODE列)
kubectl get pods pod-hostpath -o wide

在这里插入图片描述

2、访问Nginx验证数据:

curl POD_IP  # 输出对应节点的标识(如node02.benet.com)

在这里插入图片描述

3、 验证持久化:删除Pod后重新部署,若调度到同一节点,数据仍保留;若调度到其他节点,数据为新节点的标识。

kubectl delete -f pod-hostpath.yaml && kubectl apply -f pod-hostpath.yaml kubectl get pods -o wide

在这里插入图片描述

2.3 NFS:跨节点共享存储

NFS(Network File System)是一种网络存储协议,可实现多节点共享数据,解决hostPath跨节点的局限性,是K8s集群中常用的共享存储方案。

2.3.1 核心特点

  • 跨节点共享:数据存储在NFS服务端,所有K8s节点均可访问;
  • 持久化可靠:数据独立于K8s集群,Pod删除、节点故障均不影响数据;
  • 支持RWX:支持多Pod同时读写(ReadWriteMany),适合分布式应用共享数据。

2.3.2 实战配置(分服务端与客户端)

步骤1:部署NFS服务端(独立节点,如stor01)

1、安装NFS服务:

# 安装NFS服务端组件(rpcbind为依赖)
yum install -y nfs-utils rpcbind

2、配置NFS共享目录:

# 创建共享目录
mkdir -p /data/volumes
chmod 777 /data/volumes  # 避免容器权限问题# 编辑NFS配置文件(允许192.168.10.0/24网段访问)
vim /etc/exports
/data/volumes 192.168.10.0/24(rw,no_root_squash)# 启动服务并设置开机自启
systemctl start rpcbind && systemctl enable rpcbind
systemctl start nfs && systemctl enable nfs
  • rw:读写权限;
  • no_root_squash:容器以root用户访问时,映射为NFS服务端的root用户(避免权限不足);

3、验证配置

netstat -anpu | grep rpc    
# 验证共享(输出共享目录信息即为成功)
showmount -e 

在这里插入图片描述

步骤2:K8s节点安装NFS客户端(所有节点)
# 所有K8s节点(master、node01、node02)执行
yum install -y nfs-utils
# 将store01加入/etc/hosts
echo "192.168.10.17 stor01" >> /etc/hosts
# 验证NFS服务端连通性(能看到共享目录即为成功)
showmount -e stor01  # stor01为NFS服务端 hostname/IP

在这里插入图片描述

步骤3:编写NFS Volume测试Pod配置
vim pod-nfs-vol.yaml
kind: Pod
apiVersion: v1
metadata:name: pod-vol-nfsnamespace: default
spec:containers:- name: myappimage: ikubernetes/myapp:v1volumeMounts:- name: htmlmountPath: /usr/share/nginx/htmlvolumes:- name: htmlnfs:path: /data/volumes  # NFS共享目录server: stor01   # NFS服务端地址
步骤4:验证与结果

1、部署Pod并访问:

kubectl apply -f pod-nfs-vol.yaml
# 在NFS服务端创建测试文件
echo "<h1> nfs stor01</h1>" >/data/volumes/index.html
# 访问Pod验证数据
kubectl get po -owide
NAME           READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
pod-hostpath   1/1     Running   0          29m     10.244.1.61   node01   <none>           <none>
pod-vol-nfs    1/1     Running   0          2m19s   10.244.2.68   node02   <none>           <none>
curl POD_IP  # 输出"nfs stor01"

在这里插入图片描述

2、验证跨节点能力:删除Pod后重新部署(可能调度到其他节点),再次访问仍能获取NFS中的数据,证明跨节点共享生效。

# 修改 pod-nfs-vol.yaml,通过nodeName:强制绑定节点
kind: Pod
apiVersion: v1
metadata:name: pod-vol-nfsnamespace: default
spec:nodeName: node01  # 强制调度到node01containers:- name: myappimage: ikubernetes/myapp:v1volumeMounts:- name: htmlmountPath: /usr/share/nginx/htmlvolumes:- name: htmlnfs:path: /data/volumes  # NFS共享目录server: stor01   # NFS服务端地址#删除nfs相关pod,再重新创建,可以得到数据的持久化存储
kubectl delete -f pod-nfs-vol.yaml && kubectl apply -f pod-nfs-vol.yaml

在这里插入图片描述
在这里插入图片描述

三、PV与PVC:K8s持久化存储的核心机制

基础Volume(如NFS)需要在Pod中直接配置存储细节(如NFS服务端IP、路径),导致开发与运维职责耦合(开发者需关注存储配置)。PV与PVC机制通过“资源抽象”解耦职责,是生产环境中推荐的持久化方案。

3.1 PV与PVC的核心概念

PV与PVC的本质是“存储资源的供给与需求”,分工明确:

概念名称核心作用维护者
PV(Persistent Volume)持久化存储卷定义底层存储资源(如NFS路径、容量、访问模式),是“存储资源的模板”运维工程师
PVC(Persistent Volume Claim)持久化存储的请求描述应用对存储的需求(如容量、访问模式),是“存储资源的申请单”应用开发者
StorageClass存储类自动创建 PV 模板运维工程师

PVC 的使用逻辑:管理员在 Pod 中定义PV,定义的时候直接指定大小,PVC 必须与对应的 PV 建立关系,PVC 会根据配置的定义去 PV 申请,而 PV 是由存储空间创建出来的。PV 和 PVC 是 Kubernetes 抽象出来的一种存储资源

3.2 PV与PVC的生命周期

PV与PVC的交互遵循“配置→绑定→使用→释放→回收”5个阶段,对应PV的4种核心状态:

3.2.1 生命周期流程

PV和PVC之间的相互作用遵循这个生命周期Provisioning(配置)–> Binding(绑定)–> Using(使用)–> Releasing(释放)–> Recycling(回收)

  1. Provisioning(配置):运维手动创建PV(静态配置),或通过StorageClass自动创建PV(动态配置);
  2. Binding(绑定):开发者创建PVC,K8s根据PVC的需求(容量、访问模式)匹配可用PV,绑定后PV状态变为Bound
  3. Using(使用):Pod通过PVC挂载存储,K8s通过StorageProtection机制阻止删除正在使用的PVC;
  4. Releasing(释放):Pod 释放 Volume 并删除 PVC,PV状态变为Released(数据仍保留);
  5. Reclaiming(回收):K8s根据PV的回收策略处理Released状态的PV(如保留数据、删除数据、清空数据)。

3.2.2 PV的核心状态

状态触发条件
AvailablePV已创建,未被任何PVC绑定
BoundPV已成功绑定到PVC
ReleasedPVC被删除,PV与PVC解绑,但数据未被回收
FailedPV自动回收失败(如NFS服务端不可用)

3.2.3 一个PV从创建到销毁的具体流程如下

  1. 一个PV创建完后状态会变成Available,等待被PVC绑定。
  2. 一旦被PVC邦定,PV的状态会变成Bound,就可以被定义了相应PVC的Pod使用。
  3. Pod使用完后会释放PV,PV的状态变成Released。
  4. 变成Released的PV会根据定义的回收策略做相应的回收工作。有三种回收策略,Retain、Delete和Recycle。

3.3 PV的核心配置项

创建PV时需关注3个核心配置项,直接影响PVC的匹配逻辑:

3.3.1 访问模式(accessModes)

定义PV支持的访问方式,PVC的访问模式必须是PV的子集(如PV支持RWX,PVC可申请RWXRWO):

  • RWO(ReadWriteOnce):仅允许单个节点的多个Pod读写(最常用,支持所有存储类型);
  • ROX(ReadOnlyMany):允许多个节点的Pod只读访问;
  • RWX(ReadWriteMany):允许多个节点的Pod读写访问(仅支持NFS、Ceph等共享存储)。

3.3.2 容量(capacity)

定义PV的存储容量(如storage: 2Gi),PVC申请的容量必须≤PV的容量。

3.3.3 回收策略(persistentVolumeReclaimPolicy)

定义PVC删除后PV的处理方式:

  • Retain(保留):默认策略,PV状态变为Released,数据保留,需手动清理数据并删除PV;
  • Delete(删除):自动删除PV及关联的底层存储(如云存储EBS、S3,仅支持部分存储类型);
  • Recycle(回收):清空PV数据(执行rm -rf /path/*),状态变为Available(仅支持NFS、hostPath,已逐步废弃)。

3.4 PV 的示例

kubectl explain pv    #查看pv的定义方式
FIELDS:apiVersion: v1kind: PersistentVolumemetadata:    #由于 PV 是集群级别的资源,即 PV 可以跨 namespace 使用,所以 PV 的 metadata 中不用配置 namespacename: speckubectl explain pv.spec    #查看pv定义的规格
spce:nfs:(定义存储类型)path:(定义挂载卷路径)server:(定义服(定义访问模型,务器名称)accessModes:有以下三种访问模型,以列表的方式存在,也就是说可以定义多个访问模式) * * *- ReadWriteOnce          #(RWO)存储可读可写,但只支持被单个 Pod 挂载- ReadOnlyMany           #(ROX)存储可以以只读的方式被多个 Pod 挂载- ReadWriteMany          #(RWX)存储可以以读写的方式被多个 Pod 共享         注:官网
#nfs 支持全部三种;iSCSI 不支持 ReadWriteMany(iSCSI 就是在 IP 网络上运行 SCSI 协议的一种网络存储技术);HostPath 不支持 ReadOnlyMany 和 ReadWriteMany。capacity:(定义存储能力,一般用于设置存储空间)storage: 2Gi (指定大小)storageClassName: (自定义存储类名称,此配置用于绑定具有相同类别的PVC和PV)persistentVolumeReclaimPolicy: Retain    #回收策略(Retain/Delete/Recycle) * * *
#Retain(保留):当删除与之绑定的PVC时候,这个PV被标记为released(PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV上,但是该PV不可用,需要手动来处理这些数据并删除该PV。
#Delete(删除):删除与PV相连的后端存储资源(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)
#Recycle(回收):删除数据,效果相当于执行了 rm -rf /thevolume/* (只有 NFS 和 HostPath 支持)
kubectl explain pvc   #查看PVC的定义方式
KIND:     PersistentVolumeClaim
VERSION:  v1
FIELDS:apiVersion	<string>kind	<string>  metadata	<Object>spec	<Object>#PV和PVC中的spec关键字段要匹配,比如存储(storage)大小、访问模式(accessModes)、存储类名称(storageClassName)
kubectl explain pvc.spec
spec:accessModes: (定义访问模式,必须是PV的访问模式的子集)resources:requests:storage: (定义申请资源的大小)storageClassName: (定义存储类名称,此配置用于绑定具有相同类别的PVC和PV)

四、NFS + PV + PVC 实战演练

本节通过NFS存储实现PV与PVC的绑定,验证持久化存储的全流程。

4.1 前置准备(NFS服务端配置)

在NFS服务端(stor01)创建5个独立目录(对应5个PV):

# 创建5个PV对应的NFS目录
mkdir -p /data/volumes/v{1,2,3,4,5}
# 编辑NFS配置,共享所有子目录
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)
# 生效配置
exportfs -arvshowmount -e

在这里插入图片描述

官方文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolume

4.2 手动创建PV(静态配置)

编写5个PV的YAML配置,分别对应NFS的5个目录:

vim pv-demo.yaml
# PV001:1Gi,支持RWO、RWX
apiVersion: v1
kind: PersistentVolume
metadata:name: pv001labels:name: pv001
spec:nfs:server: stor01path: /data/volumes/v1accessModes: ["ReadWriteMany","ReadWriteOnce"]capacity:storage: 1Gi
---
# PV002:2Gi,仅支持RWO
apiVersion: v1
kind: PersistentVolume
metadata:name: pv002labels:name: pv002
spec:nfs:server: stor01path: /data/volumes/v2accessModes: ["ReadWriteOnce"]capacity:storage: 2Gi
---
# PV003:2Gi,支持RWO、RWX
apiVersion: v1
kind: PersistentVolume
metadata:name: pv003labels:name: pv003
spec:nfs:server: stor01path: /data/volumes/v3accessModes: ["ReadWriteMany","ReadWriteOnce"]capacity:storage: 2Gi
---
# PV004:4Gi,支持RWO、RWX
apiVersion: v1
kind: PersistentVolume
metadata:name: pv004labels:name: pv004
spec:nfs:server: stor01path: /data/volumes/v4accessModes: ["ReadWriteMany","ReadWriteOnce"]capacity:storage: 4Gi
---
# PV005:5Gi,支持RWO、RWX
apiVersion: v1
kind: PersistentVolume
metadata:name: pv005labels:name: pv005
spec:nfs:server: stor01path: /data/volumes/v5accessModes: ["ReadWriteMany","ReadWriteOnce"]capacity:storage: 5Gi

部署PV并查看状态:

kubectl apply -f pv-demo.yaml
# 查看PV状态(初始均为Available)
kubectl get pv

在这里插入图片描述

4.3 创建PVC并绑定PV

开发者创建PVC,申请2Gi容量、支持RWX访问模式(多路读写)的存储,此时PVC会自动去匹配多路读写且大小为2Gi的PV,匹配成功获取PVC的状态即为Bound(pv003)。

4.3.1 编写PVC与Pod配置

vim pod-vol-pvc.yaml
# PVC:申请2Gi,RWX访问模式
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: mypvcnamespace: default
spec:accessModes: ["ReadWriteMany"]  # 申请RWX模式resources:requests:storage: 2Gi  # 申请2Gi容量
---
# Pod:通过PVC挂载存储
apiVersion: v1
kind: Pod
metadata:name: pod-vol-pvcnamespace: default
spec:containers:- name: myappimage: ikubernetes/myapp:v1volumeMounts:- name: html  # 与下方volumes.name对应mountPath: /usr/share/nginx/html# 通过PVC引用存储volumes:- name: htmlpersistentVolumeClaim:claimName: mypvc  # 与PVC名称一致

4.3.2 部署与验证

1、部署PVC与Pod:

kubectl apply -f pod-vol-pvc.yaml

2、查看绑定状态:

# PVC状态变为Bound,VOLUME列显示绑定的PV(pv003)
kubectl get pvc
# PV003状态变为Bound,CLAIM列显示绑定的PVC(default/mypvc)
kubectl get pv pv003

在这里插入图片描述

3、验证数据持久化:

# 在NFS服务端的v3目录创建测试文件
echo "welcome to use pv3" > /data/volumes/v3/index.htmlkubectl get pods -o wide
[root@master01 pv]# kubectl get po -owide
NAME           READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES
pod-hostpath   1/1     Running   0          164m   10.244.1.61   node01   <none>           <none>
pod-vol-nfs    1/1     Running   0          130m   10.244.1.62   node01   <none>           <none>
pod-vol-pvc    1/1     Running   0          92s    10.244.2.69   node02   <none>           <none># 访问Pod验证
curl POD_IP  # 输出"welcome to use pv3"

PVC 自动绑定到 PV003。访问 Pod 可获取 /data/volumes/v3 中内容。
在这里插入图片描述

4、验证释放流程:

# 删除PVC
kubectl delete pvc mypvc
# 查看PV状态(变为Released,数据仍保留在NFS的v3目录)
kubectl get pv pv003

五、StorageClass:实现NFS动态PV创建

Kubernetes 本身支持的动态 PV 创建不包括 NFS,所以需要使用外部存储卷插件分配PV。详见:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

静态PV需要运维手动创建,当集群规模较大时(如数百个应用需存储),手动管理效率极低。StorageClass通过动态配置机制,实现PV的自动创建与回收,大幅提升运维效率。

5.1 StorageClass的核心组件

动态PV创建依赖3个核心组件:

  1. StorageClass:定义动态PV的“模板”(如存储类型、Provisioner、回收策略);
  2. Provisioner:存储分配器,负责根据StorageClass的配置自动创建PV(NFS需使用nfs-client-provisioner第三方组件);
  3. RBAC权限:Provisioner需要操作PV、PVC、StorageClass等资源,需配置对应的RBAC权限。

5.2 部署NFS动态存储(全流程)

5.2.1 前置修复(K8s 1.20版本)

由于 1.20 版本启用了 selfLink,所以 k8s 1.20+ 版本通过 nfs provisioner 动态生成pv会报错,解决方法如下:

# 编辑API Server配置
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 在spec.containers.command中添加一行:
spec:containers:- command:- kube-apiserver- --feature-gates=RemoveSelfLink=false       #添加这一行- --advertise-address=192.168.10.14
......
# 重启API Server
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
kubectl delete pods kube-apiserver -n kube-system 
kubectl get pods -n kube-system | grep apiserver

在这里插入图片描述

5.2.2 在stor01节点上安装nfs,并配置nfs服务

mkdir /opt/k8s
chmod 777 /opt/k8s/vim /etc/exports
/opt/k8s 192.168.10.0/24(rw,no_root_squash,sync)# 生效配置
exportfs -arv
# 或者
systemctl restart nfs

在这里插入图片描述

5.2.3 配置RBAC权限

创建 Service Account,用来管理 NFS Provisioner 在 k8s 集群中运行的权限,设置 nfs-client 对 PV,PVC,StorageClass 等的规则

vim demo1-nfs-client-rbac.yaml
# 1. 创建 Service Account 账户,用来管理 NFS Provisioner 在 k8s 集群中运行的权限
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisioner
---
# 2. 创建集群角色(ClusterRole(定义权限))
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: ["list", "watch", "create", "update", "patch"]- apiGroups: [""]resources: ["endpoints"]verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
---
# 3. 集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:name: nfs-client-provisioner-clusterrolebinding
subjects:
- kind: ServiceAccountname: nfs-client-provisionernamespace: default
roleRef:kind: ClusterRolename: nfs-client-provisioner-clusterroleapiGroup: rbac.authorization.k8s.io

部署RBAC:

kubectl apply -f demo1-nfs-client-rbac.yaml
# 查看账户与绑定关系
kubectl get ServiceAccount,ClusterRole,ClusterRoleBinding|grep provisioner

在这里插入图片描述

5.2.4 部署 NFS Provisioner

NFS Provisione(即 nfs-client),有两个功能:一个是在 NFS 共享目录下创建挂载点(volume),另一个则是将 PV 与 NFS 的挂载点建立关联。

vim demo2-nfs-client-provisioner.yaml        
kind: Deployment
apiVersion: apps/v1
metadata:name: nfs-client-provisioner
spec:replicas: 1selector:matchLabels:app: nfs-client-provisionerstrategy:type: Recreatetemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisioner   	  #指定Service Account账户containers:- name: nfs-client-provisioner# 使用官方Provisioner镜像image: quay.io/external_storage/nfs-client-provisioner:latestimagePullPolicy: IfNotPresentvolumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: nfs-storage       #配置provisioner的Name,确保该名称与StorageClass资源中的provisioner名称保持一致- name: NFS_SERVERvalue: stor01           #配置绑定的nfs服务器- name: NFS_PATHvalue: /opt/k8s          #配置绑定的nfs服务器目录volumes:              #申明nfs数据卷- name: nfs-client-rootnfs:server: stor01path: /opt/k8s

部署Provisioner并验证:

kubectl apply -f demo2-nfs-client-provisioner.yaml
# 确认Provisioner Pod正常运行
kubectl get pods -owide

在这里插入图片描述

5.2.5 创建StorageClass

创建 StorageClass,负责建立 PVC 并调用 NFS provisioner 进行预定的工作,并让 PV 与 PVC 建立关联:

vim demo3-nfs-client-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client-storageclass
provisioner: nfs-storage     #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:archiveOnDelete: "false"   #false表示在删除PVC时不会对数据进行存档,即删除数据

部署StorageClass并查看:

kubectl apply -f demo3-nfs-client-storageclass.yaml
# 查看StorageClass
kubectl get storageclass

在这里插入图片描述

5.2.6 测试动态PV创建

创建PVC时指定StorageClass,验证PV是否自动创建:

vim demo4-test-pvc-pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: test-nfs-pvc
spec:accessModes:- ReadWriteManystorageClassName: nfs-client-storageclass    #关联StorageClass对象resources:requests:storage: 1Gi
---
# Pod:使用动态PVC
apiVersion: v1
kind: Pod
metadata:name: test-storageclass-pod
spec:containers:- name: busyboximage: busybox:latestimagePullPolicy: IfNotPresentcommand:- "/bin/sh"- "-c"args:- "sleep 3600"volumeMounts:- name: nfs-pvcmountPath: /mntrestartPolicy: Nevervolumes:- name: nfs-pvcpersistentVolumeClaim:claimName: test-nfs-pvc      #与PVC名称保持一致

部署并验证:
1、部署PVC与Pod:

kubectl apply -f demo4-test-pvc-pod.yaml

2、查看动态PV:

# PVC状态变为Bound,VOLUME列显示自动生成的PV名称
kubectl get pvc 
# 查看自动创建的PV(名称以pvc-开头)
kubectl get pv

在这里插入图片描述

3、验证NFS目录:

# 查看NFS服务端的/opt/k8s目录(自动创建以PVC命名的目录)
# ${namespace}-${pvcName}-${pvName} 的目录格式放到 NFS 服务器上
ls /opt/k8s/
default-test-nfs-pvc-pvc-1d78c05b-601a-426b-b64f-91c44d0f6c2c# 进入Pod写入数据,验证NFS目录同步
kubectl exec -it test-storageclass-pod sh
echo "this is test file" > /mnt/test.txt
exit# 在NFS服务端查看文件(存在即为成功)
cat /opt/k8s/.../test.txt

在这里插入图片描述

在这里插入图片描述

六、存储方案对比与选型建议

通过前文的讲解,我们已掌握K8s中多种存储方案的特点与实战。下表对比各类方案的核心差异,并给出选型建议:

存储类型生命周期跨节点共享持久化能力核心优势典型应用场景
emptyDir与Pod绑定无需配置,临时共享容器间临时数据共享、缓存
hostPath与节点绑定✅(节点级)简单易用,节点内持久化节点本地日志存储、单节点应用
NFS(直接使用)独立于K8s跨节点共享,配置简单多节点共享数据(如日志、配置)
PV + PVC(静态)集群级解耦开发与运维职责生产环境固定存储需求
StorageClass(动态)集群级自动创建PV,运维高效大规模集群、动态存储需求(如微服务)

选型建议

  1. 临时数据:优先选择emptyDir(如Pod内多容器共享临时文件);
  2. 单节点持久化:选择hostPath(如节点本地监控数据);
  3. 多节点共享:基础场景用NFS,生产环境推荐PV + PVC
  4. 大规模集群:必须使用StorageClass(动态PV创建,减少运维成本);
  5. 高可靠性需求:推荐使用Ceph、GlusterFS等分布式存储(替代NFS,支持高可用)。

总结

本文从容器存储的痛点出发,逐步讲解了K8s中Volume、PV、PVC及StorageClass的核心技术:

  1. 基础Volume:emptyDir解决临时共享,hostPath实现节点级持久化,NFS突破跨节点限制;
  2. PV/PVC机制:通过“供给-需求”模型解耦运维与开发职责,是生产环境持久化的基础;
  3. 动态存储:StorageClass + nfs-client-provisioner实现PV自动创建,大幅提升运维效率。

掌握这些技术,能够帮助我们构建可靠、高效的K8s存储架构,适应从单节点到大规模集群的不同需求。在实际应用中,需结合业务场景(如数据持久性、共享需求、集群规模)选择合适的存储方案,同时关注存储的性能与可靠性(如分布式存储的高可用配置)。

希望本文对大家理解K8s持久化存储有所帮助,如有疑问或补充,欢迎在评论区交流!

http://www.dtcms.com/a/485836.html

相关文章:

  • 【课堂笔记】概率论-1
  • Java 大视界 -- Java 大数据在智能金融区块链跨境支付与结算中的应用
  • 面试_项目问题_RPC调用异常
  • 金融分析师核心能力构建:从数据解读到战略洞察
  • 【八股笔记】SSM
  • Selenium WebDriver自动化测试(框架篇)--数据驱动测试
  • WUJIE VS QIANKUN 微前端框架选型(一)
  • 中国万网域名官网官网优化包括什么内容
  • MySQL相关知识总结
  • 实战GPT-5:用“XML三明治”和“完美循环”重构你的提示
  • 德阳网站网站建设网页设计课程心得体会500字
  • 鸿蒙Harmony实战开发教学Day1-IDE新版本安装篇
  • K8s学习笔记(十九) K8s资源限制
  • claude-sonnet-4-5,IDE中集成,白嫖Claude Code代理,AnyRouter公益站
  • 【SSRF漏洞】Server-Side Request Forgery 服务器端请求伪造
  • 【国内电子数据取证厂商龙信科技】手机在纯净模式下如何安装客户端软件
  • Flink 算子性能优化深度解析
  • Flink受管状态自定义序列化的可演进设计与未来趋势
  • 迷你加湿器方案开发,加湿器/香薰机MCU控制方案开发设计
  • 网站模版参考中国建筑装饰网饶明富
  • ESP32的系统存储
  • HTML应用指南:利用GET请求获取全国领克经销商门店位置信息
  • 零基础OSS组件(Java)
  • 中国亚健康产业:多元创新下的健康新生态
  • 从物联网到工业控制:48元明远智睿2351核心板的多场景适配之路
  • MedHELM评估医疗大模型:设计理念、技术细节与应用影响
  • 江协科技STM32课程笔记(三)—定时器TIM(输出比较)
  • 网站建设可行性分析报告模板支付宝小程序搭建
  • 精通网站开发书籍做游戏网站赚钱么
  • Linux 网络分析终极武器:Tcpdump 深度指南!