一、整体架构与流程概览
vbscript
┌────────────────────────────────────────────────────────────┐
│ 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 统一采集:
css
┌─────────────────────────────────────────────────────┐
│ 应用 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 为例):
bash
# 安装 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,否则挂载会失败(如果控制节点没打污点也需要安装) :
bash
# CentOS/RHEL
yum install -y nfs-utils
# Ubuntu
apt install -y nfs-common
四、完整操作流程
步骤 1:安装 NFS CSI Driver
yaml
#在你的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:
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
arduino
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:
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 空间
验证:
swift
[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:
yaml
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
部署测试:
bash
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 副本需要读写同一份数据时,应用需能处理并发访问(如文件锁、数据库锁等)
yaml
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
验证:
sql
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
五、完整流程图
yaml
┌─────────────────────────────────────────────────────────────────┐
│ 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.runAsUser 与 fsGroup |
| 子目录隔离 | 不同项目使用不同的 PVC,或通过 subPath 隔离 |
| 访问控制 | 配合 RBAC + PVC scope + namespace 实现租户隔离 |
场景总结
| 场景类型 | 是否推荐 NFS-CSI | 原因 |
|---|---|---|
| 静态内容服务(多副本 Web) | ✅ 强烈推荐 | 天然支持 ReadWriteMany,内容更新方便 |
| 日志/指标采集 | ✅ 推荐 | 统一存储路径,简化采集架构 |
| AI 模型加载 | ✅ 推荐 | 多 Pod 共享只读模型,节省存储空间 |
| 数据处理流水线 | ✅ 推荐 | 解耦生产者和消费者 |
| 开发测试环境 | ✅ 推荐 | 动态供给,自动化管理 |
| 高吞吐数据库 | ❌ 不推荐 | NFS 网络延迟和锁机制不适合 OLTP 场景 |
| 需要强一致性锁的场景 | ⚠️ 谨慎 | 应用需自行处理并发写入冲突 |