Fluid支持分层数据缓存本地性调度(Tiered Locality Scheduling)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 依赖容器化带来的高效部署、敏捷迭代,以及云计算在资源成本和弹性扩展方面的天然优势,以 Kubernetes 为代表的云原生编排框架吸引着越来越多的 AI 与大数据应用在其上部署和运行。但是数据密集型应用计算框架的设计理念和云原生灵活的应用编排的分歧,导致了数据访问和计算瓶颈。CNCF开源项目Fluid作为 AI 与大数据云原生应用提供一层高效便捷的数据抽象,将数据从存储抽象出来,针对具体的场景(比如大模型),加速计算访问数据。

摘要

依赖容器化带来的高效部署、敏捷迭代,以及云计算在资源成本和弹性扩展方面的天然优势,以 Kubernetes 为代表的云原生编排框架吸引着越来越多的 AI 与大数据应用在其上部署和运行。但是数据密集型应用计算框架的设计理念和云原生灵活的应用编排的分歧,导致了数据访问和计算瓶颈。

CNCF开源项目Fluid作为 AI 与大数据云原生应用提供一层高效便捷的数据抽象,将数据从存储抽象出来,针对具体的场景(比如大模型),加速计算访问数据。

Fluid提供了可灵活配置分层数据本地性调度能力。在云平台和数据中心的场景下,可以结合数据集缓存的位置信息对于使用该数据集的任务进行调度,无需用户了解底层数据缓存的排布的前提下,将任务优先调度到和数据传输距离较近的节点。

  1. Fluid为什么要支持分层的数据本地性调度能力?因为云原生体系下,计算存储分离的架构是业界的主流,它可以带来更好的架构灵活性和成本上的优势,但是计算访问数据的性能也随之受到影响。解决这一问题一个很直接的想法就是云上或者数据中心引入缓存层,在计算侧部署一个缓存或者分布式存储。但是具体实践中,这并不能保证带来更好的性能收益,其中一个重要的原因就是用户并没有意识到实际部署物理位置差异带来的网络延迟和吞吐受限。如何解决这个问题呢?Fluid参考大数据领域的本地性调度策略。在大数据领域中,有一个著名的概念是"移动数据不如移动计算"。这是因为数据在网络中的传输会增加较大的I/O开销。为了提高效率,需要尽量减少I/O开销,即尽量避免数据在网络中传输。即使数据必须在网络上传输,也要尽量缩短传输距离,而数据本地性就是衡量这个传输距离的概念。

而Fluid在将数据的分布式缓存部署到Kubernetes集群后,就可以根据数据缓存本地性传输距离的远近进而划分为不同级别。当数据能够在本地计算节点上进行计算,而无需在网络上进行传输时,就达到了最佳的数据本地性级别。如果无法实现最佳本地性,则根据数据的传输距离划分为不同的级别,比如同一个节点(Node),机架(Rack),可用区(Availability Zone),区域(Region)。传输距离越远,数据本地性级别越低,延时的影响会越高。

加图

  1. Fluid为什么要支持分层的数据本地性调度能力的可灵活配置性?因为不同的公共云对于不同亲和性有不同的定义,比如AWS支持Placement groups,阿里云支持Deployment Set,这些不同于Kubernetes内置的标签topology.kubernetes.io/zone,topology.kubernetes.io/region;同时在自建数据中心中对于同一个机架等特有概念会有不同的标签;同时一些低版本Kubernetes中关于zone和region也有不同的标签。如果有特定的需求,可以在部署和升级Fluid时进行配置。

在此基础上,Fluid提供了分层数据本地性调度能力。Fluid负责数据集缓存本身的编排调度和数据亲和性调度,在部署缓存Pod时优先反亲和性,这样可以保证每个缓存的worker能够充分使用带宽。同时在调度使用该数据集的应用Pod时,则会根据分层的数据亲和性,优先调度到缓存所在的节点,当条件不满足的时候则会调度到同一个可用区的节点,这样可以避免跨可用区的数据访问。

演示:

本演示介绍如何通过ACK Fluid的分层数据缓存亲和性调度确保缓存数据和计算任务运行在同一个可用区,

本实验分为三部分:

1.软约束调度,优先将使用分布式缓存的Pod调度到和分布式缓存同一个地域的节点,如果条件不满足的话,最终调度仍然会成功

2.强制调度,必须将使用分布式缓存的Pod调度到和分布式缓存同一个地域的节点,直到条件满足,最终调度才会成功

3.修改整体调度策略配置,可以针对定制分层亲和调度的条件修改强制调度的。

前提条件

  • 已创建ACK Pro版集群,且集群版本为1.18及以上。具体操作,请参见创建ACK Pro版集群
  • 已安装云原生AI套件并部署ack-fluid组件。

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

背景信息

准备好K8s和OSS环境的条件,您只需要耗费10分钟左右即可完成JindoRuntime环境的部署。

环境准备:

步骤一:查看Kubernetes节点信息

在本次实验环境中,包含三个节点,其中节点cn-beijing.192.168.125.127运行在阿里云北京可用区b,cn-beijing.192.168.58.146cn-beijing.192.168.58.147运行在阿里云北京可用区l 。

$ kubectl get no -o custom-columns="NAME:.metadata.name,ZONE:.metadata.labels.topology\.kubernetes\.io/zone"
NAME                         ZONE
cn-beijing.192.168.125.127   cn-beijing-b
cn-beijing.192.168.58.146    cn-beijing-l
cn-beijing.192.168.58.147    cn-beijing-l

查看调度策略

kubectl get cm -n fluid-system tiered-locality-config -oyaml
apiVersion: v1
data:
  tieredLocality: |
    preferred:
    - name: fluid.io/node
      weight: 100
    - name: topology.kubernetes.io/zone
      weight: 50
    - name: topology.kubernetes.io/region
      weight: 20
    required:
    - fluid.io/node
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: fluid
    meta.helm.sh/release-namespace: default
  labels:
    app.kubernetes.io/managed-by: Helm
  name: tiered-locality-config
  namespace: fluid-system

步骤二:准备OSS Bucket的数据

  1. 执行以下命令,下载一份测试数据。
$ wget https://archive.apache.org/dist/hbase/2.5.2/RELEASENOTES.md
  1. 将下载的测试数据上传到阿里云OSS对应的Bucket上,上传方法可以借助OSS提供的客户端工具ossutil。具体操作,请参见安装ossutil
$ ossutil cp RELEASENOTES.md oss://<bucket>/<path>/RELEASENOTES.md

步骤三:创建Dataset和JindoRuntime

  1. 创建一个mySecret.yaml文件,用于保存OSS的accessKeyIdaccessKeySecret。YAML示例如下所示。
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
stringData:
  fs.oss.accessKeyId: ****** # 请输入accessKeyId。
  fs.oss.accessKeySecret: ****** # # 请输入accessKeySecret。
  1. 执行以下命令,生成Secret。
kubectl create -f mySecret.yaml

预期输出:

secret/demo created
  1. 创建一个dataset.yaml文件,用于创建Dataset。展开查看YAML示例
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
  name: demo
spec:
  mounts:
    - mountPoint: oss://<bucket-name>/<path>
      options:
        fs.oss.endpoint: <oss-endpoint>
      name: demo
      path: "/"
      encryptOptions:
        - name: fs.oss.accessKeyId
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: fs.oss.accessKeyId
        - name: fs.oss.accessKeySecret
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: fs.oss.accessKeySecret
---
apiVersion: data.fluid.io/v1alpha1
kind: JindoRuntime
metadata:
  name: demo
spec:
  replicas: 1
  master:
    nodeSelector:
      topology.kubernetes.io/zone: cn-beijing-l
  worker:
    nodeSelector:
      topology.kubernetes.io/zone: cn-beijing-l
  tieredstore:
    levels:
      - mediumtype: MEM
        path: /dev/shm
        quota: 20Gi
        high: "0.99"
        low: "0.8"

重要:如果想使用分层亲和性调度,在指定缓存部署时务必要对于worker角色配置nodeSelector或者nodeAffinity。

相关参数解释如下表所示:

参数

说明

Dataset

mountPoint

oss://<oss_bucket>/<path>表示挂载UFS的路径,路径中不需要包含Endpoint信息。

fs.oss.endpoint

OSS Bucket的Endpoint信息,公网或私网地址皆可。

accessModes

Dataset的访问模式。

JindoRuntime

replicas

创建JindoFS集群的Worker数量。

mediumtype

缓存类型。定义创建JindoRuntime模板样例时,JindoFS支持HDD、SSD、MEM其中任意一种缓存类型。

path

存储路径,暂时只支持单个路径。当选择MEM做缓存时,需指定一个本地路径来存储Log等文件。

quota

缓存最大容量,单位GB。缓存容量可以根据UFS数据大小自行配置。

high

存储容量上限大小。

low

存储容量下限大小。

fuse.args

表示可选的Fuse客户端挂载参数。通常与Dataset的访问模式搭配使用。

  • 当Dataset访问模式为ReadOnlyMany时,开启kernel_cache以利用内核缓存优化读性能。此时您可以设置attr_timeout(文件属性缓存保留时间)、entry_timeout(文件名读取缓存保留时间)超时时间、negative_timeout(文件名读取失败缓存保留时间),默认均为7200s。
  • 当Dataset访问模式为ReadWriteMany时,建议您使用默认配置。此时参数设置如下:- -oauto_cache- -oattr_timeout=0- -oentry_timeout=0- -onegative_timeout=0说明使用auto_cache以确保如果文件大小或修改时间发生变化,缓存就会失效。同时将超时时间都设置为0。
  1. 执行以下命令,通过部署dateset.yaml创建JindoRuntime和Dataset。
kubectl create -f dataset.yaml

预期输出:

dataset.data.fluid.io/demo created
jindoruntime.data.fluid.io/demo created
  1. 执行以下命令,查看Dataset的部署情况。
kubectl get dataset

预期输出:

NAME    UFS TOTAL SIZE   CACHED      CACHE CAPACITY   CACHED PERCENTAGE   PHASE   AGE
demo    588.90KiB        0.00B       10.00GiB         0.0%                Bound   2m7s

实验一: 软约束调度

  1. 创建应用Pod
$ cat<<EOF >app-1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-1
  labels:
    # enable Fluid's scheduling optimization for the pod
    fuse.serverful.fluid.io/inject: "true"
spec:
  containers:
    - name: app-1
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: demo
  volumes:
    - name: demo
      persistentVolumeClaim:
        claimName: demo
EOF
$ kubectl create -f app-1.yaml

注意:如果需要Fluid干预调度,必须在labels中开启fuse.serverful.fluid.io/inject: "true"

查看Pod所在节点为cn-beijing.192.168.58.147, 所在可用区就是在阿里云北京可用区l,证明这个Pod可以优先调度和分布式缓存同一个区域。

$ kubectl get po app-1 -owide
kubectl get po app-1 -owide
NAME    READY   STATUS    RESTARTS   AGE     IP               NODE                
app-1   1/1     Running   0          4m59s   192.168.58.169   cn-beijing.192.168.58.147
  1. 将北京可用区l的两个节点都设置成不可调度
$ kubectl cordon cn-beijing.192.168.58.146
$ kubectl cordon cn-beijing.192.168.58.147
  1. 此时阿里云北京可用区l的节点都处于不可调度的状态, 目的是不让新调度的Pod再调度到该可用区
$ kubectl get no
NAME                         STATUS                     ROLES    AGE   VERSION
cn-beijing.192.168.125.127   Ready                      <none>   32h   v1.26.3-aliyun.1
cn-beijing.192.168.58.146    Ready,SchedulingDisabled   <none>   81d   v1.26.3-aliyun.1
cn-beijing.192.168.58.147    Ready,SchedulingDisabled   worker   81d   v1.26.3-aliyun.1
  1. 提交于之前配置完全相同的第二个应用Pod
$ cat<<EOF >app-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-2
  labels:
    # enable Fluid's scheduling optimization for the pod
    fuse.serverful.fluid.io/inject: "true"
spec:
  containers:
    - name: app-2
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: demo
  volumes:
    - name: demo
      persistentVolumeClaim:
        claimName: demo
EOF
$ kubectl create -f app-2.yaml
  1. 此时该Pod被调度到了节点为cn-beijing.192.168.58.147, 所在可用区就是在阿里云北京可用区b, 证明该Pod在调度条件不满足的时候,可以勉强调度到不同可用区的节点。
$ kubectl get po -owide app-2
NAME    READY   STATUS    RESTARTS   AGE   IP                NODE                         NOMINATED NODE   READINESS GATES
app-2   1/1     Running   0          98s   192.168.125.131   cn-beijing.192.168.125.127   <none>           <none>

结论:使用软约束调度,会优先调度到分布式缓存所在可用区,如果调度条件不满足时会退而求其次到调度其他可用区的节点。

实验二: 强制调度

  1. 创建应用Pod,metadata中指定label(格式fluid.io/dataset.{dataset_name}.sched: required),如fluid.io/dataset.demo.sched: required表明该Pod需要使用强制亲和调度策略,按照默认配置调度到数据集 demo 的缓存节点上。
$ cat<<EOF >app-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-3
  labels:
    # enable Fluid's scheduling optimization for the pod
    fuse.serverful.fluid.io/inject: "true"
    fluid.io/dataset.demo.sched: required
spec:
  containers:
    - name: app-3
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: demo
  volumes:
    - name: demo
      persistentVolumeClaim:
        claimName: demo
EOF
$ kubectl create -f app-3.yaml

  1. 此时Pod app-3处于Pending状态,不可以调度,查看事件, 可以发现两个节点不可以调度,另外一个节点则不满足调度条件didn't match Pod's node affinity/selector,,说明强制调度生效。
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  16s   default-scheduler  0/3 nodes are available: 1 node(s) didn't match Pod's node affinity/selector, 2 node(s) were unschedulable. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling., .
  1. 查看此时的Pod调用策略, 强制要调度到缓存所在节点
$kubectl get po app-3 -o jsonpath='{.spec.affinity}'
{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"fluid.io/s-default-demo","operator":"In","values":["true"]}]}]}}}%
  1. 删除该Pod
$ kubectl delete po app-3

实验三: 修改调度策略

  1. 将强制调度策略从节点亲和性修改为可用区亲和性,这是为了满足在一些性能敏感的场景下强制要求缓存和计算资源运行在同一个可用区(数据中心)。
$ kubectl edit cm -n fluid-system tiered-locality-config
apiVersion: v1
data:
  tieredLocality: |
    preferred:
    - name: fluid.io/node
      weight: 100
    - name: topology.kubernetes.io/zone
      weight: 50
    - name: topology.kubernetes.io/region
      weight: 20
    required:
    - topology.kubernetes.io/zone
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: fluid
    meta.helm.sh/release-namespace: default
  labels:
    app.kubernetes.io/managed-by: Helm
  name: tiered-locality-config
  namespace: fluid-system

具体变化在于

修改前:

required:
    - fluid.io/node

修改后:

required:
    - topology.kubernetes.io/zone
  1. 无需重启fluid-webhook即可以使配置生效
  2. 通过ACK添加新的节点,并且查看其所在区域
$ kubectl get no -o custom-columns="NAME:.metadata.name,ZONE:.metadata.labels.topology\.kubernetes\.io/zone"
NAME                         ZONE           STATUS
cn-beijing.192.168.125.127   cn-beijing-b   True
cn-beijing.192.168.58.146    cn-beijing-l   True
cn-beijing.192.168.58.147    cn-beijing-l   True
cn-beijing.192.168.58.180    cn-beijing-l   True

其中cn-beijing.192.168.58.180 为可用区L的新加节点。

  1. 再次创建应用Pod,metadata中指定label(格式fluid.io/dataset.{dataset_name}.sched: required),如fluid.io/dataset.demo.sched: required表明该Pod需要使用强制亲和调度策略,按照默认配置调度到数据集 demo 的缓存节点上。
$ cat<<EOF >app-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-3
  labels:
    # enable Fluid's scheduling optimization for the pod
    fuse.serverful.fluid.io/inject: "true"
    fluid.io/dataset.demo.sched: required
spec:
  containers:
    - name: app-3
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: demo
  volumes:
    - name: demo
      persistentVolumeClaim:
        claimName: demo
EOF
$ kubectl create -f app-3.yaml

可以发现此时的Pod已经处于运行状态,并且运行在新扩容的节点cn-beijing.192.168.58.180上。同时查看应用Pod的affinity配置:

$ kubectl get po app-3 -o jsonpath='{.spec.affinity}'
{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"topology.kubernetes.io/zone","operator":"In","values":["cn-beijing-l"]}]}]}}}%

实验结果:

为了比较跨可用区对于大模型数据访问的性能差异,我们将选择将一个30GiB的模型放置在OSS上,并使用Fluid以同样的方式进行访问比较。

我们选择了ECS实例机型:ecs.g8i.24xlarge,该实例具有64 vCPU、256GiB内存和30Gbps网络带宽。我们将通过Fluid加速访问模式(数据预热和多流数据加速)同时访问可用区内和跨可用区内的数据,以评估性能差异。

我们的观察是,同一个可用区的性能会提升1.41倍,同区域的数据访问带宽能可以达到非常30Gbps的硬件极限,这种性能提升非常明显。

总结:

通过本篇文章,你可以了解到如何通过Fluid使用分层的数据缓存亲和调度,并根据真实场景进行亲和性自定义的配置,通过调度提升数据访问的性能。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
3月前
|
存储 缓存 分布式计算
亿级数据如何分钟级别写入缓存?
亿级数据如何分钟级别写入缓存?
22 0
|
8天前
|
存储 缓存 算法
使用Java实现高效的数据缓存系统
【2月更文挑战第3天】在大规模的应用程序中,数据缓存是提高应用程序性能的一种重要方法。本文介绍了如何使用Java实现高效的数据缓存系统。我们将讨论缓存的设计原则和缓存算法的选择,同时详细说明如何使用Java内置的缓存库和其他开源工具来构建一个可靠、高效的数据缓存系统。
|
2月前
|
canal 缓存 关系型数据库
Springcloud Alibaba使用Canal将Mysql数据实时同步到Redis保证缓存的一致性
canal [kə'næl] ,译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。其诞生的背景是早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。
|
6月前
|
缓存 达摩院 Kubernetes
数据缓存系列分享(六):通义千问Qwen-14B大模型快速体验
阿里达摩院近期对通义千问大模型 Qwen-14B 进行了开源(之前开源的是Qwen-7B模型),目前在ModelScope和HuggingFace上均可直接下载。关于Qwen-7B的搭建可以参考我们之前的文章:数据缓存系列分享(五):开源大语言模型通义千问快速体验版,本文将使用一样的方式打开Qwen-14B,快速体验一下。
1046 0
数据缓存系列分享(六):通义千问Qwen-14B大模型快速体验
|
3月前
|
SQL 缓存 分布式计算
Apache Zeppelin系列教程第九篇——Zeppelin NoteBook数据缓存
Apache Zeppelin系列教程第九篇——Zeppelin NoteBook数据缓存
77 0
|
3月前
|
缓存 Java 关系型数据库
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
45 0
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
|
4月前
|
SQL 存储 缓存
Flink CDC中flink sql 如果缓存起来所有的数据,然后基于这个数据做查询?
Flink CDC中flink sql 如果缓存起来所有的数据,然后基于这个数据做查询?
36 1
|
4月前
|
存储 缓存 小程序
微信小程序数据交互和缓存
前言: 在开发微信小程序时,数据交互和缓存是非常重要的方面。本文将介绍如何进行数据交互并有效地使用缓存来提高小程序的性能和用户体验。
111 0
|
4月前
|
缓存 NoSQL Java
springboot 启动加载数据库数据到redis缓存
springboot 启动加载数据库数据到redis缓存
|
4月前
|
缓存 NoSQL 数据可视化
c#如何将数据缓存至Redis
.netcore c# 如何将数据库查询的数据缓存至Redis
34 0