Please enable Javascript to view the contents

使用 Fluid 和 JuiceFS 在 Kubernetes 管理数据

 ·  ☕ 4 分钟

1. Fluid 简介

下面是来源于 https://github.com/fluid-cloudnative/fluid 的 Fluid 的架构图:

Fluid 抽象了两个概念:

  • Dataset,数据集合,用户视角的抽象
  • Runtime,数据存储、加速等真实服务的抽象

Fluid 主要解决了传统缓存系统在 Kubernetes 上使用的问题:

  1. 通过 CRD 对数据集合 Dataset 进行描述,提供生命周期管理
  2. 依赖于 Runtime 后端,通过 PVC 提供给 Kubernetes 集群应用本地化的分布式缓存服务

使用 Fluid 时的工作流程:

  1. 定义 Dataset,设置好数据的访问凭证、存储位置、读写模式等
  2. 定义 Runtime,runtime controller 对同名的 Dataset 和 Runtime 进行自动绑定 AddOwner;接着创建 worker 配置 Runtime 相关的资源;创建 ${NAMESPACE}- 前缀的 PV 和同名的 PVC
  3. 当有 Pod 挂载 PVC 时,会先在节点上创建一个 fuse pod 并将 /runtime-mnt/juicefs/xxx 目录挂载到主机上,然后 Fluid 的 CSI Controller 将该目录挂载到 Pod 中。

Dataset 和 Runtime 的生命周期在 Fluid 的代码仓库有描述,参考 https://github.com/fluid-cloudnative/fluid/blob/master/docs/zh/dev/runtime_dev_guide.md

下面是 Dataset 的生命周期

下面是 Runtime 的生命周期

使用时:

Pod 对挂载的文件目录请求都会转给 fuse pod,由 fuse 将文件 io 转为网络 io 访问后端的 runtime 存储。

2. 部署 Fluid

  • 创建命名空间
1
kubectl create ns fluid-system
  • 添加 Helm Repo
1
2
helm repo add fluid https://fluid-cloudnative.github.io/charts
helm repo update
  • 部署 Fluid
1
helm install --namespace fluid-system fluid fluid/fluid --devel

由于我 format 使用的 juicefs client 版本为 juicefs version 1.1.1+2023-11-28.437f4e6,为了 work/fuse pod 中的镜像版本与之匹配(当然也可以配置),这里使用的是 --devel 版本,即目前的 1.0.0 内测版本。

1
2
3
4
helm list --namespace fluid-system

NAME 	NAMESPACE   	REVISION	UPDATED                                	STATUS  	CHART               	APP VERSION
fluid	fluid-system	1       	2024-01-25 21:48:14.902476965 +0800 CST	deployed	fluid-1.0.0-alpha.17	1.0.0-719fc87
  • 查看 Pod 状态
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
kubectl -n fluid-system get pod

NAME                                        READY   STATUS    RESTARTS   AGE
csi-nodeplugin-fluid-b8k9l                  2/2     Running   0          9m33s
csi-nodeplugin-fluid-gzl6w                  2/2     Running   0          9m33s
csi-nodeplugin-fluid-p5whc                  2/2     Running   0          9m33s
csi-nodeplugin-fluid-pwplp                  2/2     Running   0          9m33s
csi-nodeplugin-fluid-xs9kc                  2/2     Running   0          9m33s
csi-nodeplugin-fluid-xwwlm                  2/2     Running   0          9m33s
dataset-controller-6978c55675-2rtdr         1/1     Running   0          9m33s
fluid-webhook-76d4c5fd45-bbmw7              1/1     Running   0          9m33s
fluidapp-controller-697656949c-487mv        1/1     Running   0          9m33s
juicefsruntime-controller-fbf45c44f-vtlcf   1/1     Running   0          5m17s

3. 环境准备

  • 启动一个 Redis 实例,提供给 JuiceFS 使用
1
mkdir -p /data/test/redis-data && cd /data/test
1
nerdctl run -d --name redis --network host -v $PWD/redis-data:/data -e REDIS_PASSWORD=mypassword redis:6
  • 配置 Redis 环境变量
1
2
3
4
export REDIS_IP=x.x.x.x
export REDIS_PORT=6379
export REDIS_USER=default
export REDIS_PASSWORD=mypassword
  • 配置存储桶的环境变量
1
2
3
4
5
6
export ACCESS_KEY=xxx
export SECRET_KEY=xxx
export BUCKET=xxx
export ENDPOINT=xxx
export BUCKET_ENPOINT=$BUCKET.$ENDPOINT
export PROVIDER=xxx
  • 创建一个文件系统
1
2
3
4
5
6
export REDIS_DIRECTSERVER=redis://${REDIS_USER}:${REDIS_PASSWORD}@${REDIS_IP}:${REDIS_PORT}/1
juicefs format \
    --storage ${PROVIDER} \
    --bucket ${BUCKET_ENPOINT}\
    ${REDIS_DIRECTSERVER} \
    juicefs-direct-demo

文件系统需要提前初始化,否则在集群中使用时,会提示找不到 .stats 文件类似错误。

  • 设置一个测试的命名空间
1
export NAMESPACE=shaowen-test
  • 【非必须】挂载文件系统到本地
1
juicefs mount -d --buffer-size 2000 --max-uploads 150 ${REDIS_DIRECTSERVER} ./${NAMESPACE}-direct --cache-dir=/data/jfs-${NAMESPACE}
  • 【非必须】进入目录,创建一点测试文件
1
2
cd ${NAMESPACE}-direct
echo "123" > test.txt

4. 配置 DatasSet

  • 创建一个命名空间
1
kubectl create ns ${NAMESPACE}
  • 创建 Secret
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: juicefs-direct-secret
  namespace: ${NAMESPACE}
type: Opaque
stringData:
  metaurl: redis://${REDIS_USER}:${REDIS_PASSWORD}@${REDIS_IP}:6379/1
  access-key: ${ACCESS_KEY}
  secret-key: ${SECRET_KEY}
EOF

Redis 需要设置用户名,默认是 default,要明文指出。

  • 创建 Dataset
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
kubectl apply -f - <<EOF
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
  name: juicefs-direct-demo
  namespace: ${NAMESPACE}
spec:
  accessModes:
    - ReadWriteMany
  mounts:
    - name: juicefs-direct-demo
      mountPoint: "juicefs:///"
      options:
        bucket: ${BUCKET_ENPOINT}
        storage: ${PROVIDER}
      encryptOptions:
        - name: metaurl
          valueFrom:
            secretKeyRef:
              name: juicefs-direct-secret
              key: metaurl
        - name: access-key
          valueFrom:
            secretKeyRef:
              name: juicefs-direct-secret
              key: access-key
        - name: secret-key
          valueFrom:
            secretKeyRef:
              name: juicefs-direct-secret
              key: secret-key
EOF

bucket 应该是 Bucket.Endpoint 的完整形式,而不能只填一个桶名。默认的 accessModes 为 ReadOnlyMany,即只读模式,这里改为 ReadWriteMany 。另外,这里配置挂载的是 JuiceFS 的 / 目录,在生产使用时,可以按照项目、应用维度挂载不同的子目录。

  • 查看 Dataset
1
2
3
4
kubectl -n ${NAMESPACE} get dataset

NAME                  UFS TOTAL SIZE   CACHED   CACHE CAPACITY   CACHED PERCENTAGE   PHASE      AGE
juicefs-direct-demo                                                                  NotBound   4s

此时还没有配置同名的 Runtime,因此状态为 NotBound。

5. 配置 Runtime

  • 创建 Runtime
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kubectl apply -f - <<EOF
apiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:
  name: juicefs-direct-demo
  namespace: ${NAMESPACE}
spec:
  replicas: 1
  tieredstore:
    levels:
      - mediumtype: SSD
        path: /cache
        quota: 40960   # 40GiB
        low: "0.1"
EOF

这里有很多参数可以配置,可以参考 https://github.com/fluid-cloudnative/fluid 对应分支 api 目录下 CRD 的定义说明。使用不同的 Fluid 版本,参数可能会有不同,注意区分。

  • 查看 Runtime 状态
1
2
3
4
kubectl -n ${NAMESPACE} get juicefsruntime

NAME                  WORKER PHASE   FUSE PHASE   AGE
juicefs-direct-demo   Ready                       96s

可能需要等待一会儿才能 Ready,因为需要创建 woker 。

  • 查看 Pod 状态
1
2
3
4
kubectl -n ${NAMESPACE} get pod

NAME                           READY   STATUS    RESTARTS   AGE
juicefs-direct-demo-worker-0   1/1     Running   0          115s

worker 无异常,正常运行。

  • 查看 Dataset 状态
1
2
3
4
kubectl -n ${NAMESPACE} get dataset

NAME                  UFS TOTAL SIZE   CACHED   CACHE CAPACITY   CACHED PERCENTAGE   PHASE   AGE
juicefs-direct-demo   1.01GiB                   40.00KiB                             Bound   33m
  • 查看 PVC
1
2
3
4
kubectl -n shaowen-test get pvc

NAME                  STATUS   VOLUME                             CAPACITY   ACCESS MODES   STORAGECLASS   AGE
juicefs-direct-demo   Bound    shaowen-test-juicefs-direct-demo   100Pi      RWX            fluid          5m21s

RWX 表示 ReadWriteMany,即读写模式。

6. 创建负载

  • 创建负载
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: juicefs-direct-demo
  namespace: ${NAMESPACE}
spec:
  containers:
    - name: demo
      image: shaowenchen/demo-ubuntu
      volumeMounts:
        - mountPath: /data/jfs
          name: data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: juicefs-direct-demo
EOF
  • 查看负载
1
2
3
4
5
6
kubectl -n ${NAMESPACE} get pod juicefs-direct-demo

NAME                             READY   STATUS    RESTARTS   AGE
juicefs-direct-demo              1/1     Running   0          52m
juicefs-direct-demo-fuse-mkz4x   1/1     Running   0          52m
juicefs-direct-demo-worker-0     1/1     Running   0          54m
  • 进入负载查看数据目录
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kubectl -n ${NAMESPACE} exec -it juicefs-direct-demo bash

ls -al /data/jfs/

total 7
drwxrwxrwx 2 root root 4096 Jan 25 12:33 .
drwxr-xr-x 3 root root   25 Jan 25 12:53 ..
-r-------- 1 root root    0 Jan 25 12:53 .accesslog
-r-------- 1 root root 1627 Jan 25 12:53 .config
-r--r--r-- 1 root root    0 Jan 25 12:53 .stats
dr-xr-xr-x 2 root root    0 Jan 25 12:53 .trash
-rw-r--r-- 1 root root    4 Jan 25 12:33 test.txt
  • 简单跑下 benchmark

在 Pod 中挂载了 JuiceFS 的目录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
juicefs bench --block-size 4096 --big-file-size 1024 --threads 5 ./

+------------------+------------------+---------------+
|       ITEM       |       VALUE      |      COST     |
+------------------+------------------+---------------+
|   Write big file |     207.88 MiB/s |  24.63 s/file |
|    Read big file |     761.77 MiB/s |   6.72 s/file |
| Write small file |    136.8 files/s | 36.56 ms/file |
|  Read small file |    293.7 files/s | 17.03 ms/file |
|        Stat file |   9007.3 files/s |  0.56 ms/file |
|   FUSE operation | 89312 operations |    0.97 ms/op |
|      Update meta |  1595 operations |    1.57 ms/op |
|       Put object |  1780 operations |  111.32 ms/op |
|       Get object |  1780 operations |   75.37 ms/op |
+------------------+------------------+---------------+

微信公众号
作者
微信公众号