05_在k8s集群中安装NFS实现ReadWriteMany存储

文章目录

  • [0 背景与架构](#0 背景与架构)
    • [0.1 为什么需要NFS](#0.1 为什么需要NFS)
    • [0.2 环境架构](#0.2 环境架构)
    • [0.3 镜像拉取方案](#0.3 镜像拉取方案)
  • [1 配置NFS服务器(rockylinux10-4)](#1 配置NFS服务器(rockylinux10-4))
    • [1.1 创建出 rockylinux10-4](#1.1 创建出 rockylinux10-4)
    • [1.2 安装nfs-utils](#1.2 安装nfs-utils)
    • [1.3 创建共享目录并设置权限](#1.3 创建共享目录并设置权限)
    • [1.4 配置/etc/exports](#1.4 配置/etc/exports)
    • [1.5 启动并启用nfs-server](#1.5 启动并启用nfs-server)
    • [1.6 验证NFS共享](#1.6 验证NFS共享)
  • [2 在所有K8s节点安装nfs-utils(客户端)](#2 在所有K8s节点安装nfs-utils(客户端))
  • [3 部署nfs-subdir-external-provisioner](#3 部署nfs-subdir-external-provisioner)
    • [3.1 下载部署文件](#3.1 下载部署文件)
    • [3.2 修改deployment.yaml](#3.2 修改deployment.yaml)
    • [3.3 应用部署](#3.3 应用部署)
  • [4 验证部署](#4 验证部署)
    • [4.1 查看 Pod 状态](#4.1 查看 Pod 状态)
    • [4.2 查看 StorageClass](#4.2 查看 StorageClass)
  • [5 测试RWX PVC](#5 测试RWX PVC)
    • [5.1 创建 RWX PVC](#5.1 创建 RWX PVC)
    • [5.2 创建两个 Pod 同时挂载](#5.2 创建两个 Pod 同时挂载)
    • [5.3 验证数据共享](#5.3 验证数据共享)
  • [6 常见问题与解决](#6 常见问题与解决)
    • [6.1 镜像拉取失败(ImagePullBackOff)](#6.1 镜像拉取失败(ImagePullBackOff))
    • [6.2 Pod 一直 Pending](#6.2 Pod 一直 Pending)
    • [6.3 挂载失败](#6.3 挂载失败)
  • [7 总结](#7 总结)
  • 感谢阅读

0 背景与架构

0.1 为什么需要NFS

在 《02_在多个RockyLinux10虚拟机上安装k8s集群》 和 《04_在k8s集群中安装OpenEBS Rawfile并测试快照》 之后,我们的集群已经具备了本地存储(Rawfile)和快照能力。但 Rawfile 只支持 ReadWriteOnce(RWO),而很多场景(例如多个Pod同时读写同一份数据)需要 ReadWriteMany(RWX)。

NFS 是 Kubernetes 中原生支持 RWX 的存储方案之一,它允许多个节点上的多个 Pod 同时挂载同一个共享目录并进行读写。非常适合学习 PVC 的不同访问模式,尤其是 RWX。

0.2 环境架构

IP 虚拟机名称 角色 说明
192.168.56.101 rockylinux10-1 control-plane k8s master 节点
192.168.56.102 rockylinux10-2 worker k8s worker 节点
192.168.56.103 rockylinux10-3 worker k8s worker 节点
192.168.56.104 rockylinux10-4 NFS Server 独立的 NFS 服务器(这个是从 rockylinux10-1 的 VirtualBox 快照中复制出来后,配置的)

0.3 镜像拉取方案

由于国内网络限制,我们采用以下镜像源:

原始仓库 替换地址 用途
docker.io docker.m.daocloud.io DaoCloud 镜像加速
registry.k8s.io k8s.m.daocloud.io DaoCloud 的 k8s 镜像代理

1 配置NFS服务器(rockylinux10-4)

1.1 创建出 rockylinux10-4

从 rockylinux10-1 的 VirtualBox 快照 rockylinux10-init 中,复制出 rockylinux10-4,如下图:

在弹出的对话框中做如下图的配置:

复制好之后,启动 rockylinux10-4,我们来修改它的静态 ip 和 hostname 。这块儿可以参考 《02_在多个RockyLinux10虚拟机上安装k8s集群》 的 1.14 设置静态IP 小节的内容。

由于 rockylinux10-4 是从 rockylinux10-1 复制出来的,所以不用想,现在 rockylinux10-4 的静态 ip 肯定是 192.168.56.101 ,我们直接在 rockylinux10-4 中执行 nmtui 命令,如下图:

回车之后:

回车之后:

按照上图的编辑好之后,一路 OK,就好。然后设置 hotstname 主机名:

bash 复制代码
hostnamectl set-hostname rockylinux10-4

然后 reboot 重启 rockylinux10-4 。重启之后,给 rockylinux10-1 到 10-4 的每台机器,设置一下 /etc/hosts ,保证有如下内容:

bash 复制代码
192.168.56.101 rockylinux10-1
192.168.56.102 rockylinux10-2
192.168.56.103 rockylinux10-3
192.168.56.104 rockylinux10-4

1.2 安装nfs-utils

在 rockylinux10-4 上执行:

bash 复制代码
sudo dnf install -y nfs-utils

1.3 创建共享目录并设置权限

bash 复制代码
sudo mkdir -p /data/k8s-nfs
sudo chown nobody:nobody /data/k8s-nfs
sudo chmod 777 /data/k8s-nfs

这里设置为 nobody:nobody 和 777 是为了简化权限,生产环境请根据实际需求调整。

1.4 配置/etc/exports

编辑 /etc/exports,添加以下内容:

bash 复制代码
/data/k8s-nfs 192.168.56.0/24(rw,sync,no_subtree_check,no_root_squash)

参数说明:

  • rw:读写权限
  • sync:同步写入
  • no_subtree_check:不检查子树,提高性能
  • no_root_squash:允许 root 用户保持权限(方便测试)

1.5 启动并启用nfs-server

bash 复制代码
sudo exportfs -ra
sudo systemctl restart nfs-server
sudo systemctl enable nfs-server

1.6 验证NFS共享

bash 复制代码
sudo showmount -e localhost

期望输出:

复制代码
Export list for localhost:
/data/k8s-nfs 192.168.56.0/24

2 在所有K8s节点安装nfs-utils(客户端)

Kubernetes 的 kubelet 需要调用 mount -t nfs 来挂载 NFS,因此每个节点 都需要安装 nfs-utils。

在 rockylinux10-1、10-2、10-3 上分别执行:

bash 复制代码
sudo dnf install -y nfs-utils --disablerepo=docker-ce-stable

加 --disablerepo=docker-ce-stable 是为了跳过可能会连接失败的 Docker 仓库,避免 dnf 卡住。如果已经永久禁用了该仓库,可以省略此参数。

3 部署nfs-subdir-external-provisioner

nfs-subdir-external-provisioner 是 Kubernetes SIG-Storage 官方维护的项目,它可以在 NFS 共享目录下自动创建子目录作为 PV,实现动态供应。

3.1 下载部署文件

在 master 节点(rockylinux10-1)上执行:

bash 复制代码
mkdir -p ~/nfs-provisioner && cd ~/nfs-provisioner

# 下载三个 YAML 文件
curl -O https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/deployment.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/rbac.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/class.yaml

如果 curl 下载失败,可以用浏览器打开链接保存文件。

3.2 修改deployment.yaml

需要修改三个地方:

a. 修改 NFS 服务器地址和路径

找到 env 部分,改成你的 NFS 服务器信息:

yaml 复制代码
env:
  - name: PROVISIONER_NAME
    value: k8s-sigs.io/nfs-subdir-external-provisioner
  - name: NFS_SERVER
    value: 192.168.56.104        # NFS 服务器 IP
  - name: NFS_PATH
    value: /data/k8s-nfs         # NFS 共享路径
volumes:
  - name: nfs-client-root
    nfs:
      server: 192.168.56.104     # 同上
      path: /data/k8s-nfs        # 同上

b. 修改镜像地址为国内可访问的

原镜像 registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 国内拉不动,改成 DaoCloud 的镜像源:

yaml 复制代码
image: docker.m.daocloud.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2

c. 确认镜像版本

当前最新稳定版是 v4.0.2。如果拉取失败,可以试试 v4.0.1 或 v4.0.0。

3.3 应用部署

bash 复制代码
kubectl apply -f rbac.yaml
kubectl apply -f deployment.yaml
kubectl apply -f class.yaml

4 验证部署

4.1 查看 Pod 状态

bash 复制代码
kubectl get pods | grep nfs-client

期望输出:

复制代码
nfs-client-provisioner-6bd7ff8964-gf9jv   1/1     Running   0          2m

4.2 查看 StorageClass

bash 复制代码
kubectl get sc

期望输出:

复制代码
NAME         PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   AGE
nfs-client   k8s-sigs.io/nfs-subdir-external-provisioner    Delete          Immediate           2m

5 测试RWX PVC

5.1 创建 RWX PVC

创建文件 test-pvc-rwx.yaml:

yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-rwx-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-client
  resources:
    requests:
      storage: 1Gi

应用:

bash 复制代码
kubectl apply -f test-pvc-rwx.yaml
kubectl get pvc

期望输出:

复制代码
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
demo-rwx-pvc   Bound    pvc-e380223a-732e-4446-89e9-20af374a7803   1Gi        RWX            nfs-client     10s

5.2 创建两个 Pod 同时挂载

创建文件 test-pods-rwx.yaml:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: demo-rwx-pod-1
spec:
  containers:
  - name: busybox
    image: m.daocloud.io.localcache.registry:65000/docker.io/library/busybox:1.38.0
    command: ["/bin/sh", "-c", "while true; do echo 'pod-1' >> /mnt/data/test.txt; sleep 5; done"]
    volumeMounts:
    - name: data
      mountPath: /mnt/data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-rwx-pvc
---
apiVersion: v1
kind: Pod
metadata:
  name: demo-rwx-pod-2
spec:
  containers:
  - name: busybox
    image: m.daocloud.io.localcache.registry:65000/docker.io/library/busybox:1.38.0
    command: ["/bin/sh", "-c", "while true; do echo 'pod-2' >> /mnt/data/test.txt; sleep 5; done"]
    volumeMounts:
    - name: data
      mountPath: /mnt/data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-rwx-pvc

应用:

bash 复制代码
kubectl apply -f test-pods-rwx.yaml
kubectl get pods -o wide

5.3 验证数据共享

进入任意一个 Pod 查看文件内容:

bash 复制代码
kubectl exec -it demo-rwx-pod-1 -- cat /mnt/data/test.txt

你会看到 pod-1 和 pod-2 交替出现,证明 RWX 生效。

6 常见问题与解决

6.1 镜像拉取失败(ImagePullBackOff)

现象 :nfs-client-provisioner Pod 处于 ImagePullBackOff。

排查:

bash 复制代码
kubectl describe pod -l app=nfs-client-provisioner | grep -E "Image:|Failed"

解决 :将 deployment.yaml 中的镜像地址替换为国内可访问的镜像源,例如:

yaml 复制代码
image: docker.m.daocloud.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2

6.2 Pod 一直 Pending

现象 :使用 RWX PVC 的 Pod 一直 Pending。

排查:

bash 复制代码
kubectl describe pod <pod-name>

常见原因:

  • PVC 未绑定:检查 PVC 状态,确保 STATUS 为 Bound。
  • 节点资源不足:检查节点 CPU/内存。
  • 节点污点:Master 节点有污点,Pod 需要容忍度。

6.3 挂载失败

现象 :Pod 事件中出现 mount failed 或 permission denied。

解决:

  • 确认所有节点已安装 nfs-utils。
  • 确认 NFS 服务器共享目录权限正确(/data/k8s-nfs 为 777 或合适的权限)。
  • 确认 /etc/exports 中 no_root_squash 已设置,并执行 exportfs -ra。

7 总结

通过本文,我们完成了以下工作:

  1. 配置了 NFS 服务器 :在 rockylinux10-4 上安装并启动了 NFS 服务,共享 /data/k8s-nfs 目录。
  2. 在所有 K8s 节点安装了 nfs-utils:确保 kubelet 可以挂载 NFS。
  3. 部署了 nfs-subdir-external-provisioner :实现了 NFS 存储的动态供应,并创建了 nfs-client StorageClass。
  4. 测试了 RWX PVC :创建了 ReadWriteMany 的 PVC,并用两个 Pod 同时挂载,验证了多节点读写共享。

感谢阅读

相关推荐
余槐i1 小时前
-m 2g 反而更早 OOM:Docker 内存计数与宿主机空闲口径差异
java·linux·docker·性能优化·cgroup
风华同学1 小时前
Docker镜像换源
运维·docker·容器
芯次元玩家2 小时前
技术岗转物联网解决方案架构师,行业洞察、商业模式该从哪开始学?
java·开发语言
EatFan2 小时前
Spring Boot 4 升级避坑指南:依赖变化、Undertow 弃用、4.0.5 补丁与 Java 25 虚拟线程落地
java·spring boot·微服务·虚拟线程·spring boot 4
卓怡学长2 小时前
w213基于Spring Boot框架的Web安全学习系统的设计与实现
java·spring boot·spring·web安全·intellij-idea
Zhou1411362 小时前
MyBatisPlus_02_条件构造器与高级功能
java·开发语言·python
垚垚学技术_聚焦云原生2 小时前
Ansible Role 生产环境标准目录结构与规范化实践
java·elasticsearch·ansible
霸道流氓气质2 小时前
Spring AI Alibaba Graph Studio 入门指南:可视化Agent编排与调试平台
java·人工智能·spring
SimonKing2 小时前
Stream-Nexus:我的轻量级推送中间件(sse、websocket)终于定下来了
java·后端·程序员