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

PersistentVolume + NFS:网络共享存储

一、引言

通过前面的文章(PersistentVolume:解决数据持久化问题),了解到Kubernetes 里的持久化存储对象 PersistentVolume、 PersistentVolumeClaim、StorageClass,把它们联合起来就可以为 Pod 挂载一块“虚拟盘”, 让 Pod 在其中任意读写数据。使用HostPath方式,存储卷只能在本机使用,而 Kubernetes 里的 Pod 经常会在集群里“漂移”,所以这种方式不是特别实用。要想让存储卷真正能被 Pod 任意挂载,我们需要变更存储的方式,不能限定在本地磁盘,而 是要改成网络存储,这样 Pod 无论在哪里运行,只要知道 IP 地址或者域名,就可以通过网络通信访问存储设备。

二、安装 NFS服务器

NFS 采用的是 Client/Server 架构,需要选定一台主机作为 Server,安装 NFS 服务端;其他要使用存储的主机作为 Client,安装 NFS 客户端工具。

我们在自己的 Kubernetes 集群里再增添一台名字叫 Storage 的服务器,在上面安装 NFS,实现网络存储、共享网盘的功能。不过这台 Storage 也只是一个逻辑概念,我们 在实际安装部署的时候完全可以把它合并到集群里的某台主机里,在 CentOS 系统里安装 NFS 服务端很容易:

1. 安装 NFS 服务:通过 yum 命令安装 nfs-utils 和 rpcbind 软件包,这两个包是 NFS 服务运行所必需的。

sudo yum install -y nfs-utils rpcbind

2. 创建共享目录:我们在服务器上创建一个目录用于共享,例如/srv/nfs/shared。

sudo mkdir -p /srv/nfs/shared

3. 设置目录权限:为了确保 NFS 客户端能够正常访问共享目录,我们需要设置合适的权限。这里将目录的所有者和组都设置为 nfsnobody,并赋予 777 权限(生产环境中请根据实际安全需求调整)。

sudo chown -R nfsnobody:nfsnobody /srv/nfs/shared
sudo chmod 777 /srv/nfs/shared

4. 配置 /etc/exports 文件:这个文件用于定义 NFS 共享目录及其访问权限。我们打开该文件并添加如下内容,将/srv/nfs/shared目录共享给 192.168.1.0/24 网段的所有客户端,并且允许读写操作,同步写入数据以保证数据一致性,同时不进行子树检查以提高性能。

sudo vim /etc/exports
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)

5. 导出共享目录并启动服务:让 NFS 服务加载新的共享配置,并启动 NFS 服务和 rpcbind 服务,同时设置它们开机自启。

sudo exportfs -a
sudo systemctl start nfs-server rpcbind
sudo systemctl enable nfs-server rpcbind

6. 开通防火墙端口:NFS 服务使用 2049 端口,我们需要在防火墙上开通该端口,以允许客户端访问。如果使用的是 firewall 防火墙,可以使用以下命令开通端口并重新加载配置。

sudo firewall-cmd --add-port=2049/tcp --zone=public --permanent
sudo firewall-cmd --reload

三、使用 NFS 存储卷

已经为 Kubernetes 配置好了 NFS 存储系统,就可以使用它来创建新的 PV 存储对象 了。 先来手工分配一个存储卷,需要指定 storageClassName 是 nfs,而 accessModes 可以设置成 ReadWriteMany,这是由 NFS 的特性决定的,它支持多个节点同时访问一个共享目录。 因为这个存储卷是 NFS 系统,所以我们还需要在 YAML 里添加 nfs 字段,指定 NFS 服务器 的 IP 地址和共享目录名。

3.1 NFS 静态存储卷

3.1.1 创建PV

接下来,我们在 K8S 集群中创建一个静态 PV,使用刚才搭建的 NFS 服务器作为存储后端。创建一个名为pv-nfs.yaml的文件,内容如下:

apiVersion: v1
kind: PersistentVolume
metadata:name: pv-nfs-10g  # PV的名称,可自定义
spec:capacity:storage: 10Gi  # 定义PV的容量为10GBaccessModes:- ReadWriteMany  # 访问模式为ReadWriteMany,表示可以被多个节点以读写方式挂载persistentVolumeReclaimPolicy: Retain  # 回收策略为Retain,当PVC删除时,PV保留数据,需要手动清理storageClassName: nfs  # 存储类名称,自定义为nfsnfs:path: /srv/nfs/shared  # NFS服务器上的共享目录路径server: 192.168.1.100  # NFS服务器的IP地址

在这个 YAML 文件中:​

  • metadata.name指定了 PV 的名称,在集群中必须唯一。​
  • spec.capacity.storage定义了 PV 的存储容量。​
  • spec.accessModes设置了 PV 的访问模式,ReadWriteMany 适用于需要多个节点同时读写数据的场景,比如一个分布式文件存储应用。​
  • spec.persistentVolumeReclaimPolicy表示 PV 的回收策略,Retain 策略可以保证数据的安全性,即使 PVC 被删除,数据也不会丢失。​
  • spec.storageClassName用于标识 PV 所属的存储类,方便与 PVC 进行匹配。​
  • spec.nfs.path和spec.nfs.server分别指定了 NFS 服务器的共享目录路径和 IP 地址。

使用以下命令创建 PV:

kubectl apply -f pv-nfs.yaml

创建完成后,可以使用kubectl get pv命令查看 PV 的状态,当状态显示为Available时,表示 PV 已准备好被 PVC 绑定。

3.1.2 创建PVC

有了 PV,我们就可以定义申请存储的 PVC 对象了,它的内容和 PV 差不多,但不涉及 NFS 存储的细节,只需要用 resources.request 来表示希望要有多大的容量。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc-test  # PVC的名称,可自定义
spec:accessModes:- ReadWriteMany  # 访问模式需要与PV一致resources:requests:storage: 5Gi  # 请求的存储容量为5GB,不能超过PV的容量storageClassName: nfs  # 存储类名称需要与PV一致

在这个 YAML 文件中:

  • metadata.name定义了 PVC 的名称。
  • spec.accessModes和spec.storageClassName必须与 PV 中的设置相匹配,这样 K8S 才能找到合适的 PV 进行绑定。
  • spec.resources.requests.storage表示 PVC 请求的存储容量,这里请求 5GB,小于 PV 的 10GB 容量,符合绑定条件。

使用以下命令创建 PVC:

kubectl apply -f pvc-test.yaml

创建完成后,使用kubectl get pvc命令查看 PVC 的状态,当状态显示为Bound时,表示 PVC 已经成功绑定到了 PV 上。此时,我们可以使用kubectl describe pvc pvc-test命令查看 PVC 的详细信息,包括绑定的 PV 名称等。

3.1.3 创建 Pod 挂载 PVC

最后,我们创建一个 Pod,并将 PVC 挂载到 Pod 中,使 Pod 能够使用 PV 提供的存储。创建一个名为pod-with-pvc.yaml的文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-with-pvc  # Pod的名称,可自定义
spec:containers:- name: test-pvc  # 容器的名称,可自定义image: nginx  # 使用nginx镜像volumeMounts:- name: data-volume  # 挂载点的名称,需要与下面volumes中的名称一致mountPath: /usr/share/nginx/html  # 挂载到容器内的路径,这里挂载到nginx的默认网页目录volumes:- name: data-volume  # 与上面volumeMounts中的名称一致persistentVolumeClaim:claimName: pvc-test  # 关联的PVC名称

在这个 YAML 文件中:

  • metadata.name指定了 Pod 的名称。
  • spec.containers定义了 Pod 中的容器,这里使用了 nginx 镜像。
  • spec.containers.volumeMounts指定了容器内的挂载点,将名为data-volume的存储卷挂载到/usr/share/nginx/html目录,这样容器就可以在这个目录下读写数据,并且数据会持久化存储在 PV 中。
  • spec.volumes定义了 Pod 使用的存储卷,通过persistentVolumeClaim.claimName关联到之前创建的 PVC。

使用以下命令创建 Pod:

kubectl apply -f pod-with-pvc.yaml

创建完成后,我们可以进入 Pod 容器,在挂载目录下创建文件,然后删除 Pod 并重新创建,会发现之前创建的文件依然存在,这就证明了数据通过 PV 和 PVC 实现了持久化存储。

Pod、PVC、PV 和 NFS 存储的关系可以用下图来形象地表示:

通过上面的图示,我们可以清楚的看到三者之间的一个引用关系,

现在有了 NFS 这样的网络存储系统,你是不是认为 Kubernetes 里的数据持久化问题就已经解决了呢? 对于这个问题,我觉得可以套用一句现在的流行语:“解决了,但没有完全解决。” 说它“解决了”,是因为网络存储系统确实能够让集群里的 Pod 任意访问,数据在 Pod 销毁后 仍然存在,新创建的 Pod 可以再次挂载,然后读取之前写入的数据,整个过程完全是自动化 的。 说它“没有完全解决”,是因为 PV 还是需要人工管理,必须要由系统管理员手动维护各种存储设备,再根据开发需求逐个创建 PV,而且 PV 的大小也很难精确控制,容易出现空间不足或者空间浪费的情况。所以就需要我们动态存储卷出手了。

3.2 NFS 动态存储卷

3.2.1 安装 NFS-Client Provisioner

动态存储卷的实现离不开一些关键组件,其中 NFS-Client Provisioner 插件起着至关重要的作用。它就像是一个智能的存储资源调配员,能够根据我们的需求,利用已有的 NFS 服务器自动创建持久卷(PV) 。当应用程序需要存储时,它能快速响应,无需我们手动去创建 PV,大大提高了存储资源分配的效率和灵活性。​

接下来我们开始安装和配置它。首先,创建一个名为nfs-client-provisioner-rbac.yaml的文件,用于配置 NFS-Client Provisioner 所需的 RBAC 权限,内容如下:

apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: nfs-client-provisioner-runner
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"]---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: run-nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: rbac.authorization.k8s.io

在这个文件中:​

  • 首先定义了一个名为nfs-client-provisioner的 ServiceAccount,它是 NFS-Client Provisioner 在 K8S 集群中运行的身份标识,用于访问 K8S API。​
  • 接着创建了一个 ClusterRole,名为nfs-client-provisioner-runner,它定义了一系列权限,包括对持久卷(persistentvolumes)、持久卷声明(persistentvolumeclaims)、存储类(storageclasses)、事件(events)和端点(endpoints)的相关操作权限,确保 NFS-Client Provisioner 能够正常创建和管理 PV。​
  • 最后通过 ClusterRoleBinding 将 ServiceAccount 和 ClusterRole 绑定在一起,使得nfs-client-provisioner具有nfs-client-provisioner-runner定义的权限。​

使用以下命令创建这些 RBAC 资源:

kubectl apply -f nfs-client-provisioner-rbac.yaml

然后,创建一个名为nfs-client-provisioner-deployment.yaml的文件,用于部署 NFS-Client Provisioner,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisionerimage: quay.io/external_storage/nfs-client-provisioner:latestvolumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner- name: NFS_SERVERvalue: 192.168.1.100- name: NFS_PATHvalue: /srv/nfs/sharedvolumes:- name: nfs-client-rootnfs:server: 192.168.1.100path: /srv/nfs/shared

在这个文件中:

  • metadata.name指定了 Deployment 的名称为nfs-client-provisioner。
  • spec.replicas设置为 1,表示只运行一个副本。
  • spec.strategy.type为 Recreate,表示在更新 Deployment 时,会先删除旧的 Pod,再创建新的 Pod。
  • spec.selector.matchLabels用于选择带有app: nfs-client-provisioner标签的 Pod。
  • spec.template.metadata.labels定义了 Pod 的标签,与选择器匹配。
  • spec.template.spec.serviceAccountName指定使用之前创建的nfs-client-provisioner服务账号。
  • spec.template.spec.containers定义了容器的相关信息,使用quay.io/external_storage/nfs-client-provisioner:latest镜像,将/persistentvolumes目录挂载到nfs-client-root卷,设置了三个环境变量:PROVISIONER_NAME指定了存储分配器的名称,NFS_SERVER指定了 NFS 服务器的 IP 地址,NFS_PATH指定了 NFS 服务器上的共享目录路径。
  • spec.template.spec.volumes定义了一个 NFS 类型的卷nfs-client-root,指向 NFS 服务器的共享目录。

使用以下命令创建 Deployment:

kubectl apply -f nfs-client-provisioner-deployment.yaml

创建完成后,可以使用kubectl get pods命令查看 NFS-Client Provisioner 的 Pod 是否正常运行。如果状态显示为Running,则表示安装和部署成功。

3.2.1 创建StorageClass

创建好 NFS-Client Provisioner 后,我们就可以创建 StorageClass 了。StorageClass 就像是一个存储模板,它定义了 PV 的属性和创建规则,当 PVC 请求存储时,K8S 会根据 StorageClass 的定义来动态创建 PV。

我们来看一下 NFS 默认的 StorageClass 定义:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:archiveOnDelete: "false"

YAML 里的关键字段是 provisioner,它指定了应该使用哪个 Provisioner。另一个字段 parameters 是调节 Provisioner 运行的参数,需要参考文档来确定具体值,在这里的 archiveOnDelete: "false" 就是自动回收存储空间。

当然也可以不使用默认的 StorageClass,而是根据自己的需求,任意定制具有不同存储特性的 StorageClass,比如添加字段 onDelete: "retain" 暂时保留分配的存储,之后再手动删除:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client-retained
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:onDelete: "retain"
3.2.1 创建PVC

有了 StorageClass 之后,我们就可以创建 PVC 来请求动态 PV 了。创建一个名为pvc-dynamic-test.yaml的文件,内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc-dynamic-test
spec:accessModes:- ReadWriteManyresources:requests:storage: 1GistorageClassName: nfs-client-retained # 引用前面创建的StorageClass

在这个文件中:​

  • metadata.name指定了 PVC 的名称为pvc-dynamic-test。​
  • spec.accessModes设置访问模式为ReadWriteMany,表示可以被多个节点以读写方式挂载,适用于需要多节点共享读写数据的场景,比如分布式文件系统应用。​
  • spec.resources.requests.storage请求的存储容量为 1Gi。​
  • spec.storageClassName指定使用nfs-client-storage这个 StorageClass,这样 K8S 会根据该 StorageClass 的定义动态创建 PV 并绑定到这个 PVC 上。​

使用以下命令创建 PVC:

kubectl apply -f pvc-dynamic-test.yaml

创建完成后,使用kubectl get pvc命令查看 PVC 的状态,当状态显示为Bound时,表示 PVC 已经成功绑定到动态创建的 PV 上。此时,我们可以使用kubectl describe pvc pvc-dynamic-test命令查看 PVC 的详细信息,包括绑定的 PV 名称、存储容量等 。

3.2.3 创建 Pod 挂载动态 PVC

最后,我们创建一个 Pod,并将动态创建的 PVC 挂载到 Pod 中,使 Pod 能够使用 PV 提供的存储。创建一个名为pod-with-dynamic-pvc.yaml的文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-with-dynamic-pvc
spec:containers:- name: test-dynamic-pvcimage: nginxvolumeMounts:- name: data-volumemountPath: /usr/share/nginx/htmlvolumes:- name: data-volumepersistentVolumeClaim:claimName: pvc-dynamic-test # 关联前面创建的PVC

在这个文件中:​

  • metadata.name指定了 Pod 的名称为pod-with-dynamic-pvc。​
  • spec.containers定义了 Pod 中的容器,这里使用nginx镜像。​
  • spec.containers.volumeMounts指定了容器内的挂载点,将名为data-volume的存储卷挂载到/usr/share/nginx/html目录,这样容器就可以在这个目录下读写数据,并且数据会持久化存储在动态创建的 PV 中。​
  • spec.volumes定义了 Pod 使用的存储卷,通过persistentVolumeClaim.claimName关联到之前创建的pvc-dynamic-testPVC。​

使用以下命令创建 Pod:

kubectl apply -f pod-with-dynamic-pvc.yaml

创建完成后,我们可以进入 Pod 容器,在挂载目录下创建文件,然后删除 Pod 并重新创建,会发现之前创建的文件依然存在,这就证明了数据通过动态 PV 和 PVC 实现了持久化存储。

最后我们还是用一张图来展示Pod、PVC、StorageClass 和 Provisioner之间的引用关系:

四、小结

在 K8S 里,静态存储卷和动态存储卷各有千秋。静态存储卷通过预先创建 PV,让我们能够对存储资源进行精细的规划和管理,就像为每个应用定制了一个专属的存储仓库,适合那些对存储需求相对稳定、明确的场景,传统企业应用在这方面就运用得较多。而动态存储卷借助 NFS-Client Provisioner 和 StorageClass 实现了存储资源的自动创建和分配,如同拥有了一个智能的存储管家,随时根据应用的需求快速调配存储,在互联网企业中,面对业务的快速变化和弹性扩展的需求,动态存储卷发挥着重要作用。

 Tips: 为了大家快速高效的学习,已经将文章提交到了git仓库,涵盖后端大部分技术,以及后端学习路线,仓库内容会持续更新,建议 Star 收藏 以便随时查看https://gitee.com/bxlj/java-article。

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

相关文章:

  • leetcode 1863 找出所有子集的异或总和再求和
  • 【C++】STL -- vector 的使用及模拟实现
  • 网站如何做图片特效erp软件实施
  • 【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】
  • 贡井区建设局网站淘宝客做自己的网站
  • 蓝牙发展史
  • 对LED点灯实验的C与汇编的深入分析,提及到volatile
  • 网站建设外包广州网站建设说说外链的建设
  • LevOJ P2080 炼金铺 II [矩阵解法]
  • wordpress网站映射wordpress免费网站国外
  • 哈尔滨企业建站系统西宁建设局官方网站
  • py_innodb_page_info.py表空间分析
  • 有什么做宝宝辅食的网站吗莱阳网站开发
  • tasklet
  • 页面 HTTPS 化实战,从证书部署到真机验证的全流程(证书链、重定向、混合内容、抓包排查)
  • 北京哪家公司做网站电脑上买wordpress
  • 家用机能否做网站服务器泰安房价走势图
  • MC33PT2000控制详解七:软件代码设计1-图形化设置
  • 在租用香港服务器之前如何判断质量
  • 【高清视频】CXL 2.0 over Fibre演示和答疑 - 将内存拉到服务器10米之外
  • 【STM32项目开源】基于STM32的独居老人监护系统
  • 海外服务器怎么测试再不同地方的访问速度?
  • Linux最忙CPU组查找函数和最忙运行队列查找函数
  • 查看未知LiDAR设备的IP地址
  • iOS 0Day漏洞CVE-2025-24085相关PoC利用细节已公开
  • 网站文案框架网络推广策划书
  • 基于Chrome140的FB账号自动化——运行脚本(三)
  • 广州做网站设计镇江建筑公司排名最新
  • RHCA - CL260 | Day12:集群性能建议
  • CNN基础学习(自用)