王尘宇王尘宇

研究百度干SEO做推广变成一个被互联网搞的人

机器学习平台——资源优化之路

资源申请流程

1.6 任务分级
我们的训练任务大致分为两类,一类是探索型实验,一类是线上实验,其中线上实验由根据所承载流量比例分为小流量实验和主流量实验。顾名思义,按重要程度来排序,主流量实验 > 小流量实验 > 探索型实验。
于是,我就把任务按照重要程度划分成三个等级,优先级越高,可申请资源的buffer越多,QoS也越高。这样就达到了节省资源和保证任务稳定性的双重目标。

1.7 资源分组
每个人都想尽可能多启动一些实验,从而更快地验证模型效果,但资源是有限的,如果一部分人占用资源太多,其他人就会陷入无资源可以的境地,这就造成经常需要我们出面去协调资源。所以,在各组之间实行资源隔离就显得很有必要。
于是,我们就按照业务对人员进行分组,并与各组负责人开会确定各组所需资源数量,然后按比例给各组分配资源数量。如果某组资源耗尽,可以通过组内线下协调的方式腾挪资源,我们也就获得了解放。

2. 技术方案

2.1 Merge调度
机器学习训练任务每10分钟会生成一个增量模型,这个增量模型(inc)中只包含近10分钟样本训练的结果,模型文件很小。每隔10小时左右会把近段时间的所有增量模型与之前的全量模型(full)进行合并(Merge)而生成新的全量模型。
当训练生成full模型时(Merge),资源占用率会明显上升,此时训练出错的风险较高。如果将Merge单独运行,则可以明显降低出错风险,并保证资源占用率的平缓和稳定。所以,需要将所有训练的Merge单独运行和调度。

生成full模型时资源占用飙升

之前的做法是给这些 Merge 任务单独搭建了一个有40台机器的 Mesos 集群,然后由训练任务每隔10小时自行调度起其对应的 Merge。由于各个任务互相独立,这就导致经常会有多个 Merge 任务被同时启动的情况出现,因为争抢有限的机器资源而发生死锁,经常需要人工去干预。
于是,我重新设计了一套 Merge 调度方案:

  • 首先,停止训练任务自行启动 Merge,改由 Alpha (我们的机器学习管理平台)来统一定时调度。
  • 其次,设置排队机制,根据一些策略计算出每个 Merge 任务的权重,权重超过阈值的可以进入队列,权重越高,越靠近队列头部。
  • 再者,定时从队列头部取出 Merge 任务并尝试启动。如果当前资源不足,就停止启动并放回队列头部,如果资源充足,就真正启动Merge任务。
  • 最后,定时检查正在运行的 Merge 的运行状态,如果 full 模型已经生成,或者运行超时,则强行停止 Merge 任务,以防任务卡死而无法正常释放资源。

整个过程如下图所示:

Merge调度

这样就彻底解决了因资源争抢而造成的死锁题。最终,只用18台机器就实现了 Merge 任务的正常运行。下图是优化后的效果,可以看到inc和full模型的延迟都非常稳定,整个调度过程完全无需人工干预

优化效果

2.2 使用K8s替代Mesos
Mesos虽然也是一个优秀的容器编排工具,但和 K8s 比起来功能还是太显单薄,特别是在任务调度方面短板很明显。于是,我通过建设 基于K8s的机器学习平台搭建(一) 并逐步通过 “绞杀者模式”实现任务从Mesos向K8s迁移 使我们具备了更稳定的基础架构和更灵活的任务调度能力,这给我们后来的一系列工作带来了巨大的便利。

2.3 真正容器化
在此之前,虽然我们也在使用 Docker 容器化技术,也在使用 Mesos 容器编排工具,但使用的方式并不能真正发挥容器化技术的优势。由于网络模式采用的是host模式(参考 Docker四种网络模式),且所有任务都使用相同的端口,因端口冲突而导致同一台机器上无法部署多个任务
于是,我在任务迁移到 K8s 的同时,借助Istio Ingress + 随机端口相结合的方式(如下图所示)彻底解决了端口冲突的问题,从而实现真正的容器化,使一台机器上运行多个任务变成可能。再结合我们的一系列资源优化手段,使我们的整体资源利用率提升了30%+

Istio Ingress机制

2.4 资源限额
上面我介绍过,之前算法同学对资源是使用是完全凭个人感觉,想用多少就用多少,完全不做限制。这就导致很多实验资源占用量远超实际使用量。
为了实现资源的精确分配,我们通过 Prometheus 来监控各项任务的实际资源使用情况,保留近7天的数据。Alpha定期从 Prometheus 读取这些数据,进行聚合后,保存到数据库中。下次用户重启任务时,我们就根据此来限制任务可申请资源量。当然,为了保证任务的稳定运行,我们会给任务预留出一些buffer。

资源限额

2.5 实验资源利用率排名
安于现状是很多人的天性,想要算法同学主动去配合节省资源存在一定的难度。于是,我们就开发了实验资源利用率排名功能,并定期在周会上通报,从而使算法同学逐步养成起了资源优化的习惯。

资源利用率排名

2.6 个人成本中心
在对实验进行资源利用率排名的同时,我们也将资源使用情况细分到了个人粒度,对个人所有资源的成本折算成现金并排名,并每周发送邮件报表。这样,领导们就能看到每个人所消耗的成本,再结合其所创造的价值,从而无形中给了算法同学优化资源的压力。

个人成本中心

2.7 GPU共享技术
通过GPU共享技术(详见 Kubernetes GPU 共享技术调研 和 Kubernetes GPU共享实践)我们实现了显卡的一卡多用,GPU使用率提升了80%。

2.8 调度方案升级
K8s默认的调度插件是kube-scheduler, kube-scheduler默认会把任务调度到资源最空闲的机器上,而机器学习任务大都是重型任务,不能有效地实现填补“资源碎片”。
于是,我们使用华为开源的Volcano替代了kube-scheduler,从而实现了更合理的任务调度方式。

Volcano 首先要解决的问题就是Gang Scheduling的问题,即一组容器要么都成功,要么都别调度。这个是最基本的用来解决资源死锁的问题,可以很好的提高集群资源利用率(在高业务负载时)。除此之外,它还提供了多种调度算法,例如priority优先级,DRF(dominant resource fairness), binpack等。 我们今天就是挖一挖Volcano内部的各种调度算法实现。

  • Gang Scheduling

这种调度算法,首先就是有’组’的概念,调度结果成功与否,只关注整一’组’容器。

具体算法是,先遍历各个容器组(代码里面称为Job),然后模拟调度这一组容器中的每个容器(代码里面称为Task)。最后判断这一组容器可调度容器数是否大于最小能接受底限,可以的话就真的往节点调度(代码里面称为Bind节点)。

Gang Scheduling

  • DRF(dominant resource fairness)

这种调度算法,主要是Yarn和Mesos都有,而K8S没有,需要补齐。概括而言,DRF意为:“谁要的资源少,谁的优先级高”。因为这样可以满足更多的作业,不会因为一个胖业务,饿死大批小业务。注意:这个算法选的也是容器组(比如一次AI训练,或一次大数据计算)。

DRF(dominant resource fairness)

  • binpack

这种调度算法,目标很简单:尽量先把已有节点填满(尽量不往空白节点投)。具体实现上,binpack就是给各个可以投递的节点打分:“假如放在当前节点后,谁更满,谁的分数就高”。因为这样可以尽量将应用负载靠拢至部分节点,非常有利于K8S集群节点的自动扩缩容功能。注意:这个算法是针对单个容器的。

binpack

从下图可以看到,优化前资源碎片率高达43%:

优化前

优化之后,资源碎片率降低到了30%,并且还在持续自动优化:

优化后

后记

资源优化是一个曲折而艰辛的道路,在这个过程中我们付出了很多努力,得到的成果也是非常丰硕的。虽然目前整体65%的资源利用率在很多人看来好像不是很高,主要是因为机器学习任务大多是重型任务,没有足够的小任务把资源碎片消除干净。后续我们也在考虑和运维同学合作,调度一些微服务到 K8s 集群中,来尽可能的填补资源碎片,那时候资源利用率提升到80%以上应该也不是难事。
最后,希望我们的资源优化方案能起到抛砖引玉的作用,当大家遇到类似的问题时,能给大家提供一些解题思路我就心满意足了。

相关文章

评论列表

发表评论:
验证码

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。