如何在docker和k8s中使用nfs文件系统

在多个 Docker 容器使用同一个 NFS 后端时,强烈建议给每个 Docker(或每个业务/应用)分配单独的 NFS 挂载目录(子目录),而不是让所有容器共用一个根目录。

是的,您的理解非常准确。在标准的 NFS 挂载流程中,确实需要人工(或通过自动化脚本)先在 NFS 服务端创建好子目录,然后再将其挂载到 Docker 宿主机上

具体分为以下两个核心步骤:

1. 在 NFS 服务端创建子目录并授权

NFS 协议本身不会自动为客户端动态创建目录。您需要提前在 NFS 服务端的根目录下规划并创建好各个子目录,并设置好正确的权限(如 chownchmod),以确保容器内的用户有权限读写。

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"

⚠️ 关键注意事项:

  1. 前提条件 :即使使用上述自动化方式,NFS 服务端的子目录也必须提前创建好,否则挂载会失败。
  2. 权限问题 :NFS 默认会将客户端的 root 权限降级为 nobody(即 root_squash)。如果您的容器需要以 root 身份写入数据,必须在 NFS 服务端的 /etc/exports 配置中加上 no_root_squash 参数,或者配置 anonuid/anongid 对齐容器内的用户 UID。
  3. 适用场景 :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 进行一键安装:

    bash 复制代码
    helm 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

总结建议

  • 如果是临时测试 ,使用方案一
  • 如果是固定业务 ,使用方案二
  • 如果是多租户/微服务架构 ,强烈建议使用方案三(动态供应),它能完美契合您之前"为每个容器分配独立子目录"的需求,且极大降低了运维成本。
相关推荐
Patrick_Wilson20 小时前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生1 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
Suroy1 天前
DockerView-Go:用 Go 写一个终端 Docker 监控工具,顺便做了个 Web 仪表盘
docker
云恒要逆袭1 天前
运行你的第一个Docker容器
后端·docker·容器
宋均浩2 天前
# Docker 镜像瘦身实战:从 1.2G 到 80MB 的五个优化步骤
ci/cd·docker
Java之美2 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵3 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
WangMingHua1113 天前
LM Studio Docker 部署——本地大模型一键启动
docker
曲幽4 天前
别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了
python·docker·web·pot·translate·libretranslate·arogstranslate