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中的数据缓存到本地,实现对文件访问速度的提升。其工作原理如下:
您可以通过定义Dataset CRD资源对象和EFCRuntime CRD资源对象,分别指定NAS或CPFS的数据源信息。
Fluid Controllers组件根据数据源信息,在集群中创建EFC Cache Worker组件和EFC FUSE组件。
用户创建应用Pod时,可以通过指定PVC(PersistentVolumeClaim)的方式挂载到EFC FUSE客户端暴露的文件系统挂载点。
当用户访问文件时,EFC FUSE客户端会将请求转发给EFC Cache Worker,后者会判断数据是否已经被缓存到本地,如果有缓存就直接返回;否则从NAS或CPFS中获取数据,并缓存到本地,即通过EFC缓存访问NAS或CPFS文件存储中的数据,获得缓存加速效果。
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套件
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择 。
在云原生AI套件页面,单击ack-fluid右侧操作列下的部署。
在安装组件对话框,单击确定。
ack-fluid组件版本较低
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择 。
在云原生AI套件页面,单击ack-fluid右侧操作列下的升级。
在升级组件对话框,单击确定。
步骤二:在NAS或CPFS中创建待访问的数据
NAS和CPFS都是存储大规模数据的技术,它们具有不同的技术架构和数据管理方式,您可以根据具体场景来选择合适的技术方案。
在NAS中创建待访问的数据
如果您的NAS中已有合适的数据文件,可以跳过此步骤。
挂载NAS文件系统至任意一台ECS。具体操作,请参见一键挂载NAS NFS协议文件系统。
执行以下命令,查找文件挂载点。
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
执行以下命令,在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中已有合适的数据文件,可以跳过此步骤。
挂载CPFS文件系统至任意一台ECS。具体操作,请参见CPFS-NFS客户端挂载文件系统(推荐)。
执行以下命令,查找文件挂载点。
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
执行以下命令,在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
创建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>
,字段解析如下。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组件副本数。可以根据计算节点内存配置和数据集大小进行调整。建议quota和replicas乘积大于所需缓存的数据集总大小。
networkMode
取值ContainerNetwork和HostNetwork。在ACK环境中建议设置为ContainerNetwork,使用容器网络不会有额外的性能损失。
mediumtype
缓存类型。只支持HDD、SSD和MEM。其中MEM代表内存。在AI训练场景中,建议使用MEM。当使用MEM时,path所指定的缓存数据存储目录需要设置为内存文件系统(例如tmpfs)。
path
EFC缓存系统Worker的缓存数据存储目录。建议使用/dev/shm。
quota
单个Worker组件提供的最大缓存容量。可以根据计算节点内存配置和数据集大小进行调整。建议quota和replicas乘积大于所需缓存的数据集总大小。
执行以下命令,创建EFCRuntime和Dataset。
kubectl create -f dataset.yaml
执行以下命令,查看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中定义的数据。
执行以下命令,查看EFCRuntime的部署情况。
kubectl get efcruntime
预期输出:
NAME MASTER PHASE WORKER PHASE FUSE PHASE AGE efc-demo Ready Ready Ready 27m
预期输出表明EFC缓存系统的Master组件、Worker组件以及FUSE组件都处于就绪状态。
执行以下命令,查看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的加速效果。
创建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
执行以下命令,创建名为efc-app的StatefulSet资源对象。
kubectl create -f app.yaml
执行以下命令,查看文件大小。
kubectl exec -it efc-app-0 -- du -h /data/allzero-demo
预期输出:
10G /data/allzero-demo
查看应用容器文件的读取时间。
说明读取文件耗时和吞吐量可能随运行环境和测量方式而变化。本文是在一个有着三个ECS节点且节点实例规格均为ecs.g7ne.8xlarge的ACK集群中获取的数据结果。其中,efc-demo的3个分布式缓存Worker Pod均运行于一个ECS节点上,并且efc-app StatefulSet的两个Pod分别运行在另外两个不同的节点上。数据结果不受FUSE端kernel cache影响。
执行以下命令,查看第一个应用容器中文件的读取时间。
说明如果您使用自己的真实数据文件,您需要使用真实文件路径替代以下命令中的/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。
执行以下命令,在另一应用容器中,读取相同的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倍读取效率。