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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器镜像服务 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 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
2月前
|
存储 缓存 分布式计算
亿级数据如何分钟级别写入缓存?
亿级数据如何分钟级别写入缓存?
35 0
|
2月前
|
缓存 JavaScript
vue使用localStorage缓存数据
vue使用localStorage缓存数据
|
2月前
|
缓存 NoSQL 算法
17- 数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?
保证Redis中的20w数据为热点数据,可以通过设置Redis的LFU(Least Frequently Used)淘汰策略。这样,当数据库有1000万数据而Redis仅能缓存20w时,LFU会自动移除使用频率最低的项,确保缓存中的数据是最常使用的。
84 8
|
24天前
|
存储 缓存 测试技术
有效使用缓存时需要缓存动态数据吗?
【6月更文挑战第7天】本文探讨了如何有效地缓存数据以提升应用性能。关键在于选择合适的数据进行缓存和适时缓存。缓存不应被视为永久存储,应同时维护原始数据存储。
76 2
有效使用缓存时需要缓存动态数据吗?
|
26天前
|
缓存 NoSQL 中间件
应对数据库不断膨胀的数据:缓存和队列中间件
【6月更文挑战第5天】该文探讨了优化数据库使用以提升应用系统性能的策略。文中建议利用Redis缓存和MQ消息队列作为辅助工具,以进一步优化性能和减少资源消耗。
23 2
应对数据库不断膨胀的数据:缓存和队列中间件
|
10天前
|
缓存 安全 算法
Go 中使用 map 实现高效的数据缓存
Go 中使用 map 实现高效的数据缓存
|
2月前
|
存储 缓存 运维
【Docker 专栏】Docker 镜像的分层存储与缓存机制
【5月更文挑战第8天】Docker 镜像采用分层存储,减少空间占用并提升构建效率。每个镜像由多个层组成,共享基础层(如 Ubuntu)和应用层。缓存机制加速构建和运行,通过检查已有层来避免重复操作。有效管理缓存,如清理无用缓存和控制大小,可优化性能。分层和缓存带来资源高效利用、快速构建和灵活管理,但也面临缓存失效和层管理挑战。理解这一机制对开发者和运维至关重要。
【Docker 专栏】Docker 镜像的分层存储与缓存机制
|
2月前
|
消息中间件 缓存 监控
中间件如果缓存中存在所需的数据(缓存命中)
【5月更文挑战第12天】中间件如果缓存中存在所需的数据(缓存命中)
37 3
|
2月前
|
存储 缓存 监控
|
2月前
|
存储 缓存 JavaScript
vue中缓存页面数据(刷新不丢失)
vue中缓存页面数据(刷新不丢失)