重新定义容器化 Serverless 应用的数据访问
作者:车漾、刘奖、景奇
随着 IT 基础设施从物理机到虚拟机,再到以 Kubernetes 为代表的容器环境,甚至到 Serverless 的逐渐演进,今天的计算和应用的形态以一日千里的速度发展。尤其是 AI 和数据分析等数据智能应用的 Serverless 化已经成为了一种趋势。 Gartner 曾预测,到 2023 年 70% 的 AI 应用将基于容器和 Serverless 技术开发。这是大家相信的未来,但应用 Serverless 化之路充满艰辛,比如现有应用迁移门槛高,供应商锁定,基础设施响应能力不足,可观测性欠缺,存储接入繁琐等。
这些都是用户对于 Serverless 基础平台提供商的刚性需求,谁能更加开放,谁能更好提供 Serverless 应用所运行的基础设施和生态系统,谁才能成为客户的最终选择,而不仅仅是谁提供的计算资源弹性能力更强或者成本更低,这实际是云计算巨头的内功较量。
(图片来源于网络)
本文首先聚焦到 AI 和大数据等应用 Serverless 化的最大挑战:计算和存储分离架构带来的数据访问延迟和远程拉取数据带宽巨大的挑战。尤其在 GPU 深度学习训练场景中,迭代式的远程读取大量训练数据方法会严重拖慢 GPU 计算效率。
Serverless 的数据访问挑战
Serverless 云平台对用户提供的核心能力就是极致弹性,但是这里讨论的弹性并不只是资源弹性,而是站在用户视角的应用弹性或者说业务弹性,即从用户决定扩容开始到应用真正就绪提供服务的端到端时间。资源弹性时间是其中很重要的部分,计算资源能够实现秒级,甚至毫秒级扩容,相关基础设施就很快会面临巨大的压力,其中最常见的基础设施就是存储。如果存储系统的 IO 吞吐能力跟不上实例变化的速度,例如对于业务来说,容器实例 2 秒就完成了扩容,但还需要花几十秒钟甚至几分钟等待从存储下载数据,那么极致弹性也就无从谈起。
应该说 Serverless 容器化的技术体系对于传统的存储系统提出了新的挑战:
1. 高密访问:
计算资源无存储能力,数据完全下沉到存储系统,导致并发数据访问压力陡增。这一方面影响存储系统的稳定性,另一方面也会打满存储系统服务带宽。
2. 网络延时:
计算存储分离架构拉长了存储访问链路,跨网络通信数据和元数据访问的额外延迟。
3. IO 吞吐能力的扩容成本高:
传统分布式存储带宽与吞吐仅和数据使用容量成正比,而 Serverless 平台会创建大量的容器并发的访问存储系统的数据,会触发存储系统访问限流。这就造成计算资源极致弹性与存储系统有限带宽之间的矛盾。
换而言之,Serverless 的大数据和 AI 场景需要数据分流(Data Offloading),缩短数据读取链路和提供弹性的 IO 吞吐能力。但是考虑到现有云存储系统的稳定性,成本和通用性,这个架构演进会是一个任重道远的工作。
Fluid:应用使用数据过程的抽象
Fluid 是云原生计算基金会(CNCF)旗下的官方沙箱开源项目。该开源项目定位于面向云原生场景下的数据密集型应用加速和编排,社区主要开发者和维护者来自阿里巴巴、南京大学等多家知名企业和高校。Fluid 的思路是将存储系统的能力进行分层,分解为数据存储和数据访问能力,并且将一部分的数据访问能力向上平移到计算集群中;并且在大数据(AI)场景下,对“计算任务使用数据的过程”进行抽象,提出了弹性数据集 Dataset 的概念,并将数据分布式缓存技术与云原生自动弹性(Autoscaling),可迁移(Portablity),可调度(Scheduling)能力相结合。
Fluid 项目地址:
https://github.com/fluid-cloudnative/fluid
数据访问能力向上平移的好处是可以利用计算集群中闲置的资源,通过分布式数据缓存能力一方面提供数据分流降低中心存储压力,另一方面利用数据本地性,VPC 网络性能优势和数据访问复用的特性,缩短数据访问路径,提升性能;通过 Dataset 的抽象,在特定语境下,根据应用的需求定义数据访问的范围,特征以及弹性,并且根据这些特性进行特定性能优化。
All problems in computer science can be solved by another level of indirection
实际上 Fluid 还是遵循计算科学中的所有问题都可以通过增加一层抽象来解决的原则,在 Kubernetes 为代表的计算集群提供数据访问编排层 Dataset 抽象, 实现 Dataset 管理(CRUD 操作)、数据预热,数据备份/恢复权限控制和访问加速等能力,Fluid 的架构可以通过 CacheRuntime plugin 的方式,扩展兼容多种分布式缓存服务。目前已经支持了阿里云 EMR 的 JindoFS,开源的 Alluxio,Juicefs 等缓存引擎, 而对于用户来说这是一个透明的体验。
没有银弹,Fluid 的实质是利用计算集群的空闲资源(CPU,Memory,Disk)和特定场景的抽象假设简化问题,通过数据分流(Data Offloading)降低中心存储的压力;就近缓存(Tiered Locality Cache)和亲和性调度(Cache Locality Scheduling)提升数据访问性能;在计算资源高并发访问数据的时候,通过自动化扩容缓存集群提供弹性 IO 吞吐能力。
在此基础上,Fluid 支持数据弹性和可操作性,能够根据计算吞吐需求实现:
- 数据集的可伸缩性——Fluid 支持 Runtime 弹性伸缩的模式,通过控制缓存 worker 的数量实现弹性伸缩。
- 数据集合的可操作性——Fluid 支持通过 Dataload CRD 来进行多种模式的预热,包括指定文件列表、文件夹的元数据进行预热。
从下到上优化,Fluid 拥抱阿里云 ECI
因此如何将分布式缓存引擎落地到 Serverless 容器化平台就成了一个挑战。而其中的关键问题是如何能够安全,开放,灵活的在 Serverless 容器化平台中运行。将 Fluid 开源软件与 Serverless 平台相结合加速应用访问数据性能在业界是第一次尝试,需要双方相向而行,共同定义协作的界面和权责, 否则改变不会发生。Fluid 需要尊重 Serverless 平台的概念模型和生命周期(比如一个 Pod 占用一个虚拟机的模式),Serverless 平台需要提供基于一定标准的最小开放性。
这背后主要有三个问题:
1. 协议与分工界面不清:Serverless 平台是黑盒系统,运行时支持都是由平台提供商开发的,对于 Kubernetes 生态的支持并不完善。比如多数 Serverless 不支持社区广泛支持的 CSI Plugin、Device Plugin 机制等。传统的方式是将各种存储系统客户端注入 Serverless Pod 运行的虚拟机镜像,但这就要面对虚拟机镜像管理就要面对绑定严重,升级困难,镜像臃肿等问题,同时软件问题排查时边界不清。
2. 安全风险和开放性的平衡:Serverless 平台一定是多租户的,因此平台安全性是生命线。但是 Serverless 赋能更多场景,就需要支持更多的开源软件,而这时一定会引入更多个攻击面;因此需要仔细的权衡二者之间关系。这非常考验平台的硬核实力,也需要软件供应链的控制能力。
3. 分布式与 Serverless 之间的冲突: Serverless 应用假设执行的是简单的、独立的任务,相互间不通信,同时计算任务完全无状态;生命周期管理也要差异。 然而,Fluid 本身是要通过分布式缓存系统实现提速的。这里涉及到的分布式(网络通信),有状态(缓存),生命周期一致性,又是一个矛盾点。
为了支持 Serverless 场景,阿里云容器服务团队,基础软件和操作系统团队,弹性计算 ECI 团队,数据湖存储团队联合作战,提供了从底到顶的完整解决方案。解决问题的基本思路是对不同的问题分而治之,而原则是:
- 拥抱已有标准,比如通过 Kubernetes 中 Sidecar,Device Plugin 等标准作为开放接口。保障用户一致性体验。
- 精细化 Linux 特权控制
- 关注点分离,让不同的角色关注自己的能力,存储和计算可以并行演进。
最终的方案是:通过 Sidecar 机制透明替换 Perstistent Volume Claim,并且对于现有 FUSE 方案 进行安全加固;同时将分布式缓存和弹性能力运行在 ServerFull 节点(ECS);另外针对包含 Sidecar 的任务应用进行特殊的支持。
价值
开放标准
对于存储能力提供方,存储组件无需与 Serverless 平台直接对接,由开源的 Fluid 扮演适配角色,标准公开同时降低计算平台维护存储客户端的复杂度。对于用户来说,以 sidecar 模式运行存储客户端,云上和云下使用体验基本一致, 同时无需担心平台绑定。
关注点分离
关注点分离是 Fluid 用来解决存储能力提供者与 Serverless 平台团队缺乏责任分层的困境。
对于存储能力提供团队,不需要将存储客户端以安装包的方式加载到虚拟机,这样就可以独立迭代自身能力,无需依赖 Serverless 平台团队虚拟机镜像发布节奏;同时问题排查时也无需等待 Serverless 平台团队收集日志,提升了问题诊断和修复的效率。
对于 Serverless 平台团队,存储组件作为 sidecar 存在,减小的虚拟机镜像中软件的复杂度和大小,同时也避免了频发发布虚拟机镜像引入的稳定性风险,降低了问题排查的复杂度。
弹性
可以与分布式存储相结合,通过用户使用自身计算集群的资源,按需的实现吞吐能力扩容。一方面为用户提供了弹性按 IO 需求付费的选择,也降低了存储的成本压力。
安全
通过精细化特权控制,实现 Serverless 下一定的程度实现能力分层,在开放的前提下为底层平台提供安全保障。
可观测
传统存储客户端以进程方式运行在 Serverless 平台中,健康状态和资源消耗都是黑盒;存储客户端问题排查也需要依赖 Serverless 平台运维,而新的模式下,存储客户端以容器的形态运行,提供了完整的可观测性。
快速体验
该实验模拟模型推理服务启动时下载并且初始化模型。
前置条件
- 拥有一个阿里云容器服务 ACK Pro 集群,并且 K8s 版本号≥1.18
- 集群中有虚拟节点,如无可参考 ACK Virtual Node 安装,详见:https://help.aliyun.com/document_detail/118970.htm
- 开通云原生 AI 套件,并安装升级 Fluid 到最新版本,详见:https://help.aliyun.com/document_detail/201997.html
操作步骤
1. 查看 Fluid 是否安装成功以及版本是否是最新:
$ helm list -n fluid-system
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
ack-fluid fluid-system 1 2022-08-13 16:05:47.540883158 +0800 CST deployed ack-fluid-0.8.1 0.8.0-50edf67
2. Fluid 依赖 Webhook 机制注入 Serverless 依赖的 sidecar
$ kubectl label namespace default fluid.io/enable-injection=true
3. 检查 namespace 的标签是否成功打开
$ kubectl get namespace default --show-labels
NAME STATUS AGE LABELS
default Active 5h28m fluid.io/enable-injection=true
4. 创建数据集和缓存运行时
本示例使用 JindoFS 作为数据集的缓存引擎并挂载一个 OSS Bucket,OSS Bucket 中存储着应用需要访问的数据集。该 OSS Bucket 位于北京地域,Bucket 名为 large-model-sh。在实际使用中,请根据实际使用的 OSS Bucket 属性进行修改。
4.1 创建 Secret 加密 Access Key
$ cat << EOF > secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: access-key
stringData:
fs.oss.accessKeyId: <YOUR_ACCESS_KEY_ID>
fs.oss.accessKeySecret: <YOUR_ACCESS_KEY_SECRET>
EOF
4.2 上述例子中的 Access Key 可在 RAM 访问控制页面查询
$ kubectl create -f secret.yaml
4.3 创建 Dataset 和 JindoRuntime
$ cat << EOF > dataset.yaml
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: serverless-data
spec:
mounts:
- mountPoint: oss://large-model-sh/
name: demo
path: "/"
options:
fs.oss.endpoint: oss-cn-beijing-internal.aliyuncs.com
encryptOptions:
- name: fs.oss.accessKeyId
valueFrom:
secretKeyRef:
name: access-key
key: fs.oss.accessKeyId
- name: fs.oss.accessKeySecret
valueFrom:
secretKeyRef:
name: access-key
key: fs.oss.accessKeySecret
---
apiVersion: data.fluid.io/v1alpha1
kind: JindoRuntime
metadata:
name: serverless-data
spec:
replicas: 1
tieredstore:
levels:
- mediumtype: MEM
path: /dev/shm
quota: 10Gi
high: "0.95"
low: "0.7"
EOF
相关参数:
4.4 执行以下步骤
$ kubectl create -f dataset.yaml
4.5 查看 Dataset 状态:
$ kubectl get dataset serverless-data
NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE
serverless-data 1.16GiB 0.00B 10.00GiB 0.0% Bound 80s
4.6 对应会创建 pvc
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
serverless-data Bound default-serverless-data 100Gi ROX fluid 91s
5. 创建基于 ECI 的 Deployment 访问数据集
$ cat << EOF > serving.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: model-serving
spec:
selector:
matchLabels:
app: model-serving
template:
metadata:
labels:
app: model-serving
alibabacloud.com/fluid-sidecar-target: eci
alibabacloud.com/eci: "true"
annotations:
k8s.aliyun.com/eci-use-specs: ecs.s6-c1m2.xlarge
spec:
containers:
- image: fluidcloudnative/serving
name: serving
ports:
- name: http1
containerPort: 8080
env:
- name: TARGET
value: "World"
volumeMounts:
- mountPath: /data
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: serverless-data
EOF
- labels中alibabacloud.com/eci: "true"表示该容器以 ECI 容器方式启动
- labels 中 alibabacloud.com/fluid-sidecar-target "eci"告知 Fluid 该 Pod 以 Serverless 方式启动,Fluid 会利用 Webhook 自动向应用 Pod 注入 Fuse 容器,让应用 Pod 可通过 POSIX 接口访问数据集
- annotations 中的 k8s.aliyun.com/eci-use-specs 表示使用的 ECI 实例规格
6. 创建 Deployment
$ kubectl create -f serving.yaml
7. 查看启动日志,可以看到启动加载数据的时间是 64s
$ kubectl logs model-serving-546578c447-5x6fm -c serving
Begin loading models at 16:35:38
real 1m4.999s
user 0m0.000s
sys 0m1.143s
Finish loading models at 16:36:43
2022-08-13 16:36:43 INFO Hello world sample started.
8. 删除该服务
$ kubectl delete -f serving.yaml
9. 预热缓存数据, 该步骤不是必须执行的操作
$ cat<<EOF >dataload.yaml
apiVersion: data.fluid.io/v1alpha1
kind: DataLoad
metadata:
name: serverless-data-warmup
spec:
dataset:
name: serverless-data
namespace: default
EOF
执行 Dataload, 并查看缓存效果。可以看到 1.2G 数据已经完全缓存了。
$ kubectl create -f dataload.yaml
dataload.data.fluid.io/serverless-dataload created
$ kubectl get dataload
NAME DATASET PHASE AGE DURATION
serverless-dataload serverless-data Complete 2m43s 34s
$ kubectl get dataset
NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE
serverless-data 1.16GiB 1.16GiB 10.00GiB 100.0% Bound 19m
10. 再次创建该服务
$ kubectl create -f serving.yaml
此时查看启动时间发现当前启动加载数据的时间是 6.263s,变成没有预热的情况下耗时的 1/10,速度提升了 10 倍
kubectl logs model-serving-546578c447-pkcpf -c serving
Begin loading models at 17:18:54
real 0m6.263s
user 0m0.000s
sys 0m0.998s
Finish loading models at 17:19:00
2022-08-13 17:19:04 INFO Hello world sample started.
更多使用方法请参考文档:
https://help.aliyun.com/document_detail/440049.html
展望
Fluid 与 Serverless 场景的结合只是刚刚开始,和 Serverless 同行(With the Serverless),与 Serverless 协作(On the Serverless),服务好 Serverless(For the Serverless)。 在第一阶段,我们与 JindoFSX Runtime 一起实现了 Serverless 无缝对接,后续我们也会支持开源社区的 JuiceFS,Alluxio;同时我们在内核和容器层面的修改也会贡献到社区,一起推动 Serverless 平台的改变。
致谢
Fluid 支持 Serverless 的工作感谢来自阿里云,南京大学,Juicedata,哔哩哔哩的小伙伴们共同努力,这是 Serverless 支持数据场景的第一步,我们一起加油来让云的世界更加美好。如果您有兴趣也可以钉钉搜索群号:32850151,加入 Fluid 开源社区技术交流群。如果您认为 Fluid 有用的话,欢迎与感谢您给 Fluid 项目一个 star。
Fluid 的代码仓库地址为:
https://github.com/fluid-cloudnative/fluid
作者简介:
车漾,阿里云容器服务高级技术专家
刘奖,阿里云操作系统部资深技术专家
景奇,阿里云弹性计算技术专家
如何高效调度 AI 和大数据作业?怎样提高 GPU 等资源效率和弹性?快来尝试 ACK 云原生 AI 套件吧!基于标准 Kubernetes,提供组件化、可扩展、可灵活组合和定制的云原生 AI 能力,全栈优化 AI 性能、效率和成本,助力企业级用户快速定制化构建 AI 平台。戳此处链接领取 2022 年免费体验席位!