作者简介:
吕冬冬,云知声超算平台架构师,负责大规模分布式机器学习平台架构设计与性能研发,负责深度学习算法利用的优化与 AI 模型减速。钻研畛域包含高性能计算、分布式文件存储、分布式缓存等。
朱唯唯,Juicedata 全栈工程师,负责 JuiceFS CSI Driver 的开发和保护,负责 JuiceFS 在云原生畛域的倒退。
云知声 Atlas 团队在 2021 年初开始接触并跟进 JuiceFS 存储,并且在晚期曾经积攒了丰盛的 Fluid 应用教训。近期,云知声团队与 Juicedata 团队合作开发了 Fluid JuiceFS 减速引擎,使用户可能更好地在 Kubernetes 环境中应用 JuiceFS 缓存治理能力。本篇文章解说如何在 Kubernetes 集群中玩转 Fluid + JuiceFS。
背景介绍
Fluid 简介
CNCF Fluid 是一个开源的 Kubernetes 原生的分布式数据集编排和减速引擎,次要服务于云原生场景下的数据密集型利用,例如大数据利用、AI 利用等,对于 Fluid 更多信息能够参考地址。
Fluid 不是全存储减速和治理,而是利用应用的数据集减速和治理。Fluid 提供了一种更加云原生的形式对数据集进行治理,通过缓存减速引擎实现将底层存储系统的数据 cache 在计算节点的内存或者硬盘上,解决了计算与存储拆散架构中因为数据传输带宽限度以及底层存储带宽与 IOPS 能力限度等问题,导致的 IO 效率不低等问题。Fluid 提供缓存数据调度能力,缓存被纳入 kubernetes 扩大资源,kubernetes 在进行工作的调度的时候,可能参考缓存进行调度策略的调配。
Fluid 有 2 个重要的概念:Dataset 与 Runtime
- Dataset: 数据集是逻辑上相干的一组数据的汇合,统一的文件个性,会被同一运算引擎应用。
- Runtime: 实现数据集安全性,版本治理和数据减速等能力的执行引擎的接口,定义了一系列生命周期的办法。
Fluid 的 Runtime 定义了标准化的接口,Cache Runtime Engine 能够对接多种缓存引擎,提供了用户更灵便的抉择,用户可能针对不同的场景与需要,充分利用缓存引擎减速相应的场景利用。
JuiceFS 简介
JuiceFS 是一个面向云环境设计的高性能开源分布式文件系统,齐全兼容 POSIX、HDFS、S3 接口,实用于大数据、AI 模型训练、Kubernetes 共享存储、海量数据归档治理等场景。
应用 JuiceFS 存储数据,数据自身会被长久化在对象存储(例如,Amazon S3),而数据所对应的元数据能够依据场景需要被长久化在 Redis、MySQL、TiKV 等多种数据库引擎中。JuiceFS 客户端具备数据缓存能力,当通过 JuiceFS 客户端读取数据时,这些数据将会智能地缓存到利用配置的本地缓存门路(能够是内存,也能够是磁盘),同时元数据也会缓存到客户端节点本地内存中。
对于 AI 模型训练场景来说,第一个 epoch 实现之后后续的计算都能够间接从缓存中获取训练数据,极大地晋升了训练效率。JuiceFS 也具备预读、并发读取数据的能力,在 AI 训练场景可能保障每个 mini-batch 的生成效率,提前准备好数据。数据预热可能提前将私有云上的数据换到到本地节点,对于 AI 训练场景可能保障申请完 GPU 资源后,即有预热的数据进行运算,为贵重的 GPU 应用节俭了工夫。
为什么应用 JuiceFSRuntime
云知声 Atlas 超算平台作为底层基础架构,反对着公司在 AI 各个领域的模型训练与推理服务的发展。云知声很早就开始布局建设业界当先的 GPU/CPU 异构 Atlas 计算平台和分布式文件存储系统,该计算集群可为 AI 计算提供高性能计算和海量数据的存储拜访能力。云知声 Atlas 团队在 2021 年初开始接触并跟进 JuiceFS 存储,进行了一系列 POC 测试,在数据可靠性与业务场景的适配,都满足咱们目前的需要。
在训练场景咱们充分利用 JuiceFS 客户端的缓存能力,为 AI 模型训练做数据减速,然而在应用过程中发现了一些问题:
- 训练 Pod 通过 hostpath 挂载,须要在每个计算节点挂载 JuiceFS 客户端,挂载须要管理员操作,挂载参数固定,不够灵便。
- 用户无奈对计算节点客户端的缓存治理,缓存无奈手动清理与扩容。
- 缓存数据集无奈像 Kubernetes 自定义资源一样可能被 kubernetes 进行调度。
因为咱们在生产环境曾经积攒了肯定的 Fluid 应用教训,所以咱们与 Juicedata 团队单干设计并开发了 JuiceFSRuntime,将 Fluid 对数据编排与治理能力和 JuiceFS 的缓存能力联合起来。
什么是 Fluid + JuiceFS(JuiceFSRuntime)
JuiceFSRuntime 是 Fluid 自定义的一种 Runtime,其中能够指定 JuiceFS 的 worker、fuse 镜像以及相应的缓存参数。其构建形式与 Fluid 其余 Runtime 统一,即通过 CRD 的形式构建,JuiceFSRuntime Controller 监听 JuiceFSRuntime 资源,实现缓存 Pod 的治理。
JuiceFSRuntime 反对数据亲和性调度(nodeAffinity),抉择适合的缓存节点,反对 Fuse pod 懒启动,反对用户以 POSIX 接口拜访数据,目前只反对一个挂载点。
其架构图如上图所示,JuiceFSRuntime 由 Fuse Pod 与 Worker Pod 组成。Worker pod 次要实现缓存的治理,如 Runtime 退出时的缓存清理;Fuse pod 次要负责 JuiceFS 客户端的参数设置及挂载。
如何应用 JuiceFSRunime
上面来看看如何应用 JuiceFSRuntime 进行缓存减速。
后期筹备
要应用 JuiceFSRuntime 首先须要筹备元数据引擎和对象存储。
构建元数据引擎
用户能够很容易的在云计算平台购买到各种配置的云 Redis 数据库,如果是评估测试应用能够应用 Docker 疾速的在服务器上运行一个 Redis 数据库实例:
$ sudo docker run -d --name redis \
-v redis-data:/data \
-p 6379:6379 \
--restart unless-stopped \
redis redis-server --appendonly yes
筹备对象存储
和 Redis 数据库一样,简直所有的私有云计算平台都提供对象存储服务。因为 JuiceFS 反对简直所有支流平台的对象存储服务,用户能够联合本人的状况进行部署。
这里是评估测试应该应用的是 Dokcer 运行的 minio 实例:
$ $ sudo docker run -d --name minio \
-p 9000:9000 \
-p 9900:9900 \
-v $PWD/minio-data:/data \
--restart unless-stopped \
minio/minio server /data --console-address ":9900"
对象存储初始的 Access Key 和 Secret Key 均为 minioadmin。
下载并装置 Fluid
依照文档步骤装置 Fluid,在 Fluid 的装置 chart values.yaml
中将 runtime.juicefs.enable
设置为 true,并装置 Fluid。确保 Fluid 集群失常运行:
kubectl get po -n fluid-system
NAME READY STATUS RESTARTS AGE
csi-nodeplugin-fluid-ctc4l 2/2 Running 0 113s
csi-nodeplugin-fluid-k7cqt 2/2 Running 0 113s
csi-nodeplugin-fluid-x9dfd 2/2 Running 0 113s
dataset-controller-57ddd56b54-9vd86 1/1 Running 0 113s
fluid-webhook-84467465f8-t65mr 1/1 Running 0 113s
juicefsruntime-controller-56df96b75f-qzq8x 1/1 Running 0 113s
确保 juicefsruntime-controller
、dataset-controller
、fluid-webhook
的 pod
以及若干 csi-nodeplugin pod
失常运行。
创立 Dataset
在应用 JuiceFS 之前,须要提供元数据服务(如 redis)及对象存储服务(如 minio)的参数,并创立对应的 secret:
kubectl create secret generic jfs-secret \
--from-literal=metaurl=redis://$IP:6379/1 \ # redis 的地址 IP 为 redis 所在节点的 IP
--from-literal=access-key=minioadmin \ # 对象存储的 ak
--from-literal=secret-key=minioadmin #对象存储的 sk
创立 Dataset yaml 文件
cat<<EOF >dataset.yaml
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: jfsdemo
spec:
mounts:
- name: minio
mountPoint: "juicefs:///demo"
options:
bucket: "<bucket>"
storage: "minio"
encryptOptions:
- name: metaurl
valueFrom:
secretKeyRef:
name: jfs-secret
key: metaurl
- name: access-key
valueFrom:
secretKeyRef:
name: jfs-secret
key: access-key
- name: secret-key
valueFrom:
secretKeyRef:
name: jfs-secret
key: secret-key
EOF
因为 JuiceFS 采纳的是本地缓存,对应的 Dataset 只反对一个 mount,且 JuiceFS 没有 UFS,mountpoint 中能够指定须要挂载的子目录 (“juicefs:///” 为根门路),会作为根目录挂载到容器内。
创立 Dataset 并查看 Dataset 状态
$ kubectl create -f dataset.yaml
dataset.data.fluid.io/jfsdemo created
$ kubectl get dataset jfsdemo
NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE
jfsdemo NotBound 44s
如上所示,status 中的 phase 属性值为 NotBound,这意味着该 Dataset 资源对象目前还未与任何 JuiceFSRuntime 资源对象绑定,接下来,咱们将创立一个 JuiceFSRuntime 资源对象。
创立 JuiceFSRuntime
创立 JuiceFSRuntime 的 yaml 文件
$ cat<<EOF >runtime.yaml
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
name: jfsdemo
spec:
replicas: 1
tieredstore:
levels:
- mediumtype: SSD
path: /cache
quota: 40960 # JuiceFS 中 quota 的最小单位是 MiB,所以这里是 40GiB
low: "0.1"
EOF
创立并查看 JuiceFSRuntime
$ $ kubectl create -f runtime.yaml
juicefsruntime.data.fluid.io/jfsdemo created
$ kubectl get juicefsruntime
NAME WORKER PHASE FUSE PHASE AGE
jfsdemo Ready Ready 72s
查看 JuiceFS 相干组件 Pod 的状态
$$ kubectl get po |grep jfs
jfsdemo-worker-mjplw 1/1 Running 0 4m2s
JuiceFSRuntime 没有 master 组件,而 Fuse 组件实现了懒启动,会在 pod 应用时再创立。
创立缓存减速作业
创立须要减速的利用,其中 Pod 应用下面创立的 Dataset 的形式为指定同名的 PVC
$ cat<<EOF >sample.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-app
spec:
containers:
- name: demo
image: nginx
volumeMounts:
- mountPath: /data
name: demo
volumes:
- name: demo
persistentVolumeClaim:
claimName: jfsdemo
EOF
创立 Pod
$ kubectl create -f sample.yaml
pod/demo-app created
查看 pod 状态
$ kubectl get po |grep demo
demo-app 1/1 Running 0 31s
jfsdemo-fuse-fx7np 1/1 Running 0 31s
jfsdemo-worker-mjplw 1/1 Running 0 10m
能够看到 pod 曾经创立胜利,同时 JuiceFS 的 Fuse 组件也启动胜利。
进入 Pod 执行 df -hT
查看缓存目录是否挂载:
$ kubectl exec -it demo-app bash -- df -h
Filesystem Size Used Avail Use% Mounted on
overlay 20G 14G 5.9G 71% /
tmpfs 64M 0 64M 0% /dev
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
JuiceFS:minio 1.0P 7.9M 1.0P 1% /data
能够看到这时候缓存目录曾经胜利挂载了。
接下来,咱们在 demo-app 这个 pod 中测试一下写性能:
$ kubectl exec -it demo-app bash
[root@demo-app /]# df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 20751360 14585944 6165416 71% /
tmpfs 65536 0 65536 0% /dev
tmpfs 3995028 0 3995028 0% /sys/fs/cgroup
JuiceFS:minio 1099511627776 8000 1099511619776 1% /data
/dev/sda2 20751360 14585944 6165416 71% /etc/hosts
shm 65536 0 65536 0% /dev/shm
tmpfs 3995028 12 3995016 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 3995028 0 3995028 0% /proc/acpi
tmpfs 3995028 0 3995028 0% /proc/scsi
tmpfs 3995028 0 3995028 0% /sys/firmware
[root@demo-app /]#
[root@demo-app /]# cd /data
[root@demo-app data]# echo "hello fluid" > hello.txt
[root@demo-app data]# cat hello.txt
hello fluid
最初再来看看缓存性能,在 demo-app 这个 pod 中的挂载目录 /data
中创立一个 1G 的文件,而后再 cp 进去:
$ kubectl exec -it demo-app bash
root@demo-app:~# dd if=/dev/zero of=/data/test.txt count=1024 bs=1M
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 6.55431 s, 164 MB/s
root@demo-app:~# time cp /data/test.txt ./test.txt
real 0m5.014s
user 0m0.003s
sys 0m0.702s
root@demo-app:~# time cp /data/test.txt ./test.txt
real 0m0.602s
user 0m0.004s
sys 0m0.584s
从执行后果来看,第一次 cp 用了 5s,此时建设缓存,第二次 cp 的时候因为缓存曾经存在,只用了 0.6s。JuiceFS 所提供的弱小的缓存能力,使得只有拜访某个文件一次,该文件就会被缓存在本地缓存门路中中,所有接下来的反复拜访都是从 JuiceFS 中间接获取数据。
后续布局
目前 JuiceFSRuntime 反对的性能并不多,将来咱们会持续欠缺,比方 Fuse Pod 以 Nonroot 的形式运行,以及 Dataload 数据预热性能等。
举荐浏览:
知乎 x JuiceFS:利用 JuiceFS 给 Flink 容器启动减速
如有帮忙的话欢送关注咱们 Juicedata/JuiceFS 哟!(0ᴗ0✿)