k8s 部署NFS和动态供应器

一、环境清单
集群基础信息
项目 配置值
集群类型 单 Master + 3 Worker(1 主 3 从)
Kubernetes 版本 v1.23.0
容器运行时 Docker 26.1.4
网络插件 Calico v3.22
操作系统 CentOS 7.9
内核版本 3.10.0-1160.el7.x86_64
Pod 网络段 10.244.0.0/16(Calico 默认)
Service 网络段 10.96.0.0/12(K8s 默认)
CPU 4核
内存 8G
机器名称 机器IP 操作系统 角色
k8s-master 10.132.47.60 Centos7.9 master
k8s-node1 10.132.47.61 Centos7.9 Worker
k8s-node2 10.132.47.62 Centos7.9 Worker
k8s-node3 10.132.47.63 Centos7.9 Worker

环境部署查阅我的这篇文章:Centos7.9 安装K8S 1master3node-CSDN博客

二、配置NFS
所有节点安装
bash 复制代码
# CentOS/RHEL系统
yum install -y nfs-utils rpcbind

# Ubuntu/Debian系统
apt update && apt install -y nfs-kernel-server
创建共享目录
bash 复制代码
# 创建共享目录(用于存储MySQL数据)
mkdir -p /data/nfs/mysql
# 设置目录权限(确保K8s节点能读写)
chmod -R 777 /data/nfs/mysql
chown -R nfsnobody:nfsnobody /data/nfs/mysql
配置共享规则

编辑 NFS 配置文件 /etc/exports,添加以下内容:

bash 复制代码
# 格式:共享目录 K8s节点网段/子网掩码(rw,sync,no_root_squash,no_all_squash)
/data/nfs/mysql 10.132.47.60/24(rw,sync,no_root_squash,no_all_squash)
  • rw:读写权限
  • sync:数据实时同步到磁盘(保证数据一致性)
  • no_root_squash:允许 root 用户操作(避免 K8s Pod 权限问题)
  • no_all_squash:保留用户身份(不映射为匿名用户)
配置服务
bash 复制代码
# CentOS/RHEL系统
systemctl start rpcbind
systemctl start nfs-server
systemctl enable rpcbind
systemctl enable nfs-server

# Ubuntu/Debian系统
systemctl start nfs-kernel-server
systemctl enable nfs-kernel-server

# 使配置生效(修改exports后执行)
exportfs -r
# 验证共享配置
exportfs -v
开放防火墙规则
bash 复制代码
# CentOS/RHEL(firewalld)
firewall-cmd --permanent --add-service=nfs
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
firewall-cmd --reload

# 关闭防火墙(测试环境可选,生产环境不推荐)
# systemctl stop firewalld && systemctl disable firewalld
验证NFS
bash 复制代码
# 所有节点查看NFS服务器的共享列表
showmount -e 10.132.47.60
挂载测试
bash 复制代码
# 在K8s节点创建临时目录
mkdir -p /tmp/test-nfs
# 挂载NFS共享目录到临时目录
mount -t nfs 10.132.47.60:/data/nfs/mysql /tmp/test-nfs
# 测试读写:创建文件
touch /tmp/test-nfs/test.txt
# 查看文件是否同步到NFS服务器(可登录NFS服务器查看/data/nfs/mysql目录)
# 卸载临时挂载(测试完成后)
umount /tmp/test-nfs
三、部署 NFS 动态供应器(nfs-subdir-external-provisioner)

自动为 PVC 创建对应的 PV,并关联 NFS 共享目录,避免手动创建 PV 的繁琐。

创建部署清单 nfs-provisioner.yaml
yaml 复制代码
# 1. 服务账户:给供应器授权访问 K8s API 的权限
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner  # 服务账户名称
  namespace: kube-system  # 部署在系统命名空间,便于管理
---
# 2. 集群角色:定义供应器需要的权限(操作 PV、PVC、StorageClass 等)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-provisioner-runner  # 角色名称
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]  # 允许操作 PV
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]  # 允许操作 PVC
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]  # 允许查看 StorageClass
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]  # 允许生成事件(便于排障)
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]  # 允许获取服务信息
  - apiGroups: ["extensions"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "delete", "update"]  # 允许操作自身部署
---
# 3. 集群角色绑定:将角色权限绑定到服务账户
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: run-nfs-provisioner  # 绑定名称
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner  # 关联上面创建的服务账户
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner  # 关联上面创建的角色
  apiGroup: rbac.authorization.k8s.io
---
# 4. 部署供应器 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner  # 部署名称
  namespace: kube-system  # 与服务账户同命名空间
spec:
  replicas: 1  # 单副本(生产可根据需求调整)
  selector:
    matchLabels:
      app: nfs-client-provisioner  # 匹配 Pod 标签
  strategy:
    type: Recreate  # 重建策略(更新时删除旧 Pod 再创建新的)
  template:
    metadata:
      labels:
        app: nfs-client-provisioner  # Pod 标签
    spec:
      serviceAccountName: nfs-provisioner  # 使用上面创建的服务账户
      containers:
        - name: nfs-client-provisioner  # 容器名称
          # 镜像:使用阿里云镜像,避免国外镜像拉取失败(适配 K8s v1.23)
          image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root  # 挂载 NFS 共享目录到容器内
              mountPath: /persistentvolumes  # 容器内挂载点
          env:
            - name: PROVISIONER_NAME  # 供应器名称,需与 StorageClass 一致
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER  # NFS 服务器 IP(你的 master 节点 IP)
              value: 10.132.47.60
            - name: NFS_PATH  # NFS 共享目录(之前创建的 /data/nfs/mysql)
              value: /data/nfs/mysql
      volumes:
        - name: nfs-client-root  # 定义 NFS 卷
          nfs:
            server: 10.132.47.60  # NFS 服务器 IP(同上)
            path: /data/nfs/mysql  # NFS 共享目录(同上)
启动
bash 复制代码
kubectl apply -f nfs-provisioner.yaml
验证部署
bash 复制代码
# 查看 Pod 是否运行(状态为 Running 表示成功)
kubectl get pods -n kube-system | grep nfs-client-provisioner
------------------------------------------------------------------------------------------------------------
# 输出示例:
[root@k8s-master nfs]# kubectl get pods -n kube-system | grep nfs-client-provisioner
nfs-client-provisioner-5bfb45b7b8-6xd5z    1/1     Running   0               29s
四、创建 StorageClass

定义动态供应的规则(如回收策略、是否允许扩容等),关联 NFS 供应器。

创建清单 storageclass.yaml
yaml 复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage  # StorageClass 名称,PVC 会引用此名称
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner  # 必须与供应器的 PROVISIONER_NAME 一致
parameters:
  archiveOnDelete: "false"  # 删除 PVC 时是否归档数据(false 表示直接删除,避免残留文件)
reclaimPolicy: Delete  # PV 回收策略(Delete:删除 PVC 时自动删除 PV;Retain:保留 PV 手动处理)
allowVolumeExpansion: true  # 允许 PVC 扩容(需在 PVC 中修改 storage 请求)
volumeBindingMode: Immediate  # 立即绑定(PVC 创建后立即分配 PV)
部署启动
bash 复制代码
kubectl apply -f storageclass.yaml
验证 StorageClass
bash 复制代码
kubectl get sc  # sc 是 storageclasses 的缩写
------------------------------------------------------------------------------------------------------------
# 输出示例
[root@k8s-master nfs]# kubectl get sc
NAME          PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-storage   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           true                   24s
五、创建 PVC 测试动态供应

通过 PVC 请求存储,验证动态供应器是否自动创建对应的 PV 并绑定。

创建清单 test-mysql-pvc.yaml(模拟 MySQL 存储需求)
yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data-pvc  # PVC 名称,供 Pod 引用
  namespace: default  # 可根据实际命名空间调整(如创建 mysql 命名空间)
spec:
  accessModes:
    - ReadWriteMany  # NFS 支持多节点读写(RWX),适合 MySQL 主从等场景
  resources:
    requests:
      storage: 5Gi  # 请求 5GB 存储(根据实际需求调整)
  storageClassName: nfs-storage  # 指定使用上面创建的 StorageClass
部署启动
bash 复制代码
kubectl apply -f test-mysql-pvc.yaml
验证PV/PVC绑定
bash 复制代码
# 查看 PVC 状态(STATUS 为 Bound 表示绑定成功)
kubectl get pvc mysql-data-pvc -n default
------------------------------------------------------------------------------------------------------------
# 输出示例:
[root@k8s-master nfs]# kubectl get pvc mysql-data-pvc -n default
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-data-pvc   Bound    pvc-9ea1beb5-7e35-4955-a567-66eead2161c3   5Gi        RWX            nfs-storage    7m15s
[root@k8s-master nfs]# kubectl get pvc mysql-data-pvc -n default
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-data-pvc   Bound    pvc-9ea1beb5-7e35-4955-a567-66eead2161c3   5Gi        RWX            nfs-storage    10m
------------------------------------------------------------------------------------------------------------


# 查看自动创建的 PV(与 PVC 绑定,容量和访问模式一致)
kubectl get pv
------------------------------------------------------------------------------------------------------------
# 输出示例:
[root@k8s-master nfs]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pvc-9ea1beb5-7e35-4955-a567-66eead2161c3   5Gi        RWX            Delete           Bound    default/mysql-data-pvc   nfs-storage             4m25s
六、清理测试资源(可选)
bash 复制代码
# 删除 PVC(会自动删除关联的 PV 和 NFS 子目录,因 reclaimPolicy 为 Delete)
kubectl delete pvc mysql-data-pvc -n default

# 如需删除 StorageClass 和供应器(谨慎操作,会影响依赖的资源)
kubectl delete sc nfs-storage
kubectl delete -f nfs-provisioner.yaml
七、可能遇到的问题

NFS 动态供应器的服务账户(nfs-provisioner)缺少创建 endpoints 资源的权限,导致 leader 选举失败(供应器需要通过 leader 选举确保单实例运行)

解决方法:补充 RBAC 权限(添加 endpoints 资源操作权限)

需要修改之前的 ClusterRole 配置,新增对 endpoints 资源的 createupdatepatch 权限。

编辑集群角色(nfs-provisioner-runner
bash 复制代码
kubectl edit clusterrole nfs-provisioner-runner
rules 中添加以下内容(补充 endpoints 权限)

找到现有规则,在其中新增一段关于 endpoints 的配置,完整规则如下:

yaml 复制代码
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: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]  # 保留原有的 services,新增 endpoints
    verbs: ["get", "create", "update", "patch"]  # 新增 create、update、patch 权限
  - apiGroups: ["extensions"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "delete", "update"]
  # 新增 leader 选举需要的 leases 权限(K8s v1.14+ 用 leases 替代 endpoints 做选举)
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["get", "create", "update"]

直接替换完整的 ClusterRole 配置(避免手动编辑出错)

bash 复制代码
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-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: ["create", "update", "patch"]
- apiGroups: [""]
  resources: ["services", "endpoints"]
  verbs: ["get", "create", "update", "patch"]  # 补充 create/update/patch 权限
- apiGroups: ["extensions"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "delete", "update"]
- apiGroups: ["coordination.k8s.io"]  # 新增 leases 权限(leader 选举必需)
  resources: ["leases"]
  verbs: ["get", "create", "update"]
EOF
存退出后,重启 NFS 供应器 Pod
bash 复制代码
# 删除现有 Pod(Deployment 会自动重建)
kubectl delete pod -n kube-system nfs-client-provisioner-5bfb45b7b8-6xd5z
验证权限是否生效
bash 复制代码
kubectl logs -n kube-system <新的 Pod 名称>  # 新 Pod 名称可通过 kubectl get pods -n kube-system 查看
检查 PVC 状态

当供应器正常运行后,PVC 应自动绑定 PV:

bash 复制代码
kubectl get pvc mysql-data-pvc -n default
八、清单整合
核心清单:NFS 动态供应器(含完整 RBAC 权限)

文件名:nfs-provisioner-complete.yaml(整合所有依赖资源,一次部署)

yaml 复制代码
# 1. 服务账户:授权供应器访问 K8s API
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
  namespace: kube-system
---
# 2. 集群角色:包含 leader 选举+存储操作完整权限(已修复 endpoints/leases 权限)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-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: ["create", "update", "patch"]
- apiGroups: [""]
  resources: ["services", "endpoints"]
  verbs: ["get", "create", "update", "patch"]  # 解决 leader 选举权限问题
- apiGroups: ["extensions"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "delete", "update"]
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["get", "create", "update"]  # 兼容 K8s 新版选举机制
---
# 3. 集群角色绑定:关联服务账户与权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
  name: nfs-provisioner
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
# 4. 动态供应器部署:关联 NFS 服务器配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-provisioner
      containers:
      - name: nfs-client-provisioner
        image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2  # 国内镜像,避免拉取失败
        volumeMounts:
        - name: nfs-client-root
          mountPath: /persistentvolumes
        env:
        - name: PROVISIONER_NAME
          value: k8s-sigs.io/nfs-subdir-external-provisioner  # 与 StorageClass 供应器名称一致
        - name: NFS_SERVER
          value: 10.132.47.60  # 你的 NFS 服务器(master 节点)IP
        - name: NFS_PATH
          value: /data/nfs/mysql  # 你的 NFS 共享目录
      volumes:
      - name: nfs-client-root
        nfs:
          server: 10.132.47.60
          path: /data/nfs/mysql
验证用 PVC 清单(测试动态供应)

文件名:mysql-data-pvc.yaml(模拟 MySQL 持久化存储需求)

yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data-pvc
  namespace: default  # 可根据实际应用命名空间调整
spec:
  accessModes:
  - ReadWriteMany  # NFS 支持多节点读写(适配 MySQL 主从等场景)
  resources:
    requests:
      storage: 5Gi  # 存储容量需求(可按需修改)
  storageClassName: nfs-storage  # 关联已存在的 StorageClass
部署验证
bash 复制代码
#部署 NFS 动态供应器
kubectl apply -f nfs-provisioner-complete.yaml

#验证供应器运行状态
kubectl get pods -n kube-system | grep nfs-client-provisioner

#部署测试 PVC
kubectl apply -f mysql-data-pvc.yaml

#验证 PV/PVC 绑定
# 查看 PVC 状态(Bound 表示成功)
kubectl get pvc mysql-data-pvc -n default

# 查看自动创建的 PV
kubectl get pv

#最终验证 NFS 目录
ls /data/nfs/mysql
九、配置(方便记录学习)
集群基础信息
项目 配置值
集群类型 单 Master + 3 Worker(1 主 3 从)
Kubernetes 版本 v1.23.0
容器运行时 Docker 26.1.4
网络插件 Calico v3.22
操作系统 CentOS 7.9
内核版本 3.10.0-1160.el7.x86_64
Pod 网络段 10.244.0.0/16(Calico 默认)
Service 网络段 10.96.0.0/12(K8s 默认)
CPU 4 核
内存 8G
节点信息
机器名称 机器 IP 操作系统 角色 主要功能
k8s-master 10.132.47.60 Centos7.9 Master + NFS 服务器 集群控制面 + 提供 NFS 共享存储
k8s-node1 10.132.47.61 Centos7.9 Worker 运行应用 Pod + NFS 客户端
k8s-node2 10.132.47.62 Centos7.9 Worker 运行应用 Pod + NFS 客户端
k8s-node3 10.132.47.63 Centos7.9 Worker 运行应用 Pod + NFS 客户端
NFS 服务器配置(master 节点)
配置项 配置值
共享目录路径 /data/nfs/mysql
目录权限 777(读写执行)
目录所属用户 / 组 nfsnobody:nfsnobody
共享规则(/etc/exports) /data/nfs/mysql 10.132.47.0/24(rw,sync,no_root_squash,no_all_squash)
依赖服务 rpcbind、nfs-server(已开机自启)
服务状态 运行中
NFS 动态供应器配置
配置项 配置值
供应器名称 nfs-client-provisioner
部署命名空间 kube-system
服务账户名称 nfs-provisioner
集群角色名称 nfs-provisioner-runner
集群角色绑定名称 run-nfs-provisioner
供应器镜像 registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2
供应器标识(PROVISIONER_NAME) k8s-sigs.io/nfs-subdir-external-provisioner
关联 NFS 服务器 IP 10.132.47.60
关联 NFS 共享目录 /data/nfs/mysql
运行副本数 1
容器挂载点 /persistentvolumes
核心权限 PV/PVC/StorageClass 操作、endpoints/leases leader 选举权限
StorageClass 配置
配置项 配置值
StorageClass 名称 nfs-storage
关联供应器 k8s-sigs.io/nfs-subdir-external-provisioner
回收策略 Delete(删除 PVC 时自动删除 PV)
允许扩容 是(allowVolumeExpansion: true)
绑定模式 Immediate(立即绑定)
额外参数 archiveOnDelete: "false"(删除 PVC 不归档数据)
状态 可用(Available)
PVC 配置(测试 / 业务用)
配置项 配置值
PVC 名称 mysql-data-pvc
所属命名空间 default
关联 StorageClass nfs-storage
访问模式 ReadWriteMany(RWX,多节点读写)
申请存储容量 5Gi
状态 待绑定 / 已绑定(根据实际部署结果更新)
用途 模拟 MySQL 持久化存储
PV 配置(动态创建)
配置项 配置值
PV 名称 自动生成(格式:pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
关联 PVC default/mysql-data-pvc
存储容量 5Gi(与 PVC 申请容量一致)
访问模式 ReadWriteMany(RWX)
回收策略 Delete(继承 StorageClass 配置)
存储后端 NFS(10.132.47.60:/data/nfs/mysql/[子目录])
动态创建触发条件 PVC 创建后自动生成
相关推荐
hweiyu003 小时前
Docker(K8S)容器架构教程(视频教程)
docker·架构·kubernetes
眠りたいです4 小时前
基于脚手架微服务的视频点播系统-客户端业务逻辑处理部分(三)-客户端主体部分完结
c++·微服务·云原生·架构·json·restful·qt6.7
jyan_敬言7 小时前
【Docker】定义和运行多容器应用程序
运维·docker·容器·学习方法
summer_west_fish8 小时前
K8S Base: CrashLoopBackOff
云原生·容器·kubernetes
过客随尘8 小时前
Redis主从同步以及Redis-Shake数据同步实战
redis·云原生
阿里云云原生9 小时前
实战|魔方文娱全链路可观测架构设计:SLS+ARMS+Prometheus+云拨测落地指南
云原生
和光同尘202310 小时前
使用Rancher快速部署K8S集群
docker·云原生·容器·kubernetes·centos·rancher·虚拟机
shixian103041111 小时前
Dify Docker Compose 安装指南
docker·容器·eureka