使用EFC加速NAS或CPFS文件访问

Fluid是一个开源的Kubernetes原生的分布式数据集编排和加速引擎,主要服务于云原生场景下的数据密集型应用。Fluid支持管理和调度EFCRuntime,实现数据集的可见性、弹性伸缩和数据访问加速等能力。本文介绍如何使用Fluid EFCRuntime加速NAS或CPFS文件访问。

前提条件

  • 已确保ECS的操作系统为Alibaba Cloud Linux 2,内核版本为v4.19.91-23及以上版本。

  • 已创建ACK Pro版集群,且集群的Kubernetes版本不低于1.18。具体操作,请参见创建ACK Pro版集群

  • 已开通阿里云文件存储NAS或CPFS服务,并在已创建的ACK Pro集群节点可正常挂载访问的通用容量型、通用性能型NAS或CPFS实例。具体操作,请参见NAS入门概述CPFS入门概述

    说明

    对于AI训练场景,建议您根据吞吐性能选择文件存储规格。更多信息,请参见选型指导

  • 已配置kubectl,并可以正常连接ACK Pro版集群。具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群

EFC介绍

EFC弹性文件客户端(Elastic File Client)是阿里云文件存储团队开发的基于FUSE的用户态POSIX客户端。它可以替代传统的内核态NFS客户端,提供多链接访问、元数据缓存、分布式数据缓存等加速能力,并结合阿里云Prometheus监控提供端侧性能监控能力。相比于内核态NFS v3、v4.x客户端和其他基于开源FUSE的客户端,EFC有以下优势:

  • 强一致的语义:EFC通过强一致的分布式锁机制实现文件和目录的强一致;文件写入可以立即被其他客户端读取;新文件创建出来后,就可以立即让所有的其他客户端同步访问到,便于您在多节点间管理数据。

  • 端上单机读写缓存能力:优化了FUSE的缓存逻辑,利用计算节点上的少量内存,提供了更好的小文件读写性能。相比于传统的NFS客户端,性能提升了50%以上。

  • 分布式只读缓存能力:除了端上读写缓存,EFC提供分布式只读缓存的能力,利用多节点的内存建立缓存池,并随计算规模免运维自动扩展。

  • 小文件预取能力:EFC会针对性地预取热目录下的热数据,节省拉取数据的开销。

  • 热升级和Failover能力:EFC支持秒级Failover特性,实现了对业务无影响的客户端版本的热升级能力。

使用Fluid EFCRuntime加速NAS或CPFS文件访问

Fluid通过EFCRuntime的Kubernetes自定义资源对接EFC,帮助用户实现数据集的可见性、弹性伸缩等能力。

使用限制

EFCRuntime具有以下限制:

  • 暂不支持DataLoad缓存预热功能,只支持第一次读取数据时放入缓存的功能。

  • 暂不支持Dataset资源对象上的缓存状态信息透出。

  • 仅支持在华北3(张家口)、华北2(北京)、华南3(广州)、华南1(深圳)、华东2(上海)地域使用。

原理介绍

如下图所示,通过将NAS或CPFS中的数据缓存到本地,实现对文件访问速度的提升。其工作原理如下:

  1. 您可以通过定义Dataset CRD资源对象和EFCRuntime CRD资源对象,分别指定NAS或CPFS的数据源信息。

  2. Fluid Controllers组件根据数据源信息,在集群中创建EFC Cache Worker组件和EFC FUSE组件。

  3. 用户创建应用Pod时,可以通过指定PVC(PersistentVolumeClaim)的方式挂载到EFC FUSE客户端暴露的文件系统挂载点。

  4. 当用户访问文件时,EFC FUSE客户端会将请求转发给EFC Cache Worker,后者会判断数据是否已经被缓存到本地,如果有缓存就直接返回;否则从NAS或CPFS中获取数据,并缓存到本地,即通过EFC缓存访问NAS或CPFS文件存储中的数据,获得缓存加速效果。

image
  • Dataset:由Fluid定义的一种Kubernetes CRD。它描述了逻辑上相关的一组数据的集合,会被上层运算引擎使用。

  • EFCRuntime:支撑Dataset访问加速能力的一种Runtime类型实现,其背后使用的缓存引擎为EFC。EFC缓存引擎包括EFC Cache Worker组件和EFC FUSE组件。

    • EFC Cache Worker组件:通过一致性哈希提供缓存能力的服务端组件,根据不同的数据访问需求可选择关闭该组件。关闭该组件后,EFC将关闭分布式只读缓存功能。其他特性不受影响。

    • EFC FUSE组件:以POSIX协议标准暴露数据访问接口的EFC客户端组件。

操作流程

步骤一:安装ack-fluid组件

安装云原生AI套件和ack-fluid组件,且确保组件为0.9.10及以上版本。

重要

若您已安装开源Fluid,请卸载后再部署ack-fluid组件。

未安装云原生AI套件

您可以在安装云原生AI套件时开启Fluid数据加速。具体操作,请参见安装云原生AI套件

已安装云原生AI套件

  1. 登录容器服务管理控制台,在左侧导航栏选择集群

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择应用 > 云原生AI套件

  3. 云原生AI套件页面,单击ack-fluid右侧操作列下的部署

  4. 安装组件对话框,单击确定

ack-fluid组件版本较低

  1. 登录容器服务管理控制台,在左侧导航栏选择集群

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择应用 > 云原生AI套件

  3. 云原生AI套件页面,单击ack-fluid右侧操作列下的升级

  4. 升级组件对话框,单击确定

步骤二:在NAS或CPFS中创建待访问的数据

NAS和CPFS都是存储大规模数据的技术,它们具有不同的技术架构和数据管理方式,您可以根据具体场景来选择合适的技术方案。

在NAS中创建待访问的数据

说明

如果您的NAS中已有合适的数据文件,可以跳过此步骤。

  1. 挂载NAS文件系统至任意一台ECS。具体操作,请参见一键挂载NAS NFS协议文件系统

  2. 执行以下命令,查找文件挂载点。

    findmnt /mnt

    预期输出:

    TARGET   SOURCE                                         FSTYPE OPTIONS
    /mnt/nfs xxxxxxxxxxx-xxxxx.cn-beijing.nas.aliyuncs.com:/ nfs    rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,nolock,no
  3. 执行以下命令,在NAS文件存储挂载点目录下创建一个大小为10G的文件。

    dd if=/dev/zero of=/mnt/nfs/allzero-demo count=1024 bs=10M

    预期输出:

    1024+0 records in
    1024+0 records out
    10737418240 bytes (11 GB) copied, 50.9437 s, 211 MB/s

在CPFS中创建待访问的数据

说明

如果您的CPFS中已有合适的数据文件,可以跳过此步骤。

  1. 挂载CPFS文件系统至任意一台ECS。具体操作,请参见CPFS-NFS客户端挂载文件系统(推荐)

  2. 执行以下命令,查找文件挂载点。

    findmnt /mnt/cpfs

    预期输出:

    TARGET   SOURCE                                         FSTYPE OPTIONS
    /mnt/cpfs xxxxxxxxxxx-xxxxx.cn-beijing.cpfs.aliyuncs.com:/ nfs    rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,nolock,noresvport,proto=tcp,port=30000,timeo=600,retrans=2,sec=sys

  3. 执行以下命令,在CPFS文件存储挂载点目录下创建一个大小为10G的文件。

    dd if=/dev/zero of=/mnt/cpfs/allzero-demo count=1024 bs=10M

    预期输出:

    1024+0 records in
    1024+0 records out
    10737418240 bytes (11 GB) copied, 50.9437 s, 211 MB/s

步骤三:创建Fluid Dataset和EFCRuntime

  1. 创建dataset.yaml文件,代码示例如下。

    NAS代码示例

    apiVersion: data.fluid.io/v1alpha1
    kind: Dataset
    metadata:
      name: efc-demo
    spec:
      mounts:
        - mountPoint: "nfs://<nas_url>:<nas_dir>"
          name: efc
          path: "/"
    ---
    apiVersion: data.fluid.io/v1alpha1
    kind: EFCRuntime
    metadata:
      name: efc-demo
    spec:
      replicas: 3
      master:
        networkMode: ContainerNetwork
      worker:
        networkMode: ContainerNetwork
      fuse:
        networkMode: ContainerNetwork
      tieredstore:
        levels:
          - mediumtype: MEM
            path: /dev/shm
            quota: 15Gi

    CPFS代码示例

    apiVersion: data.fluid.io/v1alpha1
    kind: Dataset
    metadata:
      name: efc-demo
    spec:
      mounts:
        - mountPoint: "cpfs://file-system-id.region.cpfs.aliyuncs.com:/share/path"
          name: efc
          path: "/mnt"
    ---
    apiVersion: data.fluid.io/v1alpha1
    kind: EFCRuntime
    metadata:
      name: efc-demo
    spec:
      replicas: 3
      master:
        networkMode: ContainerNetwork
      worker:
        networkMode: ContainerNetwork
      fuse:
        networkMode: ContainerNetwork
      tieredstore:
        levels:
          - mediumtype: MEM
            path: /dev/shm
            quota: 15Gi

    dataset.yaml文件中包含Dataset和EFCRuntime。

    • Dataset:描述NAS或CPFS文件存储的相关信息,例如NAS或CPFS文件存储URL,需挂载的子目录等。

    • EFCRuntime:启动一个EFC缓存系统来提供缓存服务。在EFCRuntime中描述EFC缓存系统Worker组件副本数,以及每个Worker组件最大的缓存容量等。

    参数

    说明

    mountPoint

    • NAS的格式为nfs://<nas_url>:<nas_dir>,字段解析如下。

      • nas_url:挂载NAS文件系统的挂载地址。

        登录NAS控制台,在左侧导航栏选择文件系统>文件系统列表。在文件系统列表页面,单击目标文件系统右侧的管理,单击挂载使用,获取挂载地址。更多信息,请参见管理挂载点

      • nas_dir:所需挂载的子路径。如果无特殊要求,可以使用根目录。例如efc://xxxxxxxxxxx-xxxxx.cn-beijing.nas.aliyuncs.com:/表示挂载一个NAS存储系统的根目录。

    • CPFS的格式为cpfs://file-system-id.region.cpfs.aliyuncs.com:/share/path,字段解析如下。

      • cpfs://为前缀,表示文件系统为CPFS。

      • file-system-id.region.cpfs.aliyuncs.com:/share/path表示<挂载地址>:<CPFS文件系统目录> <当前服务器上待挂载的本地路径>,请根据实际情况替换。更多信息,请参考步骤二:使用CPFS-NFS客户端挂载文件系统

    replicas

    创建EFC缓存系统的Worker组件副本数。可以根据计算节点内存配置和数据集大小进行调整。建议quotareplicas乘积大于所需缓存的数据集总大小。

    networkMode

    取值ContainerNetworkHostNetwork。在ACK环境中建议设置为ContainerNetwork,使用容器网络不会有额外的性能损失。

    mediumtype

    缓存类型。只支持HDD、SSD和MEM。其中MEM代表内存。在AI训练场景中,建议使用MEM。当使用MEM时,path所指定的缓存数据存储目录需要设置为内存文件系统(例如tmpfs)。

    path

    EFC缓存系统Worker的缓存数据存储目录。建议使用/dev/shm

    quota

    单个Worker组件提供的最大缓存容量。可以根据计算节点内存配置和数据集大小进行调整。建议quotareplicas乘积大于所需缓存的数据集总大小。

  2. 执行以下命令,创建EFCRuntime和Dataset。

    kubectl create -f dataset.yaml
  3. 执行以下命令,查看Dataset的部署情况。

    kubectl get dataset efc-demo

    预期输出:

    NAME       UFS TOTAL SIZE   CACHED   CACHE CAPACITY   CACHED PERCENTAGE   PHASE   AGE
    efc-demo                                                                  Bound   24m

    Dataset处于Bound状态,表明EFC缓存系统已在集群内正常启动,应用Pod可正常访问Dataset中定义的数据。

  4. 执行以下命令,查看EFCRuntime的部署情况。

    kubectl get efcruntime

    预期输出:

    NAME       MASTER PHASE   WORKER PHASE   FUSE PHASE   AGE
    efc-demo   Ready          Ready          Ready        27m

    预期输出表明EFC缓存系统的Master组件、Worker组件以及FUSE组件都处于就绪状态。

  5. 执行以下命令,查看PVC和PV的创建情况。

    在Dataset以及对应的EFC缓存系统准备就绪后,Fluid将会自动创建PVC和PV。

    kubectl get pv,pvc

    预期输出:

    NAME                                CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
    persistentvolume/default-efc-demo   100Gi      ROX            Retain           Bound    default/efc-demo         fluid                   94m
    
    NAME                                   STATUS   VOLUME             CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    persistentvolumeclaim/efc-demo         Bound    default-efc-demo   100Gi      ROX            fluid          94m

步骤四:创建应用容器访问数据

您可以通过创建应用容器来使用EFC加速服务。下述示例将创建两个应用容器,在两个不同的节点上多次访问同一存储于NAS或CPFS文件系统中的数据,通过比较访问时间来展示EFCRuntime的加速效果。

  1. 创建app.yaml,代码示例如下。

    以下内容描述了一个名为efc-app的StatefulSet资源对象,它包含两个Pod,每个Pod都将使用Fluid自动创建的efc-demo PVC挂载到容器内的/data目录。

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: efc-app
      labels:
        app: nginx
    spec:
      serviceName: nginx
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: registry.openanolis.cn/openanolis/nginx:1.14.1-8.6
            command: ["/bin/bash"]
            args: ["-c", "sleep inf"]
            volumeMounts:
            - mountPath: "/data"
              name: data-vol
          volumes:
            - name: data-vol
              persistentVolumeClaim:
                claimName: efc-demo
  2. 执行以下命令,创建名为efc-app的StatefulSet资源对象。

    kubectl create -f app.yaml
  3. 执行以下命令,查看文件大小。

    kubectl exec -it efc-app-0 -- du -h /data/allzero-demo

    预期输出:

    10G     /data/allzero-demo
  4. 查看应用容器文件的读取时间。

    说明

    读取文件耗时和吞吐量可能随运行环境和测量方式而变化。本文是在一个有着三个ECS节点且节点实例规格均为ecs.g7ne.8xlarge的ACK集群中获取的数据结果。其中,efc-demo的3个分布式缓存Worker Pod均运行于一个ECS节点上,并且efc-app StatefulSet的两个Pod分别运行在另外两个不同的节点上。数据结果不受FUSE端kernel cache影响。

    1. 执行以下命令,查看第一个应用容器中文件的读取时间。

      说明

      如果您使用自己的真实数据文件,您需要使用真实文件路径替代以下命令中的/data/allzero-demo

      kubectl exec -it efc-app-0 -- bash -c "time cat /data/allzero-demo > /dev/null"

      预期输出:

      real    0m15.792s
      user    0m0.023s
      sys     0m2.404s

      预期输出表明读取10G文件需要15.792s,读取速度为648 MiB/s。

    2. 执行以下命令,在另一应用容器中,读取相同的10G大小文件的耗时。

      说明

      如果您使用自己的真实数据文件,您需要使用真实文件路径替代以下命令中的/data/allzero-demo

      kubectl exec -it efc-app-1 -- bash -c "time cat /data/allzero-demo > /dev/null"

      预期输出:

      real    0m9.970s
      user    0m0.012s
      sys     0m2.283s

      预期输出表明读取10G文件需要9.970s,读取速度为1034.3 MiB/s。

    可以看到,没有使用缓存时,数据读取速度为648 MiB/s;使用EFCRuntime缓存后,数据读取速度为1034.3 MiB/s。对于相同文件,EFCRuntime提升了约2倍读取效率。