乐趣区

关于存储:Alluxio-助力-Kubernetes加速云端深度学习

简介: 人工智能是近几年十分炽热的技术畛域,而推动这个畛域疾速演进的原动力包含以英伟达 GPU 为代表的异构算力,以 TensorFlow,Pytorch 为代表的的机器学习框架,以及海量的数据集。除此之外咱们也发现了一个趋势,就是以 Kubernetes 和 Docker 为代表的容器化基础架构也成为了数据科学家的首选,这次要有两个因素:别离是标准化和规模化。比方 TensorFlow,Pytorch 的软件公布过程中肯定蕴含容器版本,这次要是凭仗容器的标准化特点。另一方面以 Kubernetes 为根底的集群调度技术使大规模的分布式训练成为了可能。

作者 | 
车漾  阿里云高级技术专家
范斌  Alluxio 开创成员,开源社区副总裁
起源 | 阿里巴巴云原生公众号

为什么要减速云端深度学习

人工智能是近几年十分炽热的技术畛域,而推动这个畛域疾速演进的原动力包含以英伟达 GPU 为代表的异构算力,以 TensorFlow,Pytorch 为代表的的机器学习框架,以及海量的数据集。除此之外咱们也发现了一个趋势,就是以 Kubernetes 和 Docker 为代表的容器化基础架构也成为了数据科学家的首选,这次要有两个因素:别离是标准化和规模化。比方 TensorFlow,Pytorch 的软件公布过程中肯定蕴含容器版本,这次要是凭仗容器的标准化特点。另一方面以 Kubernetes 为根底的集群调度技术使大规模的分布式训练成为了可能。

背景

首先咱们察看下图,这是模仿数据下的深度学习模型训练速度,所谓模仿数据的意思就是这个测试中没有 IO 的影响。从这个图中咱们能够失去两个发现:

  • GPU 硬件降级的减速效果显著。从单卡的算力看,pascal 架构为代表的 P100 一秒钟只能解决 300 张图片,而 volta 架构的 v100 一秒钟能够解决 1200 张图片,晋升了 4 倍。
  • 分布式训练的也是无效减速的形式。从单卡 P100 到分布式 32 卡 v100,能够看到训练速度晋升了 300 倍。

1. 模仿数据训练速度

而从训练工夫来看,同样的数据,同样的训练指标,单卡 P100 须要 108 个小时,4 天半的工夫。而 V100 的 32 卡分布式训练只须要 1 小时。而从老本上来看,单卡 P100 的老本是靠近 1400 元,而 8 卡 V 100 是 600 元,不到一半。

能够发现,更新的 GPU 硬件岂但会更高效,实际上也会更省钱。这兴许就是黄教主说的买的越多,省的越多。从云资源的角度来说还是有情理的。

2. 模仿数据训练工夫

然而之前的测试后果实际上是做了一些前提假如,就是没有数据延时的影响。而实在的状况下,模型训练是离不开海量数据的拜访。而实际上:

  • 弱小的算力须要与之匹配的数据拜访能力,不论是延时还是吞吐,都提出了更高的需要。上面的图能够看到,在云盘的数据读取的状况下,GPU 的训练速度间接降为了原来的三分之一。GPU 的使用率也很高。
  • 在云环境下,计算和存储拆散后,一旦没有了数据本地化,又显著好转了 I/O 影响。
  • 此时如果可能把数据间接加载到计算的节点上,比方 ossutil 把数据拷贝到 GPU 机器是不是能够满足计算的需要呢。实际上也还是不够的,因为一方面数据集无奈选集管制,另一方面 AI 场景下是全量数据集,一旦引入驱赶机制,实际上性能影响也十分显著。因而咱们意识到在 K8s 下应用分布式缓存的意义。

Alluxio 是什么

Alluxio 是一个面向 AI 以及大数据利用,开源的分布式内存级数据编排零碎。在很多场景底下,Alluxio 非常适合作为一个分布式缓存来减速这些利用。这个我的项目是李浩源博士在加州大学 Berkeley 分校的 AMPLab 攻读博士的时候创建的,最早的名字 Tachyon。AMPLab 也是孵化出了 Spark 和 Mesos 等优良开源我的项目的勋绩实验室。2015 年,由顶级的风险投资 Andreessen Horowitz 投资,Alluxio 我的项目的次要贡献者在旧金山湾区成立了 Alluxio 这家公司。

1. Alluxio – 分布式缓存的领导者

2. Alluxio 的简介

简略看一下在大数据和 AI 生态圈里,Alluxio 处于什么地位。在大数据软件栈里,Alluxio 是新的一层,咱们称之为数据编排层。它向上对接计算利用,比方 Spark,Presto,Hive,Tensorflow,向下对接不同的存储,比方阿里巴巴的 OSS,HDFS。咱们心愿通过这一层新退出的数据编排层,能够让计算和存储之间的强关联解耦。从而让计算和存储都能够独立而更麻利的部署和演进。数据利用能够不用关怀和保护数据存储的具体类型,协定,版本,地理位置等。而数据的存储也能够通过数据编排这一层更灵便更高效的被各种不同利用生产。

3. Alluxio 的外围性能

1)分布式数据缓存

上面介绍一下 Alluxio 的外围性能。Alluxio 最外围的服务就是提供一个分布式的数据缓存用来减速数据利用。对于 Spark,Presto,Tensorflow 等数据密集型的利用,当读取非本地的数据源时,Alluxio 能够通过加载原始数据文件,将其分片以及打散,并存储在凑近利用的 Alluxio 服务器上,加强这些利用的数据本地性。

比方在这个例子里,文件 1 和文件 2 别离被分片后存储在不同的 Alluxio 服务器上,利用端能够就近从存储了对应的数据分片的服务器读取。当利用须要的读入有显著的热数据时,增加缓存层能够显著的节俭资源以及晋升效率。

2)灵活多样的数据拜访 API

Alluxio 的第二个外围利用,是对利用提供不同类型的数据接口,包含在大数据畛域最常见的 HDFS 接口,以及在 ai 和模型训练场景下罕用的 POSIX 规范文件系统接口。

这样同样的数据一旦筹备结束,能够以不同的模式出现给利用,而不必做屡次解决或者 ETL。

3)对立文件系统形象

Alluxio 的第三个外围性能,是把多个不同的存储系统,以对用户通明的形式,对立接入一个文件系统形象中。这样使得简单的数据平台变得简略而易于保护。数据消费者,只须要晓得数据对应的逻辑地址,而不必去关怀底层对接的时候什么存储系统。

举个例子,如果一家公司同时有多个不同的 HDFS 部署,并且在线上接入了 Alibaba 的 OSS 服务,那么咱们齐全能够应用 Alluxio 的挂载性能,把这些零碎接入一个对立的逻辑上的 Alluxio 文件系统形象中。每一个 HDFS 会对应到不同的 Alluxio 目录。

Alluxio 在云端 AI 训练场景的性能益处

介绍完了 Alluxio 的外围性能,让咱们聚焦在云端 AI 训练场景下,再来回顾一下 Alluxio 可能带来的益处。在模型训练场景下内存减速能力满足 GPU 须要的高吞吐。如果通过一般的网络从 Object store 传输数据,大概能撑持 300MB/s,这远远不能达到充沛应用训练资源特地是 GPU 高吞吐的个性。然而一旦利用 Alluxio 构建了一层分布式的数据缓存,负责训练的容器过程和 alluxio worker 容器过程就能够以很高的速率替换数据。比方当两者在同一物理主机上的时候,能够达到 1-6GB 每秒。从其余 alluxioworker 处读取也能够通常达到 1-2GB/s。

此外,通过 Alluxio 能够实现非常简单便捷的分布式缓存治理,比方设置缓存替换策略,设置数据的过期工夫,预读取或者驱赶特定目录下的数据等等操作。这些都能够给模型训练带来效率的晋升和治理的便捷。

Alluxio 在 Kubernetes 上的架构

要在 Kubernetes 中原生的应用 Alluxio,首先就要把它部署到 K8s 中,因而咱们的第一步工作和 Alluxio 团队一起提供一个 Helmchart,能够对立的配置用户身份,参数以及分层缓存配置。

从左图中看,这里 Alluxio 的 master 以 statefulset 的模式部署,这是因为 Alluxiomaster 首先须要稳固,惟一的网络 id,能够应答容灾等简单场景。而 worker 和 Fuse 以 daemonset 的模式部署,并且二者通过 podaffinity 绑定,这样能够应用到数据亲和性。

KubeNode – Remedy Operator

通过将利用实现 helm 化之后,部署它就变成了非常简单的事件,只须要编写 cong.yaml,执行 helminstall 就能够一键式在 Kubernetes 中部署 Alluxio。大家感兴趣的话能够查看 alluxio 文档,或者借鉴阿里云容器服务的文档。

Alluxio 反对 AI 模型训练场景的挑战

在性能评估中,咱们发现当 GPU 硬件从 NVidia P100 降级到 NVidia V100 之后,单卡的计算训练速度失去了不止 3 倍的晋升。计算性能的极大晋升给数据存储拜访的性能带来了压力。这也给 Alluxio 的 I/O 提出了新的挑战。

下图是在别离在合成数据 (Synthetic Data) 和应用 Alluxio 缓存的性能比照,横轴示意 GPU 的数量,纵轴示意每秒钟解决的图片数。合成数据指训练程序读取的数据有程序本身产生,没有 I/O 开销,代表模型训练性能的实践下限; 应用 Alluxio 缓存指训练程序读取的数据来自于 Alluxio 零碎。在 GPU 数量为 1 和 2 时,应用 Alluxio 和合成数据比照,性能差距在能够承受的范畴。然而当 GPU 的数量增大到 4 时,二者差距就比拟显著了,Alluxio 的处理速度曾经从 4981 images/second 降到了 3762 images/second。而当 GPU 的数量达到 8 的时候,Alluxio 上进行模型训练的性能有余合成数据的 30%。而此时通过系统监控,咱们察看到整个零碎的计算、内存和网络都远远没有达到瓶颈。这间接阐明了简略应用 Alluxio 难以高效反对 V100 单机 8 卡的训练场景。

调优策略

1. 缓存元数据缩小 gRPC 交互

Alluxio 不只是一个单纯的缓存服务。它首先是一个分布式虚构文件系统,蕴含残缺的元数据管理、块数据管理、UFS 治理(UFS 是底层文件系统的简称)以及健康检查机制,尤其是它的元数据管理实现比很多底层文件系统更加弱小。这些性能是 Alluxio 的长处和特色,但也意味着应用分布式系统带来的开销。例如,在默认设置下应用 Alluxio 客户端来读一个文件,即使数据曾经缓存在本地的 Alluxio Worker 中,客户端也会和 Master 节点有屡次 RPC 交互来获取文件元信息以保证数据的一致性。实现整个读操作的链路额定开销在传统大数据场景下并不显著,然而深度面对学习场景下高吞吐和低延时的需要就显得顾此失彼了。因而咱们要提供客户端的元数据缓存能力。

2. Alluxio 缓存行为管制

因为深度学习训练场景下,每次训练迭代都是全量数据集的迭代,缓存几个 TB 的数据集对于任何一个节点的存储空间来说都是顾此失彼。而 Alluxio 的默认缓存策略是为大数据处理场景(例如查问)下的冷热数据明显的需要设计的,数据缓存会保留在 Alluxio 客户端所在的本地节点,用来保障下次读取的性能最优。具体来说:

  • alluxio.user.ufs.block.read.location.policy 默认值为 alluxio.client.block.policy.LocalFirstPolicy,这示意 Alluxio 会一直将数据保留到 Alluxio 客户端所在的本地节点,就会引发其缓存数据靠近饱和时,该节点的缓存始终处于抖动状态,引发吞吐和延时极大的降落,同时对于 Master 节点的压力也十分大。因而须要 location.policy 设置为 alluxio.client.block.policy.LocalFirstAvoidEvictionPolicy 的同时,指定 alluxio.user.block.avoid.eviction.policy.reserved.size.bytes 参数,这个参数决定了当本地节点的缓存数据量到肯定的水平后,预留一些数据量来保障本地缓存不会被驱赶。通常这个参数应该要大于节点缓存下限 X(100%- 节点驱赶下限的百分比)。
  • alluxio.user.file.passive.cache.enabled 设置是否在 Alluxi 的本地节点中缓存额定的数据正本。这个属性是默认开启的。因而,在 Alluxio 客户端申请数据时,它所在的节点会缓存曾经在其余 Worker 节点上存在的数据。能够将该属性设为 false,防止不必要的本地缓存。
  • alluxio.user.file.readtype.default 默认值为 CACHE\_PROMOTE。这个配置会有两个潜在问题,首先是可能引发数据在同一个节点不同缓存档次之间的一直挪动,其次是对数据块的大多数操作都须要加锁,而 Alluxio 源代码中加锁操作的实现不少中央还比拟重量级,大量的加锁和解锁操作在并发较高时会带来不小的开销,即使数据没有迁徙还是会引入额定开销。因而能够将其设置为 CACHE 以防止 moveBlock 操作带来的加锁开销,替换默认的 CACHE\_PROMOTE。

3. Fuse 性能调优

1)缩短 FUSE 元数据无效工夫

Linux 中每个关上文件在内核中领有两种元数据信息:struct dentrystruct inode,它们是文件在内核的根底。所有对文件的操作,都须要先获取文件这两个构造。所以,每次获取文件 / 目录的 inode 以及 dentry 时,FUSE 内核模块都会从 libfuse 以及 Alluxio 文件系统进行残缺操作,这样会带来数据拜访的高延时和高并发下对于 Alluxio Master 的微小压力。能够通过配置 –o entry_timeout=T –o attr_timeout=T 进行优化。

2)配置 max_idle_threads 防止频繁线程创立销毁引入 CPU 开销

这是因为 FUSE 在多线程模式下,以一个线程开始运行。当有两个以上的可用申请,则 FUSE 会主动生成其余线程。每个线程一次解决一个申请。解决完申请后,每个线程查看目前是否有超过 max_idle_threads(默认 10)个线程;如果有,则该线程回收。而这个配置实际上要和用户过程生成的 I/O 沉闷数相干,能够配置成用户读线程的数量。而可怜的是  max_idle_threads 自身只在 libfuse3 才反对,而 AlluxioFUSE 只反对 libfuse2,因而咱们批改了 libfuse2 的代码反对了 max_idle_threads 的配置。

总结

在优化 Alluxio 之后,ResNet50 的训练性能单机八卡性能晋升了 236.1%,并且扩展性问题失去了解决,训练速度在岂但能够扩大到了四机八卡,而且在此场景下和合成数据相比性能损失为 3.29%(31068.8images/s vs 30044.8 images/s)。相比于把数据保留到 SSD 云盘,在四机八卡的场景下,Alluxio 的性能晋升了 70.1% (云 SSD 17667.2 images/s vs 30044.8 images/s)。

端到端的优化计划

如果您对通过 Alluxio 在 Kubernetes 中减速深度学习感兴趣,欢送钉钉扫码退出中国社区大群。咱们一起来探讨您的场景和问题。

作者简介

范斌 ,是 Alluxio 的开创成员,已经就任于 Google,晚期负责 Alluxio 的架构设计,当初关注于 Alluxio 开源社区的经营。

车漾 ,就任于阿里云容器服务团队,关注于云原生技术与 AI、大数据场景的联合。

版权申明: 本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

退出移动版