Kubernetes NFS 接入方案

11 阅读9分钟

一、整体架构与流程概览

┌────────────────────────────────────────────────────────────┐
│                    Kubernetes 集群                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  StorageClass (nfs-csi)                             │   │
│  │    provisioner: nfs.csi.k8s.io                      │   │
│  │    parameters: server, share                        │   │
│  └───────────────────┬─────────────────────────────────┘   │
│                      │ 动态创建                             │
│                      ▼                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  PVC (用户申请)  ──绑定──►  PV (自动创建)             |   │
│  └───────────────────┬─────────────────────────────────┘   │
│                      │ 挂载                                │
│                      ▼                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Pod ── 通过 CSI 驱动 ──►  NFS Server                │   │
│  └─────────────────────────────────────────────────────┘   │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │   NFS Server    │
                    │ 192.168.100.13  │
                    │ /exports/share  │
                    └─────────────────┘

核心概念

  • CSI (Container Storage Interface) :Kubernetes 的存储插件标准,让存储厂商可以按统一方式接入
  • NFS CSI Driver:实现了 CSI 接口的 NFS 驱动,能在 PVC 创建时自动在 NFS 服务器上创建子目录作为 PV
  • StorageClass:定义存储的“模板”,指定使用哪个驱动、NFS 服务器地址等参数

二、使用场景

NFS 作为共享文件系统,适合以下场景 :

场景说明访问模式
多 Pod 共享数据多个 Pod 同时读写同一份数据,如内容管理系统、文件服务ReadWriteMany
日志收集多个 Pod 将日志写入共享存储,统一采集ReadWriteMany
Web 应用内容静态网站文件存储在 NFS 上,多个 Web Pod 同时挂载ReadWriteMany
数据分析数据预处理、AI 训练数据集共享ReadWriteMany
CI/CD 构建产物多个构建任务共享输出目录ReadWriteMany

注意:NFS 不适合高 IOPS 的数据库场景(如 MySQL、PostgreSQL),建议使用本地 SSD 或块存储。

如:多个 Pod 写入同一个共享目录,再由 Sidecar 或 DaemonSet 统一采集:

┌─────────────────────────────────────────────────────┐
│  应用 Pod A ──┐                                     │
│  应用 Pod B ──┼──► 共享 NFS 卷 (/var/log/app)        │
│  应用 Pod C ──┘              │                      │
│                              ▼                      │
│                    日志采集器 (Fluentd/Filebeat)     │
│                              │                      │
│                              ▼                      │
│                      Elasticsearch / S3             │
└─────────────────────────────────────────────────────┘

生产优势:采集器只挂载一个 NFS 路径即可收集所有 Pod 的日志,避免每个 Pod 单独配置日志采集。

三、前置准备

1. 准备 NFS 服务器

你需要有一台已配置好的 NFS 服务器,并且所有 K8s 节点都能访问它 。

服务端配置示例(以 192.168.100.13 为例):

# 安装 NFS 服务
yum install -y nfs-utils rpcbind   # CentOS/RHEL/Rocky
# 或 apt install -y nfs-kernel-server  # Ubuntu# 创建共享目录
mkdir -p /data/nfs
chmod 755 /data/nfs
​
# 配置 exports
echo "/data/nfs 192.168.100.0/24(rw,sync,no_root_squash,no_subtree_check)" >> /etc/exports
​
# 启动服务
systemctl start rpcbind nfs-server
systemctl enable rpcbind nfs-server
​
# 验证
showmount -e localhost
​
结果:
    Export list for localhost:
    /data/nfs 192.168.100.0/24

2. 确保节点安装 NFS 客户端

所有 K8s 工作节点(work)安装 nfs-utils,否则挂载会失败(如果控制节点没打污点也需要安装) :

# CentOS/RHEL
yum install -y nfs-utils
​
# Ubuntu
apt install -y nfs-common

四、完整操作流程

步骤 1:安装 NFS CSI Driver

#在你的K8S集群运行:
curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/v4.11.0/deploy/install-driver.sh | bash -s v4.11.0 --


#如果你的服务器无法访问这个地址,人么办?
#例如本人的服务器环境就无法访问😢,不过没关系,你只要能搞下来这个脚本就好。比如迅雷,野生加速网站等👌,以下是风骚操作:
# 1 迅雷下载脚本
# 2 rz上传到服务器 
ls install-driver.sh
sh install-driver.sh v4.11.0
#欸? 这里执行又失败了,打开脚本看看。原来yaml文件也是要在raw.githubusercontent.com下载
# 编辑脚本:
vim install-driver.sh

---
if [ $ver != "master" ]; then
  repo="$repo/$ver"
fi

repo="https://gh-proxy.org/"$repo        #在此处加入这一行。使用野生加速网站

echo "Installing NFS CSI driver, version: $ver ..."
kubectl apply -f $repo/rbac-csi-nfs.yaml
kubectl apply -f $repo/csi-nfs-driverinfo.yaml
---

sh install-driver.sh
#这下就把yaml正常加载了
#但是还没完... 你会发现镜像又下载不下来。目前免费的镜像加速网站几乎绝种了。怎么办?
#那只能是找一台云服务器或科学上网用docker拉取镜像下载下来,再导到harbor或者直接到本机了。
#比如本人使用的csi-driver-nfs v4.11.0,需要的镜像有:
---
registry.k8s.io/sig-storage/csi-resizer         v2.1.0      589e525cddef    12 hours ago    linux/amd64    94.02MB    37.25MB
registry.k8s.io/sig-storage/csi-snapshotter     v8.5.0      da081c27e8a6    12 hours ago    linux/amd64    94.39MB    37.29MB
registry.k8s.io/sig-storage/csi-node-driver-registrar    v2.16.0     ab482308a492    12 hours ago    linux/amd64    30.21MB    14.11MB
gcr.io/k8s-staging-sig-storage/nfsplugin        canary      eb3733a6070b    12 hours ago    linux/amd64    161.8MB    56.32MB
registry.k8s.io/sig-storage/livenessprobe       v2.18.0     c4cc074199c0    12 hours ago    linux/amd64    30.43MB    14.22MB
registry.k8s.io/sig-storage/csi-provisioner     v6.2.0      6be9f63ca4ca    12 hours ago    linux/amd64    102.4MB    39.52MB
---

# docker下载镜像导出:
docker pull registry.k8s.io/sig-storage/csi-resize:v2.1.0
docker save -o csi-resize.tgz registry.k8s.io/sig-storage/csi-resize:v2.1.0
# 如果你用的是containerd, 用nerdctl 导入:
nerdctl -n k8s.io load -i csi-resize.tgz
# 如果你有harbor仓库的话会方便一点,如果没有需要每台node都导入,最后:
kubectl  get pod -n kube-system | grep nfs
csi-nfs-controller-675cf9f6c9-n7kz8        5/5     Running   0              11m
csi-nfs-node-5mznd                         3/3     Running   0              13m
csi-nfs-node-7ch8j                         3/3     Running   0              13m
csi-nfs-node-hg86g                         3/3     Running   0              12m
csi-nfs-node-qj6fc                         3/3     Running   0              13m
csi-nfs-node-sp8v4                         3/3     Running   0              12m

步骤 2:创建 StorageClass

这是关键配置,告诉 Kubernetes 如何使用 NFS 动态创建存储

创建 sc-nfs.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io     # 指定使用 NFS CSI 驱动
parameters:
  server: 192.168.100.13        # 替换为你的 NFS 服务器 IP
  share: /data/nfs              # NFS 共享路径
    mountPermissions: '0777'    # 指定文件权限
  # subDir: ${pvc.name}         # 可选:自动创建子目录命名规则
mountOptions:
  - nfsvers=4.2                 # NFS 协议版本 (这里需要再nfs服务器查看支持的版本:cat /proc/fs/nfsd/versions )
  - hard                        # 硬挂载,失败时持续重试
  - noatime                     # 不更新访问时间,提升性能
reclaimPolicy: Retain           # PVC 删除时保留后端数据 ,如果Delete就是不保留
volumeBindingMode: Immediate    # PVC 创建后立即尝试绑定符合条件的 PV
kubectl get sc
NAME      PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-csi   nfs.csi.k8s.io   Retain          Immediate           false                  16s

参数说明

  • server:NFS 服务器地址(必填)
  • share:NFS 共享根路径(必填)
  • subDir:可选,自定义子目录命名规则,默认使用 pvc-{namespace}-{name}
  • mountOptions:挂载选项,可指定 NFS 协议版本、超时策略等

步骤 3:创建 PVC(PersistentVolumeClaim)

用户创建 PVC 申请存储,系统会自动创建对应的 PV 。

创建 pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-demo-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany          # 支持多 Pod 同时读写(多路读写)
  storageClassName: nfs-csi  # 使用上面创建的 StorageClass
  resources:
    requests:
      storage: 5Gi          # 申请 5GB 空间

验证:

[root@vm-100-11 pv]# kubectl  apply  -f pvc.yaml 
persistentvolumeclaim/nfs-demo-pvc created
[root@vm-100-11 pv]# kubectl  get pvc 
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
nfs-demo-pvc   Bound    pvc-4524298a-aa5e-4574-90b6-9f76f73207d7   5Gi        RWX            nfs-csi        <unset>                 9s
[root@vm-100-11 pv]# kubectl  get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-4524298a-aa5e-4574-90b6-9f76f73207d7   5Gi        RWX            Retain           Bound    default/nfs-demo-pvc   nfs-csi        <unset>                          15s

关键点

  • PVC 状态变为 Bound 表示绑定成功
  • PV 是自动创建的,名称格式为 pvc-{uuid}
  • 在 NFS 服务器上会自动创建一个子目录(路径:/data/nfs/share/pvc-abc123...

步骤 4:在 Pod 中使用 PVC

创建 pod.yaml 挂载 PVC:

apiVersion: v1
kind: Pod
metadata:
  name: nfs-test-pod
spec:
  containers:
  - name: app
    image: nginx:alpine
    volumeMounts:
    - name: persistent-storage
      mountPath: /usr/share/nginx/html   # 容器内挂载路径
    ports:
    - containerPort: 80
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: nfs-demo-pvc            # 引用上面创建的 PVC

部署测试:

kubectl  apply -f pod.yaml 
kubectl exec nfs-test-pod -- sh -c "echo 'Hello NFS' > /usr/share/nginx/html/test.txt"
# 在 NFS 服务器上验证文件已创建
# 预期在 /data/nfs/pvc-xxx/test.txt 中看到内容

步骤 5:Deployment 共享同一个 PVC 的配置

  • 使用的 StorageClass 或 PVC 必须支持 ReadWriteMany(NFS-CSI 默认支持)
  • 所有 Pod 副本需要读写同一份数据时,应用需能处理并发访问(如文件锁、数据库锁等)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-shared
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-shared
  template:
    metadata:
      labels:
        app: nginx-shared
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        volumeMounts:
        - name: shared-storage
          mountPath: /usr/share/nginx/html   # 所有容器共享此目录
        ports:
        - containerPort: 80
      volumes:
      - name: shared-storage
        persistentVolumeClaim:
          claimName: nfs-demo-pvc           # 所有 Pod 共用同一个 PVC

验证:

kubectl  apply  -f testpvc-depolyment.yaml  
​
[root@vm-100-11 pv]# kubectl  get pods  -l app=nginx-shared
NAME                            READY   STATUS    RESTARTS   AGE
nginx-shared-796bb49fdb-5t9fj   1/1     Running   0          94s
nginx-shared-796bb49fdb-cr7dn   1/1     Running   0          94s
nginx-shared-796bb49fdb-r9qqg   1/1     Running   0          94s
[root@vm-100-11 pv]# kubectl  get pods  -l app=nginx-shared -o wide
 NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-shared-796bb49fdb-5t9fj   1/1     Running   0          99s   10.1.12.144   vm-100-22   <none>           <none>
nginx-shared-796bb49fdb-cr7dn   1/1     Running   0          99s   10.1.10.145   vm-100-21   <none>           <none>
nginx-shared-796bb49fdb-r9qqg   1/1     Running   0          99s   10.1.12.145   vm-100-22   <none>           <none>
[root@vm-100-11 pv]#  curl 10.1.12.144/test.txt
Hello NFS
[root@vm-100-11 pv]#  curl 10.1.12.145/test.txt
Hello NFS
[root@vm-100-11 pv]#  curl 10.1.10.145/test.txt
Hello NFS

五、完整流程图

┌─────────────────────────────────────────────────────────────────┐
                    1. 环境准备                                   
  ┌──────────────┐     ┌──────────────────────────────────────┐  
   NFS Server   │────▶│ 所有 Worker 节点安装 nfs-utils         
  │192.168.100.13│     └──────────────────────────────────────┘  
  └──────────────┘                                               
└─────────────────────────────────────────────────────────────────┘
                              
┌─────────────────────────────────────────────────────────────────┐
                    2. 安装 NFS CSI Driver                        
  kubectl create -f https://raw.githubusercontent.com/...        
   helm install ...                                             
└─────────────────────────────────────────────────────────────────┘
                              
┌────────────────────────────────────────────────────────────────┐
                    3. 创建 StorageClass                         
  ┌──────────────────────────────────────────────────────────┐  
   apiVersion: storage.k8s.io/v1                              
   kind: StorageClass                                         
   metadata: name: nfs-csi                                    
   provisioner: nfs.csi.k8s.io                                
   parameters: server: 192.168.100.13, share: /data/nfs/      
  └──────────────────────────────────────────────────────────┘  
└────────────────────────────────────────────────────────────────┘
                              
┌────────────────────────────────────────────────────────────────┐
                    4. 创建 PVC                                  
  ┌──────────────────────────────────────────────────────────┐  
   PVC: nfs-demo-pvc (storage: 5Gi)                          
                                                             
                       自动绑定                               
   PV: pvc-xxx (自动创建)                                      
                                                             
          NFS 服务器上创建子目录                             
   /data/nfs/pvc-xxx/                                         
  └──────────────────────────────────────────────────────────┘  
└────────────────────────────────────────────────────────────────┘
                              
┌────────────────────────────────────────────────────────────────┐
                    5. Pod 挂载使用                              
  ┌──────────────────────────────────────────────────────────┐  
   Pod: nginx                                                 
     volumes: persistentVolumeClaim: nfs-demo-pvc             
     volumeMounts: mountPath: /usr/share/nginx/html           
  └──────────────────────────────────────────────────────────┘  
└────────────────────────────────────────────────────────────────┘

六、常见问题与注意事项

1. 挂载失败:no such file or directory

  • 检查 NFS 服务器地址和共享路径是否正确
  • 确认所有节点都能 showmount -e <server> 看到共享

2. Pod 启动报错:bad option; need /sbin/mount.nfs

  • 工作节点未安装 nfs-utils,执行 yum install -y nfs-utils

3. 权限问题:无法写入文件

  • 检查 NFS 服务端的 /etc/exports 中的 no_root_squash 配置
  • 或在 StorageClass 的 mountOptions 中添加 no_root_squash

4. 协议版本降级

  • 如果 NFS 服务端不支持 nfsvers=4.2,会自动降级到低版本
  • 可在服务端执行 cat /proc/fs/nfsd/versions 确认支持的版本

5. reclaimPolicy: Delete 会删除数据

  • PVC 删除时,后端 NFS 子目录也会被删除
  • 如需保留数据,修改 StorageClass 中的 reclaimPolicy: Retain

七、总结

组件作用配置要点
NFS Server提供底层存储确保所有 K8s 节点可访问,exports 配置正确
CSI Driver实现存储插件接口安装 nfs.csi.k8s.io 驱动
StorageClass定义存储模板配置 NFS 服务器地址、共享路径、挂载选项
PVC用户申请存储指定 StorageClass 名称和容量
Pod使用存储通过 volumes.persistentVolumeClaim 引用 PVC

企业级架构方案

生产环境中,NFS 服务器本身不能是单点故障。常见的高可用方案:

方案说明
Keepalived + NFS多台 NFS 服务器通过 VIP 提供主备切换
CephFS + NFS-Ganesha底层使用 CephFS,通过 NFS 网关暴露,支持快照和克隆
云厂商托管 NFS如 AWS EFS、Azure Files、阿里云 NAS,自带高可用

生产建议:

考量维度建议
网络带宽确保 NFS 网络接口有足够带宽,或使用专用 NIC 隔离流量,避免与其他网络争抢
CPU 资源NFS 网关的 CPU 利用率随共享数量增加而上升,需预留足够资源
故障切换窗口高可用 NFS 发生主备切换时,通常有几分钟的故障转移窗口,应用需能容忍这个时间

协议版本选择

NFS 版本生产建议
NFSv4.1/v4.2推荐使用,支持更强安全特性、状态协议、性能优化
NFSv3降级选项,如果后端仅支持 v3 时可用

安全配置

根据生产实践,NFS 共享需要关注以下安全维度:

安全维度生产推荐配置
NFS Export 权限rw,sync,root_squash 限制 root 写入(而非 no_root_squash
容器用户避免使用 root,配置 securityContext.runAsUserfsGroup
子目录隔离不同项目使用不同的 PVC,或通过 subPath 隔离
访问控制配合 RBAC + PVC scope + namespace 实现租户隔离

场景总结

场景类型是否推荐 NFS-CSI原因
静态内容服务(多副本 Web)✅ 强烈推荐天然支持 ReadWriteMany,内容更新方便
日志/指标采集✅ 推荐统一存储路径,简化采集架构
AI 模型加载✅ 推荐多 Pod 共享只读模型,节省存储空间
数据处理流水线✅ 推荐解耦生产者和消费者
开发测试环境✅ 推荐动态供给,自动化管理
高吞吐数据库❌ 不推荐NFS 网络延迟和锁机制不适合 OLTP 场景
需要强一致性锁的场景⚠️ 谨慎应用需自行处理并发写入冲突