在 Fluid 中使用 JuiceFS
Fluid 是一个 Kubernetes 原生的分布式数据集编排和加速引擎,本文介绍如何在 Fluid 中使用 JuiceFS。更多关于 Fluid 使用方式,请参考 Fluid 官方文档。
背景介绍
Fluid 使用 CRD 的方式来管理资源,分别提供了如下自定义资源:
- Dataset:用于描述一组数据集,资源中记录了文件系统的名称、引用了 JuiceFS 文件系统的认证信息,下方的运行时组件也都会通过 Dataset 来挂载文件系统。
- JuiceFSRuntime:承载 JuiceFS 的运行时组件,为应用提供 Dataset 的访问能力。组件包含:
- Worker,相当于 JuiceFS 的独立缓存集群,每一个 worker pod 都是一个缓存集群成员
- FUSE,也就是实际使用着缓存集群的客户端,和 worker pod 类似,每一个 FUSE pod 也是一个 JuiceFS 客户端,他们同样加入缓存组,但启用了
--no-sharing
,以「只索取、不分享」得方式使用缓存集群的数据
以及一些使用过程中可能要用到的可选资源,可以点击链接跳转到 Fluid 官方文档、详细了解:
- DataLoad:用于描述数据预热任务,其中运行的是 JuiceFS 预热命令,
juicefs warmup
。 - DataMigrate:用于描述数据迁移任务,其中运行的是
juicefs sync
命令。
安装
Fluid 提供 Helm Chart 的安装方式,如下:
helm repo add fluid https://fluid-cloudnative.github.io/charts
helm repo update
helm install fluid fluid/fluid
另外,如果你是在阿里云容器服务 ACK 中使用 Fluid,可在集群中选择安装「云原生 AI 套件」并开启「Fluid 数据加速」功能。
运行 JuiceFS
在 Fluid 中运行 JuiceFS 只需要 3 步:
- 创建文件系统认证信息
- 创建 Dataset
- 创建与 Dataset 同名的 JuiceFSRuntime
- (可选)创建 DataLoad,提前预热数据
创建文件系统认证信息
进行这一步之前,你需要有一个已经能够正常挂载、运行的 JuiceFS 文件系统,如果尚未创建,请先在 JuiceFS 云服务中创建文件系统。
创建好文件系统后,推荐先挂载测试、确保正常运行,然后再把这些认证信息按照下方示范创建成 Kubernetes Secret。
apiVersion: v1
kind: Secret
metadata:
name: jfs-secret
type: Opaque
stringData:
token: ${JUICEFS_TOKEN}
access-key: ${ACCESS_KEY}
secret-key: ${SECRET_KEY}
创建 Dataset
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: jfsdemo
spec:
mounts:
- name: <vol-name> # JuiceFS 文件系统名称
mountPoint: "juicefs:///"
encryptOptions:
- name: token
valueFrom:
secretKeyRef:
name: jfs-secret
key: token
- name: access-key
valueFrom:
secretKeyRef:
name: jfs-secret
key: access-key
- name: secret-key
valueFrom:
secretKeyRef:
name: jfs-secret
key: secret-key
其中:
name
:需要与在 JuiceFS 控制台创建的文件系统名一致。mountPoint
:指的是 JuiceFS 的子目录,是用户在 JuiceFS 文件系统中存储数据的目录,以juicefs://
开头;如juicefs:///demo
为 JuiceFS 文件系统的/demo
子目录,juicefs:///
为 JuiceFS 文件系统的根目录。encryptOptions
:用于指定 JuiceFS 认证信息,需要与上一步创建的 Secret 中的 key 一致。
创建 JuiceFSRuntime
Fluid 中 Runtime 和 Dataset 是一对一的关系,一个 Runtime 只能绑定一个 Dataset,且二者通过同名来绑定。
在实际业务环境中,一个典型的 JuiceFSRuntime 示例如下:
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
name: jfsdemo # 与 Dataset 同名
spec:
tieredstore:
levels:
- mediumtype: SSD
path: /data/jfscache
quota: 500Mi
low: "0.01"
# volumeType: hostPath (default), emptyDir
worker:
options:
"cache-size": "102400"
fuse:
options:
# 设为 0 以禁用客户端本地缓存,最大程度利用分布式缓存
# 详见 https://juicefs.com/docs/zh/cloud/guide/distributed-cache/#dedicated-cache-cluster
"cache-size": "0"
tieredstore
的字面含义是“多级存储”,但 JuiceFS 目前并不支持多级存储,因此只需要配置一个缓存级别,也就是 levels
列表只需要填写一个项目,其中各个字段含义:
-
mediumtype
:用于指明缓存目录所使用的存储设备类型,取值为[MEM|SSD|HDD]
,需要注意这个字段是一个“标记”,并不一定代表底层存储就是内存、SSD 或机械硬盘,实际物理存储介质取决于path
的设置。特别地,如果取值
MEM
,那么 Fluid 会根据quota
大小自动设置容器的内存资源声明,保证容器的 memory requests; -
path
:缓存目录,多盘缓存用:
分割多个目录,设置为/dev/shm
以使用内存设备。该参数作用同--cache-dir
; -
quota
:每个 worker 容器(也就是缓存集群成员节点)的最大缓存容量,将该值乘以replicas
(容器数量),就能得到整个缓存集群的总体可用空间。该参数作用同--cache-size
,但单位和写法不同,详见上方示范和对应的文档; -
low
:缓存盘的最小剩余空间占比,默认 0.2,在上方示范中填写为 0.01 代表最多可以使用缓存盘 99%。该参数作用同--free-space-ratio
。 -
volumeType
:卷类型,支持目前支持[hostPath|emptyDir]
,默认均为hostPath
,也就是将缓存目录从宿主机目录挂载到容器里。在不支持特权模式(privileged)的情况下,需要改为emptyDir
,用容器存储作为缓存目录,多用于 serverless 场景下。
tieredstore
同时对 worker 和 FUSE 生效,但考虑到在 Fluid 中使用 JuiceFS,属于一个「独立缓存集群」架构,这种场景往往需要为缓存集群和客户端采用不同的缓存设置,因此在上方的示范中也体现了这一点:
-
tieredstore
代表默认的缓存配置,如果worker.options
、fuse.options
中没有覆盖缓存相关参数,那么便会以tieredstore
的设置为准; -
worker.options
中设置了"cache-size": "102400"
,单位为 MiB,相当于覆盖了tieredstore
中设置的quota: 500Mi
。注意两个字段虽然作用相同,但写法不一样,
cache-size
是 JuiceFS 客户端挂载参数,而quota
是 Fluid Runtime 参数,支持使用 Kubernetes 资源的单位写法,并且最终会被翻译为 JuiceFS 客户端的--cache-size
挂载参数; -
fuse.options
中设置了"cache-size": "0"
,代表 FUSE 容器完全不使用本地缓存,最大程度利用缓存集群(也就是 worker)的数据。要注意,这种场景下 FUSE 容器虽然不使用本地磁盘缓存,但随着读取文件,也会在内存中建立内核页缓存,也能获得极致性能。
JuiceFSRuntime 和 Dataset 创建好后,二者会 自动绑定(status.phase
为 Bound
),绑定后,Fluid 会创建 worker pod,其个数为 replicas
指定的值,这些 pod 一起组成分布式缓存集群。
$ kubectl get po | grep worker
jfsdemo-worker-0 1/1 Running 0 4m2s
企业版(私有部署)相关配置
JuiceFS Web 控制台负责着客户端的挂载认证、配置文件下发等工作。而在私有部署环境中,控制台的地址不再是 https://juicefs.com/console,需要在 JuiceFSRuntime 中指定控制台的地址,示例如下:
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
name: jfsdemo
spec:
worker:
env:
- name: "BASE_URL"
# 将 <CONSOLE_URL> 替换为私有部署控制台地址,例如 http://console.juicefs.com:8080
value: "<CONSOLE_URL>/static"
fuse:
env:
- name: "BASE_URL"
# 将 <CONSOLE_URL> 替换为私有部署控制台地址,例如 http://console.juicefs.com:8080
value: "<CONSOLE_URL>/static"
使用 Dataset
Dataset 和 JuiceFSRuntime 创建完成后,由于二者同名,所以会自动绑定。Fluid 会创建一个与 Dataset 同名的 PVC,应用就可以通过 PVC 来使用 JuiceFS 了。参考我们的示范,创建一个示例如下:
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
Fluid 会在应用 pod 所在的节点上创建一个运行 JuiceFS 的客户端 pod,称为 FUSE pod,用以为应用提供数据读写的服务,如下:
$ kubectl get po |grep demo
demo-app 1/1 Running 0 31s
jfsdemo-fuse-fx7np 1/1 Running 0 31s
jfsdemo-worker-0 1/1 Running 0 10m
创建 DataLoad
在 Dataset 可用(Bound 状态)之后,接下来可以通过 DataLoad 来预热数据(也就是运行 juicefs warmup
,提前将数据下载到缓存集群中)。以下是一个 DataLoad 的示例:
apiVersion: data.fluid.io/v1alpha1
kind: DataLoad
metadata:
name: jfs-load
spec:
dataset:
name: jfsdemo
namespace: default
target:
- path: /dir1
其中:
spec.dataset.name
:指定要加速数据访问的 Dataset 名称;spec.dataset.namespace
:指定要加速数据访问的 Dataset 的 namespace;spec.target.path
:指定要加速数据访问的数据路径,可以是目录或文件;target 是列表,可以声明多个 path,达到预热多个目录的效果。path 的含义就是juicefs warmup
的PATH
参数。
按照上方示范创建出 DataLoad 以后,预热任务(Kubernetes Job)就会自动创建并执行。Job 成功以后,数据就预热到缓存组中了,也就是各个 worker 节点的缓存盘中。需要注意的是,预热任务成功,并不意味着数据完整下载到了本地,这是因为缓存空间可能不够大,导致数据发生了淘汰。如果后续的使用发现命中率不符合预期,阅读「为什么预热后仍然会访问对象存储?」并排查。
如果排查发现缓存空间不足、后续 worker 进行了扩容,那么也需要重新创建 DataLoad 资源,来再次执行预热任务。
缓存组名称
在 JuiceFS 分布式缓存中,缓存数据在同一个组内共享。Fluid 中已经自动为你设置了缓存组名称,默认为 <namespace>-<runtime>
,因此如果你的场景中,所有的 JuiceFS 客户端都通过 Fluid 部署、没有其他类型的挂载点(比如宿主机挂载点、CSI 驱动),那么通常不需要关注或者手动修改缓存组名称。
如果你不仅在 Fluid 中使用 JuiceFS,还需要配合其他场景使用,那么就可能需要手动覆盖、修改缓存组名。比方说:独立缓存集群并没有以 Fluid worker 的形式部署,而是另一批宿主机挂载点,这时候需要对齐缓存组名称:
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
name: jfsdemo
spec:
# 使用外部部署的独立缓存集群,因此将 Fluid worker 副本数设为 0
replicas: 0
worker:
options:
"cache-group": "mygroup"
fuse:
options:
"cache-group": "mygroup"
在上方的示范中,外部部署的缓存集群的组名为 mygroup
,因此为了让 Fluid Runtime 能够直接利用该缓存组的数据,需要在 fuse.options
中覆盖 cache-group
参数,来加入 mygroup
这个缓存组。
Dataset 的访问模式
Dataset 的默认访问模式为 ReadOnlyMany
,也支持 ReadWriteMany
,可以通过 spec.accessModes
来修改,示例如下:
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: demo
spec:
accessModes:
- ReadWriteMany
...
子目录配额设置
在 Dataset 中可以设置访问 JuiceFS 文件系统的子目录,同时可以为子目录设置配额。以下是一个 Dataset 的示例:
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: jfsdemo
spec:
mounts:
- name: minio
mountPoint: "juicefs:///demo"
options:
quota: "2Gi"
其中,spec.mounts.options.quota
指定 Dataset 子目录的配额,至少为 1Gi。
商业版 4.9.2 以上支持子目录配额设置。设置子目录配额时,需要确保 JuiceFS 的版本满足要求,在 JuiceFSRuntime 中指定 FUSE 的镜像为 juicedata/juicefs-fuse:v1.0.4-4.9.2
,具体镜像参考 JuiceFS 镜像仓库。
FUSE pod 的配置
FUSE pod 是运行在应用 pod 所在的节点上,并为其提供数据读写服务的组件。
对于 ACK AI 套件 1.0.5 之前版本的 Fluid,FUSE 的配置无法在 Runtime 中动态修改——需要删除重建整个 JuiceFSRuntime。从 1.0.5 开始,Runtime 支持动态编辑 FUSE 相关参数。
但是需要注意,修改完 JuiceFSRuntime 后,还需要删除 FUSE pod,令其重建后,改动才真正生效。
基础配置
对于 FUSE pod 本身的配置,均在 JuiceFSRuntime 中设置。
JuiceFSRuntime 的基础配置如下:
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
name: jfsdemo
spec:
fuse:
image: juicedata/juicefs-fuse
imageTag: ee-4.9.19
imagePullPolicy: IfNotPresent
podMetadata:
labels:
juicefs: "fuse"
annotations:
juicefs: "fuse"
networkMode: ContainerNetwork
env:
- name: "GOOGLE_CLOUD_PROJECT"
value: "xxx"
resources:
limits:
cpu: 2
memory: 5Gi
requests:
cpu: 1
memory: 1Gi
options:
"buffer-size": "1000"
其中:
spec.fuse.image
:指定 FUSE pod 的镜像;spec.fuse.imageTag
:指定 FUSE pod 的镜像标签;spec.fuse.imagePullPolicy
:指定 FUSE pod 的镜像拉取策略;spec.fuse.podMetadata
:指定 FUSE pod 的元数据,包括 labels 和 annotations;spec.fuse.networkMode
:指定 FUSE pod 的网络模式,目前支持HostNetwork
和ContainerNetwork
,默认为HostNetwork
;spec.fuse.env
:指定 FUSE pod 的环境变量;spec.fuse.resources
:指定 FUSE pod 的资源限制;spec.fuse.options
:指定 FUSE pod 的挂载参数。