使用 NFS 配置持久存储

集群支持使用 NFS 的持久存储。PersistentVolumes (PVs) 和 PersistentVolumeClaims (PVCs) 提供一个抽象层,用于在项目内创建和使用存储卷。虽然可以将 NFS 配置细节直接嵌入 Pod 定义中,但这种方式不会将该卷创建为一个独立、隔离的集群资源,从而增加冲突风险。

前提条件

  • 中将存储挂载为卷之前,底层基础设施中必须已存在该存储。
  • 要创建 NFS 卷,只需要一份 NFS 服务器和导出路径列表。

操作步骤

为 PV 创建对象定义

cat << EOF | kubectl create -f -
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-example
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  nfs:
    path: /tmp
    server: 10.0.0.3
  persistentVolumeReclaimPolicy: Retain
EOF
  1. 卷名称。
  2. 存储量。
  3. 虽然这看起来与控制对卷的访问有关,但实际上它的作用类似于标签,用于将 PVC 与 PV 匹配。目前不会基于 accessModes 强制执行任何访问规则。
  4. 正在使用的卷类型,在本例中为 nfs 插件。
  5. NFS 服务器地址。
  6. NFS 导出路径。
  7. PVC 被删除后会发生什么(Retain、Delete、Recycle)。

验证 PV 已创建

命令
输出示例
kubectl get pv

创建一个引用该 PV 的 PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-claim1
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  volumeName: pv-nfs-example
  storageClassName: ""
  1. 访问模式不会强制执行安全性,而是充当标签,用于将 PV 与 PVC 匹配。
  2. 该声明会查找提供 1Gi 或更大容量的 PV。
  3. 要使用的 PV 名称。

验证已创建 persistent volume claim

命令
输出示例
kubectl get pvc

通过分区导出强制执行磁盘配额

要强制执行磁盘配额和大小限制,可以使用磁盘分区。将每个分区指定为专用导出点,每个导出点对应一个独立的 PersistentVolume (PV)。

虽然 要求 PV 名称唯一,但管理员还必须确保每个导出的 NFS 服务器和路径组合都唯一。

这种分区方式可实现精确的容量管理。开发人员通过指定所需容量(例如 10Gi)来申请持久存储,而 会将该请求匹配到一个由分区或导出提供至少该容量的 PV。配额强制适用于分配的分区或导出中可用的存储空间。

NFS 卷安全性

NFS 卷安全性取决于权限匹配。在配置用于共享的 NFS 卷之前,请检查 POSIX 权限、进程 UID 和 supplemental groups。

开发人员可以通过以下任一方式请求 NFS 存储:

  • 按名称引用 PersistentVolumeClaim (PVC),或
  • 在其 Pod 规范的 volumes 部分直接配置 NFS 卷插件。

在 NFS 服务器上,/etc/exports 文件定义可访问目录的导出规则。每个已导出的目录都会保留其原生 POSIX owner/group IDs。

NFS 插件的关键行为:

  1. 在将卷挂载到容器时保留源目录中的精确 POSIX 所有权和权限
  2. 运行容器时不会强制进程 UID 与挂载所有权匹配——这是有意为之的安全措施

例如,考虑一个具有以下服务器端属性的 NFS 目录:

命令
输出示例
ls -l /share/nfs -d
命令
输出示例
id nfsnobody

因此,容器必须使用 UID 65534(nfsnobody 所有者)运行,或者在其 supplemental groups 中包含 5555,才能访问该目录。

NOTE

注意 65534 的所有者 ID 仅用作示例。尽管 NFS 的 root_squash 会将 root、uid 0 映射为 nfsnobody,也就是 uid 65534,但 NFS 导出可以使用任意所有者 ID。NFS 导出并不要求所有者必须是 65534。

组 ID

NFS 访问管理建议(当导出权限已固定时) 如果无法修改 NFS 导出的权限,建议通过 supplemental groups 管理访问。

中的 supplemental groups 是控制共享文件存储(如 NFS)访问的一种常见机制。

与块存储相比:对块存储卷(例如 iSCSI)的访问通常通过在 Pod 的 securityContext 中设置 fsGroup 值来管理。此方法利用挂载时文件系统组所有权的变更。

NOTE

要获得对持久存储的访问权限,通常优先使用 supplemental group ID,而不是 user ID。

由于示例目标 NFS 目录的 group ID 为 5555,Pod 可以在 Pod 的 securityContext 定义中使用 supplementalGroups 来定义该 group ID。例如:

spec:
  containers:
    - name:
    ...
  securityContext:
    supplementalGroups: [5555] 
  1. securityContext 必须在 pod 级别定义,而不是在某个特定容器下定义。
  2. 为 Pod 定义的 GID 数组。在这种情况下,数组中有一个元素。其他 GID 应以逗号分隔。

用户 ID

用户 ID 可以在容器镜像中定义,也可以在 Pod 定义中定义。

NOTE

通常优先使用 supplemental group ID 来获得对持久存储的访问权限,而不是使用 user ID。

在上面所示的目标 NFS 目录示例中,容器需要将其 UID 设置为 65534,暂不考虑 group ID,因此可以将以下内容添加到 Pod 定义中:

spec:
  containers:
  - name:
  ...
    securityContext:
      runAsUser: 65534
  1. Pod 包含一个针对每个容器的 securityContext 定义,以及一个适用于 Pod 中所有容器的 pod securityContext。
  2. 65534 是 nfsnobody 用户。

导出设置

要允许任意容器用户读写该卷,NFS 服务器上的每个已导出卷都应符合以下条件:

  • 每个导出项都必须按以下格式导出:

    # replace 10.0.0.0/24 to trusted CIDRs/hosts
    /<example_fs> 10.0.0.0/24(rw,sync,root_squash,no_subtree_check)
  • 必须配置防火墙以允许到挂载点的流量。

    • 对于 NFSv4,请配置默认端口 2049(nfs)。
      iptables -I INPUT 1 -p tcp --dport 2049 -j ACCEPT
    • 对于 NFSv3,需要配置三个端口:2049(nfs)、20048(mountd)和 111(portmapper)。
      iptables -I INPUT 1 -p tcp --dport 2049 -j ACCEPT
      iptables -I INPUT 1 -p tcp --dport 20048 -j ACCEPT
      iptables -I INPUT 1 -p tcp --dport 111 -j ACCEPT
  • 必须设置 NFS 导出和目录,使目标 Pod 可以访问它们。可以将导出设置为由容器的主 UID 拥有,或者按照上面的组 ID 示例,通过 supplementalGroups 为 Pod 提供组访问权限。

回收资源

NFS 实现了 可回收插件接口。自动流程会根据每个 PV 上设置的策略处理回收任务。

默认情况下,PV 设置为 Retain。

一旦某个 PVC 的声明被删除且 PV 被释放,就不应重复使用该 PV 对象。应使用与原始卷相同的基本卷详情创建一个新的 PV。

例如,管理员创建一个名为 nfs1 的 PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs1
spec:
  capacity:
    storage: 1Mi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.1
    path: "/"

用户创建 PVC1,它会绑定到 nfs1。然后用户删除 PVC1,释放对 nfs1 的声明。这会使 nfs1 变为 Released。如果管理员希望提供同一个 NFS 共享,应创建一个具有相同 NFS 服务器详细信息但不同 PV 名称的新 PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs2
spec:
  capacity:
    storage: 1Mi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.1
    path: "/"

不建议删除原始 PV 后再使用相同名称重新创建。尝试手动将 PV 的状态从 Released 更改为 Available 会导致错误并可能造成数据丢失。