NFS 是一种非常经典的网络共享存储协议,它允许一台计算机通过网络访问另一台计算机的磁盘,就像访问本地磁盘一样。在容器生态中,NFS 是实现共享存储持久化的常用且成熟的选择。

一、为什么需要持久化?为什么选择 NFS?

  1. 容器的本质问题:容器默认的文件系统是分层临时性的。当容器被删除或重启后,其内部产生的数据(如日志、上传的文件、数据库数据)也会随之消失。
  2. 持久化的目标:将容器内应用程序的重要数据存储在容器生命周期之外的地方,确保数据安全且可独立管理。
  3. NFS 的优势
    • 共享访问:多个 Pod(容器)可以同时以读写模式挂载同一个 NFS 目录,非常适合需要共享数据的分布式应用(如负载均衡后的多个 Web 服务器共享上传目录)。
    • 独立于节点:数据存储在集中的 NFS 服务器上,与运行容器的宿主机节点解耦。即使节点宕机,Pod 被调度到其他节点,依然可以挂载并访问原来的数据。
    • 成熟稳定:NFS 是经过几十年考验的协议,Linux 内核原生支持,非常稳定。
    • 成本较低:可以利用现有的 NAS 存储设备或简单搭建一台 NFS 服务器,无需复杂的分布式存储系统。

二、整体架构

在一个典型的 Kubernetes 集群中使用 NFS 持久化的架构如下:

text

+-------------------------------------------------------+

| Kubernetes Cluster                    |

||

|+------------+     +------------+     +------------+|

||   Pod A    |     |   Pod B    |     |   Pod C    ||

| | (Node 1)   |      | (Node 2)   |     | (Node 3)   ||

|+------------+     +------------+     +------------+|

||  Volume    |     |  Volume    |     |  Volume    ||

| | Mountpoint |      | Mountpoint |      | Mountpoint ||

|+------+-----+     +------+-----+     +------+-----+|

||                   |                   |      |

+---------|-------------------|-------------------|------+

|                   |                   |

| (NFS Protocol over Network)          |

|                                      |

+---------+---------------------------------------+-----+

| External to Cluster                     |

||

|+---------------------+                    |

||     NFS Server      |                    |

||                     |                    |

|| /exported/data/path <---------+          |

|+---------------------+        |          |

||          |

| Physical Disk/NAS   |

+-------------------------------------------------------+

  • NFS Server:独立的服务器(物理机、虚拟机或云上的 NAS 服务),导出(export)一个或多个目录。
  • Kubernetes Nodes:所有工作节点都需要安装 nfs-common 包(包含 mount.nfs 命令),以便能够挂载      NFS 共享。
  • Pod:通过 Volume 配置,将远端的 NFS 目录挂载到容器内的指定路径。

三、操作步骤(以 Kubernetes 为例)

步骤 1:准备 NFS 服务器

假设 NFS 服务器 IP 为 192.168.1.100,要共享的目录是 /data/k8s_nfs

  1. 安装 NFS 服务端 (

    在 NFS Server 上执行):

bash

# Ubuntu/Debian

sudo apt-get update && sudo apt-get install -y nfs-kernel-server

# CentOS/RHEL

sudo yum install -y nfs-utils

  1. 创建共享目录并设置权限

bash

sudo mkdir -p /data/k8s_nfs

sudo chown nobody:nogroup /data/k8s_nfs 根据情况修改所有权,或使用 anonuid/anongid

sudo chmod 777 /data/k8s_nfs 为简单测试,可先放宽权限

  1. 配置导出目录

    编辑 /etc/exports 文件,添加以下行:

text

/data/k8s_nfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

    • /data/k8s_nfs:要共享的目录。
    • 192.168.1.0/24:允许访问的客户端网段(请根据你的集群网络修改)。
    • rw:读写权限。
    • sync:同步写入,更安全。
    • no_subtree_check:提高性能。
    • no_root_squash:允许客户端上的 root 用户保持 root 权限(慎用,有安全风险。生产环境建议使用 all_squash 并映射到特定用户)。
  1. 使配置生效

bash

sudo exportfs -a

sudo systemctl restart nfs-kernel-server

sudo systemctl enable nfs-kernel-server

步骤 2:配置Kubernetes 节点

在所有 Kubernetes Worker 节点上安装 NFS 客户端软件。

bash

# Ubuntu/Debian

sudo apt-get update && sudo apt-get install -y nfs-common

# CentOS/RHEL

sudo yum install -y nfs-utils

步骤 3:在Kubernetes 中创建 PV 和 PVC

Kubernetes 推荐使用 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 来管理存储。

  1. 创建 PersistentVolume (PV)

          PV 是集群中的一块存储,由管理员预先制备。它是对底层存储(如 NFS 共享)的抽象。

nfs-pv.yaml

yaml

apiVersion: v1

kind: PersistentVolume

metadata:

 name: nfs-pv

spec:

 capacity:

 storage: 10Gi  存储容量(NFS本身无限制,这里起声明作用)

 volumeMode: Filesystem

 accessModes:

 - ReadWriteMany  # NFS 支持多节点读写,这是其关键优势

 persistentVolumeReclaimPolicy: Retain  删除PVC后,PV保留(数据手动清理)

 storageClassName: nfs

 nfs:

 path: /data/k8s_nfs  # NFS服务器上的路径

 server: 192.168.1.100 # NFS服务器IP

应用配置:kubectl apply -f nfs-pv.yaml

  1. 创建 PersistentVolumeClaim (PVC)

          PVC 是用户对存储的请求。Pod 通过 PVC 来使用 PV。

nfs-pvc.yaml

yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

 name: nfs-pvc

spec:

 storageClassName: nfs

 accessModes:

 - ReadWriteMany

 resources:

 requests:

storage: 10Gi  请求的存储大小,必须小于等于PV的容量

 # volumeName: nfs-pv # 可选的,可以指定绑定到特定的PV

应用配置:kubectl apply -f nfs-pvc.yaml

使用 kubectl get pv,pvc 查看状态,应显示 Bound

步骤 4:在 Pod 中引用 PVC

在 Pod 的定义中,使用 persistentVolumeClaim 卷类型来挂载存储。

pod-example.yaml

yaml

apiVersion: v1

kind: Pod

metadata:

 name: test-pod

spec:

 containers:

 - name: nginx

 image: nginx

 volumeMounts:

 - name: nfs-storage

mountPath: /usr/share/nginx/html  #容器内的挂载路径

 volumes:

 - name: nfs-storage

 persistentVolumeClaim:

claimName: nfs-pvc  引用之前创建的PVC

应用配置:kubectl apply -f pod-example.yaml

现在,容器内 /usr/share/nginx/html 目录下的任何读写操作,实际上都会发生在 NFS 服务器的 /data/k8s_nfs 目录下。

四、动态 Provisioning(可选)

上述方式是静态制备,需要管理员手动创建 PV。更高级的方式是动态制备,即无需手动创建 PV,当用户创建 PVC 时,自动创建对应的 PV。

这需要部署一个 NFS Subdir External Provisioner(例如 nfs-subdir-external-provisioner Helm Chart)。这个 Provisioner 会监视 PVC 的创建,并自动在指定的 NFS 共享目录下创建以命名空间和 PVC 名称命名的子目录作为新的 PV,极大简化了管理流程。

五、常见问题与注意事项

  1. 性能:NFS 的性能受网络带宽和延迟影响较大,不适合对 I/O 延迟要求极高的应用(如高性能数据库)。考虑使用 SSD 和万兆网络来提升性能。
  2. 安全性
    • 在生产环境中,避免使用 no_root_squash

    • 考虑使用 Kerberos 等认证方式增强 NFS 安全性。

    • 网络隔离,确保 NFS 流量只在 trusted network 中传输。

  3. 单点故障:NFS 服务器本身是一个单点。解决方案是使用高可用的 NAS 设备,或者使用 DRBD 等技术构建高可用 NFS 集群。
  4. 权限问题:容器内进程的用户 UID 必须对 NFS 共享目录有相应的读写权限。这通常需要在运行容器时指定 securityContext.runAsUser 或确保 NFS 服务器端正确配置了 squash 和 UID/GID 映射。

总结

NFS 为容器持久化提供了一个简单、有效且支持多节点共享的解决方案。它非常适合存储应用程序日志、用户上传的内容、配置文件等需要跨容器实例共享或持久化的数据。虽然它在性能和单点故障方面存在一些挑战,但对于大多数中小型应用和非极端性能要求的场景,NFS 仍然是一个极具性价比和实用性的选择。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐