4.11. 使用 NFS 的持久性存储

OpenShift Container Platform 集群可以使用 NFS 来置备持久性存储。持久性卷 (PV) 和持久性卷声明 (PVC) 提供了在项目间共享卷的方法。虽然 PV 定义中包含的与 NFS 相关的信息也可以直接在 Pod 中定义,但是这样做不会使创建的卷作为一个特定的集群资源,从而可能会导致卷冲突。

4.11.1. 置备

当存储可以被挂载为 OpenShift Container Platform 中的卷之前,它必须已存在于底层的存储系统中。要置备 NFS 卷,则需要一个 NFS 服务器和导出路径列表。

流程

  1. 为 PV 创建对象定义:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv0001 1
    spec:
      capacity:
        storage: 5Gi 2
      accessModes:
      - ReadWriteOnce 3
      nfs: 4
        path: /tmp 5
        server: 172.17.0.2 6
      persistentVolumeReclaimPolicy: Retain 7
    1
    卷的名称。这是各个 oc <command> pod 命令中的 PV 标识。
    2
    为这个卷分配的存储量。
    3
    虽然这看上去象是设置对卷的访问控制,但它实际上被用作标签并用来将 PVC 与 PV 匹配。当前,还不能基于 accessModes 强制访问规则。
    4
    使用的卷类型,在这个示例里是 nfs 插件。
    5
    NFS 服务器导出的路径。
    6
    NFS 服务器的主机名或 IP 地址。
    7
    PV 的 reclaim 策略。它决定了在卷被释放后会发生什么。
    注意

    每个 NFS 卷都必须由集群中的所有可调度节点挂载。

  2. 确定创建了 PV:

    $ oc get pv

    输出示例

    NAME     LABELS    CAPACITY     ACCESSMODES   STATUS      CLAIM  REASON    AGE
    pv0001   <none>    5Gi          RWO           Available                    31s

  3. 创建绑定至新 PV 的持久性卷声明:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: nfs-claim1
    spec:
      accessModes:
        - ReadWriteOnce 1
      resources:
        requests:
          storage: 5Gi 2
      volumeName: pv0001
      storageClassName: ""
    1
    访问模式不强制实施安全性,而是作为标签来将 PV 与 PVC 匹配。
    2
    此声明会寻找提供 5Gi 或更高容量的 PV。
  4. 确认创建了持久卷声明:

    $ oc get pvc

    输出示例

    NAME         STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    nfs-claim1   Bound    pv0001   5Gi        RWO                           2m

4.11.2. 强制磁盘配额

使用磁盘分区强制磁盘配额和大小限制。每个分区都可以有自己的导出。每个导出都是一个 PV。OpenShift Container Platform 会保证每个 PV 都使用不同的名称,但 NFS 卷服务器和路径的唯一性是由管理员实现的。

采用这种方法强制配额可让软件开发人员以特定数量(如 10Gi)请求持久性存储,并可与相等或更大容量的卷进行匹配。

4.11.3. NFS 卷安全

这部分论述了 NFS 卷安全性,其中包括匹配的权限和 SELinux 考虑。用户需要了解 POSIX 权限、进程 UID 、supplemental 组和 SELinux 的基本知识。

软件开发人员可以使用 PVC 名称,或直接在 Pod 定义中 volumes 部分使用 NFS 插件来请求 NFS 存储。

NFS 服务器中的 /etc/exports 文件包含可访问的 NFS 目录。目标 NFS 目录有 POSIX 拥有者和组群 ID。OpenShift Container Platform NFS 插件使用相同的 POSIX 所有者权限及在导出的 NFS 目录中找到的权限挂载容器的 NFS 目录。然而,容器实际运行时所使用的 UID 与 NFS 挂载的所有者的 UID 不同。这是所需的行为。

例如,目标 NFS 目录在 NFS 服务器中,如下所示:

$ ls -lZ /opt/nfs -d

输出示例

drwxrws---. nfsnobody 5555 unconfined_u:object_r:usr_t:s0   /opt/nfs

$ id nfsnobody

输出示例

uid=65534(nfsnobody) gid=65534(nfsnobody) groups=65534(nfsnobody)

为了可以访问目录,容器必须匹配 SELinux 标签,并使用 UID65534nfsnobody的所有者,或其 supplemental 组的 5555 运行。

注意

所有者 ID 65534 只是一个示例。虽然 NFS 的 root_squashroot,uid 0 映射到 nfsnobody,uid 65534,但 NFS 导出的所有者 ID 可能是任意值。NFS 导出的所有者不需要是 65534

4.11.3.1. 组 ID

用来控制 NFS 访问(假设不能在 NFS 导出中修改权限)的建议方法是使用附加组(supplemental group)。OpenShift Container Platform 中的附件组的功能是用于共享存储(NFS 是一个共享存储)。相对块存储(如 iSCSI),使用 fsGroup SCC 策略和在 Pod 的 securityContext 中的fsGroup 值。

注意

在访问持久性存储时,一般情况下最好使用 supplemental 组 ID 而不是使用用户 ID。

示例中目标 NFS 目录上的组 ID 是 5555, Pod 可以使用 Pod 的 securityContext定义中的 supplementalGroups 来设置组 ID。例如:

spec:
  containers:
    - name:
    ...
  securityContext: 1
    supplementalGroups: [5555] 2
1
securityContext 必须在 pod 一级定义,而不是在某个特定容器中定义。
2
为 pod 定义的 GID 数组。在这种情况下,是阵列中的一个元素。使用逗号将不同 GID 分开。

假设没有可能满足 pod 要求的自定义 SCC,pod 可能与受限 SCC 匹配。这个 SCC 把 supplementalGroups 策略设置为 RunAsAny。这代表提供的任何组群 ID 都被接受,且不进行范围检查。

因此,上面的 pod 可以通过,并被启动。但是,如果需要进行组 ID 范围检查,使用自定义 SCC 就是首选的解决方案。可创建一个定义了最小和最大组群 ID 的自定义 SCC,这样就会强制进行组 ID 范围检查,组 ID 5555 将被允许 。

注意

要使用自定义 SCC,需要首先将其添加到适当的服务帐户(service account)中。例如,在一个特定项目中使用 default 服务账户(除非在 Pod 规格中指定了另外一个账户)。

4.11.3.2. 用户 ID

用户 ID 可以在容器镜像或者 Pod 定义中定义。

注意

通常情况下,最好使用附件组群 ID 而不是用户 ID 来获得对持久性存储的访问。

在上面显示的目标 NFS 目录示例中,容器需要将其 UID 设定为 65534,忽略组 ID。因此可以把以下内容添加到 Pod 定义中:

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

假设项目为 default 且 SCC 为 restricted,则不允许 pod 请求的用户 ID 65534。因此,pod 会因以下原因失败:

  • 它要求 65534 作为其用户 ID。
  • Pod 可用的所有 SCC 被检查以决定哪些 SCC 允许 ID 为 65534 的用户。虽然检查了 SCC 的所有策略,但这里的焦点是用户 ID。
  • 因为所有可用的 SCC 都使用 MustRunAsRange 作为其 runAsUser 策略,所以需要进行 UID 范围检查。
  • 65534 不包含在 SCC 或项目的用户 ID 范围内。

一般情况下,作为一个最佳实践方案,最好不要修改预定义的 SCC。解决这个问题的的首选方法是,创建一个自定义 SCC,在其中定义最小和最大用户 ID。 UID 范围仍然会被强制检查,UID 65534 会被允许。

注意

要使用自定义 SCC,需要首先将其添加到适当的服务帐户(service account)中。例如,在一个特定项目中使用 default 服务账户(除非在 Pod 规格中指定了另外一个账户)。

4.11.3.3. SELinux

Red Hat Enterprise Linux(RHEL)和 Red Hat Enterprise Linux CoreOS(RHCOS)系统被配置为默认在远程 NFS 服务器中使用 SELinux。

对于非 RHEL 和非 RHCOS 系统,SELinux 不允许从 pod 写入远程 NFS 服务器。NFS 卷正确挂载,但只读。您将需要按照以下步骤启用正确的 SELinux 权限。

先决条件

  • 必须安装 container-selinux 软件包。这个软件包提供 virt_use_nfs SELinux 布尔值。

流程

  • 使用以下命令启用 virt_use_nfs 布尔值。使用 -P 选项可以使这个布尔值在系统重启后仍然有效。

    # setsebool -P virt_use_nfs 1

4.11.3.4. 导出设置

为了使任意容器用户都可以读取和写入卷, NFS 服务器中的每个导出的卷都应该满足以下条件:

  • 每个导出必须使用以下格式导出:

    /<example_fs> *(rw,root_squash)
  • 必须将防火墙配置为允许到挂载点的流量。

    • 对于 NFSv4,配置默认端口 2049nfs)。

      NFSv4

      # iptables -I INPUT 1 -p tcp --dport 2049 -j ACCEPT

    • 对于 NFSv3,需要配置三个端口: 2049nfs)、 20048mountd)和 111portmapper)。

      NFSv3

      # 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 拥有,或使用 supplementalGroups 来允许 pod 组进行访问(如上面的与组 ID 相关的章节所示)。

4.11.4. 重新声明资源

NFS 实现了 OpenShift Container Platform Recyclable 插件接口。自动进程根据在每个持久性卷上设定的策略处理重新声明的任务。

默认情况下,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 会导致错误并可能造成数据丢失。

4.11.5. 其他配置和故障排除

根据所使用的 NFS 版本以及配置,可能还需要额外的配置步骤来进行正确的导出和安全映射。以下是一些可能适用的信息:

NFSv4 挂载错误地显示所有文件的所有者为 nobody:nobody

  • 可归因于 NFS 中的 /etc/idmapd.conf 中的 ID 映射设置。
  • 请参考红帽解决方案

在 NFSv4 上禁用 ID 映射

  • 在 NFS 客户端和服务器中运行:

    # echo 'Y' > /sys/module/nfsd/parameters/nfs4_disable_idmapping