一、背景与适用场景
在 Kubernetes 集群中,持久化存储是核心能力之一。对于中小规模集群或私有化环境,NFS(Network File System)因其部署简单、成本低、易维护,成为非常常见的存储方案。
尤其在以下场景中非常适用:
- 数据库(PostgreSQL / MySQL / Redis 持久化)
- 日志存储(ELK / Loki)
- 中间件(RabbitMQ / Kafka 部分场景)
- 开发测试环境
本文基于实战经验(含 PostgreSQL Operator + NFS 动态存储),总结完整部署流程 + 常见踩坑。
二、整体架构
Pod → PVC → StorageClass → NFS Provisioner → NFS Server
组件说明:
- PVC:用户申请存储
- StorageClass:定义存储策略
- NFS Provisioner:动态创建目录
- NFS Server:真正存储数据
推荐组件:
- nfs-subdir-external-provisioner(官方维护)
三、NFS 服务端部署
1. 安装 NFS
yum install -y nfs-utils # CentOS
apt install -y nfs-kernel-server # Ubuntu
2. 创建共享目录
mkdir -p /data/nfs
chmod -R 777 /data/nfs
3. 配置 exports
vim /etc/exports
/data/nfs *(rw,sync,no_root_squash,no_subtree_check)
4. 启动服务
systemctl enable nfs-server
systemctl start nfs-server
exportfs -rv
四、Kubernetes 客户端准备
所有节点必须安装:
yum install -y nfs-utils
否则会出现典型报错:
mount.nfs: command not found
五、部署 NFS 动态 Provisioner
1. 创建 RBAC
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-client-provisioner
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
2. 部署 Provisioner
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: <NFS_SERVER_IP>
- name: NFS_PATH
value: /data/nfs
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
volumes:
- name: nfs-client-root
nfs:
server: <NFS_SERVER_IP>
path: /data/nfs
六、创建 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: example.com/nfs
reclaimPolicy: Retain
volumeBindingMode: Immediate
七、PVC 测试
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs-storage
验证:
kubectl get pvc
八、实战踩坑总结(重点)
1. 权限问题(最常见)
现象:
Permission denied
原因:
- NFS 默认 root_squash
解决:
no_root_squash
chmod 777
2. Pod 一直 Pending
原因排查顺序:
1)PVC 是否 Bound
2)Provisioner 是否运行
3)StorageClass 是否匹配
关键命令:
kubectl describe pvc
kubectl logs provisioner
3. mount 失败
报错:
mount.nfs: access denied
原因:
- exports 未放开
- 防火墙未开
解决:
systemctl stop firewalld
或放行 2049 端口
4. 性能问题(核心坑)
NFS 本质问题:
- 单点瓶颈
- 高 IO 延迟
优化建议:
- SSD 磁盘
- 独立存储网络
- 调整 mount 参数
rsize=1048576,wsize=1048576
5. PostgreSQL 场景踩坑(重点)
问题:数据库性能差 / 卡顿
原因:
- fsync + NFS 延迟
解决建议:
- 测试环境可用
- 生产建议:Ceph / 云盘
6. reclaimPolicy 坑
- Delete:删除 PVC 会删数据
- Retain:数据保留(推荐)
7. 子目录爆炸问题
现象:
/data/nfs/
pvc-xxxx
pvc-yyyy
问题:
- 文件数量过多
- inode 耗尽
建议:
- 定期清理
- 规范命名
九、最佳实践(强烈建议)
1. 生产环境建议
| 场景 | 推荐存储 |
|---|---|
| 测试 | NFS |
| 生产数据库 | Ceph / 云盘 |
| 日志 | NFS / 对象存储 |
2. 高可用方案
NFS 本身无 HA,建议:
- Keepalived + VIP
- 双机热备
3. 安全建议
- 限制 IP 白名单
- 不要使用
*
十、总结
NFS 动态存储:
优点:
- 部署简单
- 成本低
缺点:
- 性能瓶颈
- 无高可用
一句话总结:
👉 “能用,但别在核心生产数据库上用”
十一、进阶建议
如果你已经做到这一步,下一步建议:
- 学习 Ceph(Rook)
- 使用 CSI 标准驱动
- 引入存储监控(Prometheus + Grafana)
(附)排障速查表
| 问题 | 关键命令 |
|---|---|
| PVC Pending | kubectl describe pvc |
| 挂载失败 | showmount -e |
| 权限问题 | chmod 777 |
| Provisioner 异常 | kubectl logs |