在多个 Docker 容器使用同一个 NFS 后端时,强烈建议给每个 Docker(或每个业务/应用)分配单独的 NFS 挂载目录(子目录),而不是让所有容器共用一个根目录。
是的,您的理解非常准确。在标准的 NFS 挂载流程中,确实需要人工(或通过自动化脚本)先在 NFS 服务端创建好子目录,然后再将其挂载到 Docker 宿主机上。
具体分为以下两个核心步骤:
1. 在 NFS 服务端创建子目录并授权
NFS 协议本身不会自动为客户端动态创建目录。您需要提前在 NFS 服务端的根目录下规划并创建好各个子目录,并设置好正确的权限(如 chown 和 chmod),以确保容器内的用户有权限读写。
2. 在 Docker 宿主机上挂载
在每台运行 Docker 的宿主机上,您需要将 NFS 服务端的对应子目录挂载到本地的一个路径(例如 /mnt/nfs/app1_data)。
💡 进阶:如何让 Docker 自动完成挂载?(推荐)
虽然"先挂载到宿主机,再绑定给容器"是最稳妥的做法,但如果您不想手动执行 mount 命令,可以利用 Docker 的 local driver 让 Docker 自动接管 NFS 的挂载工作。
方式一:通过命令行直接创建 NFS 卷
您只需告诉 Docker NFS 服务端的 IP 和子目录路径,Docker 会在容器启动时自动去挂载它:
bash
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw,nfsvers=4.1 \
--opt device=:/nfs_root/app1_data \
app1-vol
方式二:在 Docker Compose 中声明式挂载
如果您使用 Docker Compose,可以直接在 docker-compose.yml 中定义,Docker 会自动处理挂载逻辑:
yaml
version: '3.8'
services:
app1:
image: nginx
volumes:
- app1-vol:/usr/share/nginx/html
volumes:
app1-vol:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,rw,vers=4.1
device: ":/nfs_root/app1_data"
⚠️ 关键注意事项:
- 前提条件 :即使使用上述自动化方式,NFS 服务端的子目录也必须提前创建好,否则挂载会失败。
- 权限问题 :NFS 默认会将客户端的 root 权限降级为
nobody(即root_squash)。如果您的容器需要以 root 身份写入数据,必须在 NFS 服务端的/etc/exports配置中加上no_root_squash参数,或者配置anonuid/anongid对齐容器内的用户 UID。 - 适用场景 :NFS 非常适合存放图片、配置文件、日志等静态数据,但强烈不建议将数据库(如 MySQL)的数据目录放在 NFS 上,因为网络延迟和文件锁机制极易导致数据库损坏或启动卡死。
在 Kubernetes (K8s) 中为容器挂载 NFS 目录,主要有三种常见方式。根据您的需求(是否需要为每个容器分配独立子目录、是否需要自动创建),您可以选择最适合的方案:
方案一:在 Pod 中直接声明式挂载(最简单,适合测试)
如果您只需要快速将某个 NFS 目录挂载到 Pod 中,可以直接在 YAML 文件的 volumes 中指定 NFS 服务器和路径。
示例 YAML:
yaml
apiVersion: v1
kind: Pod
metadata:
name: nfs-test
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: nfs-storage
mountPath: /data # 容器内的挂载路径
volumes:
- name: nfs-storage
nfs:
server: 192.168.1.100 # NFS 服务器 IP
path: /nfs_root/app1_data # NFS 服务端的子目录路径
注:这种方式需要您提前在 NFS 服务端创建好 /nfs_root/app1_data 目录。
方案二:使用 PV + PVC 静态绑定(生产环境推荐)
将存储资源(PV)与业务请求(PVC)解耦,是 K8s 的最佳实践。
1. 创建持久卷 (PV)
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany # 允许多个节点同时读写
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
server: 192.168.1.100
path: /nfs_root/app1_data
2. 创建持久卷声明 (PVC)
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs
resources:
requests:
storage: 10Gi
3. 在 Pod 中引用 PVC
yaml
volumes:
- name: nfs-storage
persistentVolumeClaim:
claimName: nfs-pvc
方案三:使用动态供应(Dynamic Provisioning,终极推荐)
如果您希望 K8s 能够自动在 NFS 服务端创建子目录 ,而不需要每次手动去 NFS 服务器上 mkdir,您需要部署 NFS Subdir External Provisioner 组件。
-
工作原理 :您只需在 NFS 服务端配置一个统一的根目录(如
/nfs_root)。当您在 K8s 中提交 PVC 申请时,该 Provisioner 会自动在/nfs_root下生成一个独立的子目录(如/nfs_root/default-nfs-pvc-xxx),并自动绑定给 Pod。 -
部署方式 :官方推荐使用 Helm 进行一键安装:
bashhelm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \ --set nfs.server=192.168.1.100 \ --set nfs.path=/nfs_root \ --set storageClass.name=nfs-client -
使用方式 :安装后,您只需在 PVC 中指定
storageClassName: nfs-client,无需再手动创建 PV,K8s 会自动完成子目录的创建和挂载。
⚠️ K8s 挂载 NFS 的前置要求
无论使用哪种方案,必须在 K8s 集群的所有 Node(节点)上安装 NFS 客户端工具 ,否则 Pod 会因无法识别 NFS 协议而卡在 ContainerCreating 状态:
- CentOS/RHEL 系统 :
yum install nfs-utils -y - Ubuntu/Debian 系统 :
apt install nfs-common -y
总结建议:
- 如果是临时测试 ,使用方案一。
- 如果是固定业务 ,使用方案二。
- 如果是多租户/微服务架构 ,强烈建议使用方案三(动态供应),它能完美契合您之前"为每个容器分配独立子目录"的需求,且极大降低了运维成本。