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 创建后自动生成
相关推荐
学Linux的语莫几秒前
k8s知识点整体概览
java·linux·kubernetes
VermiliEiz15 分钟前
二进制文件方式部署k8s(2)
kubernetes·云计算·etcd
古城小栈34 分钟前
Spring Boot 容器化:Docker+K8s 部署最佳实践
spring boot·docker·kubernetes
学习3人组1 小时前
docker run 命令详解
运维·docker·容器
神秘面具男032 小时前
Containerd 容器管理工具
容器
阿方索2 小时前
Docker
运维·docker·容器
番茄撒旦在上2 小时前
Docker部署springboot项目
服务器·spring boot·docker·容器
rocksun3 小时前
记一次全自动的问题诊断过程
kubernetes·devops·vibecoding
不想画图3 小时前
Docker 容器核心配置实战:资源管控、数据卷与容器互联
运维·docker·容器
记得记得就1513 小时前
Docker核心功能全解析:网络、资源控制、数据卷
网络·docker·容器