Hulu是美国领先的互联网专业视频服务平台,目前在美国拥有超过2000万付费用户。Hulu总部位于美国洛杉矶,北京办公室是仅次于总部的第二大研发中心,也是从Hulu成立伊始就具有重要战略地位的分支办公室,独立负责播放器开发,搜索和推荐,广告精准投放,大规模用户数据处理,视频内容基因分析,人脸识别,视频编解码等核心项目。
在视频领域我们有大量的视频转码任务;在广告领域当我们需要验证一个投放算法的效果时,我们需要为每种新的算法运行一个模拟的广告系统来产出投放效果对比验证;在AI领域我们需要对视频提取帧,利用一些训练框架产出模型用于线上服务。这一切都需要运行在一个计算平台上,Capos是Hulu内部的一个大规模分布式任务调度和运行平台。
Capos是一个容器运行平台,包含镜像构建,任务提交管理,任务调度运行,日志收集查看,Metrics收集,监控报警,垃圾清理各个组件。整个平台包含的各个模块,如下图所示:
用户可以在界面上创建镜像描述符,绑定GitHub的repo,生成镜像。之后在界面上创建作业描述符,填上镜像地址,启动参数,资源需求,选择资源池,就可以运行作业,看作业运行日志等。这些所有操作也可以通过REST API来调用 ,对于一些高级的需求,Capos提供Golang和Python的SDK,可以让用户申请资源,然后启动作业,广告系统就是利用SDK,在Capos上面申请多个资源,灵活的控制这些资源的生命周期,一键启动一个分布式的广告系统来做模拟测试。
Capos大部分组件都是用Golang实现的,Capos的核心组件,任务调度运行CapScheduler是今天主要和大家分享和探讨的模块。CapScheduler是一个基于Mesos的Scheduler,负责任务的接收,元数据的管理,任务调度。CapExecutor是Mesos的一个customized executor,实现Pod-like的逻辑,以及pure container resource的功能,在设计上允许Capos用户利用Capos SDK复用计算资源做自定义调度。
Capos Scheduler的架构图如下所示:
上图浅蓝色部分是Mesos的组件,包括Mesos master,Mesos agent,Mesos zookeeper。Mesos作用是把所有单体的主机的资源管理起来,抽象成一个CPU、Memory、Port、GPU等的资源池,供之上的Capos scheduler使用。
其中Capos scheduler是一个active-standy的HA模型,在scheduler中我们实现了一个raft based的k-v用来存储Metadata,active的scheduler注册成为Mesos之上的一个framework,可以收到资源,根据调度策略来启动作业。
Capbox是一个定制实现的Mesos的executor,作为Mesos agent的资源的占位符,接收请求与Mesos agent上的Docker daemon通信启动容器。其中也实现了POD-like的功能,同时可以启动多个容器共享network,磁盘等。
Capos scheduler提供两类作业运行,一个是简单作业直接在Capbox运行,另一个是复杂带有编程语义的作业,我们称之为AppMaster,其本身运行占用一个CapBox,然后通过编程语义二次申请CapBox运行作业。
首先说明下简单作业运行流程,这里的简单作业,提交的作业通过json描述,可以包含多个Container,然后scheduler收到请求之后,命中某个offer,向Mesos发送offer启动请求,在请求中同时夹带着作业json信息,把作业启动起来,scheduler根据Mesos状态同步信息来控制作业的生命周期。
如果是AppMaster Programmatically二次调度的作业,首先需要把AppMaster启动,这部分和简单作业运行是一致的,然后AppMaster再申请一个到多个资源来启动CapBox,运行作业。此时AppMaster申请的CapBox的生命周期完全由AppMaster决定,所以这里AppMaster可以复用CapBox,或者批量申请CapBox完成自己特定的调度效果。多说一句,AppMaster可以支持client-mode和cluster-mode,client-mode是指AppMaster运行在集群之外,这种情况适用于把AppMaster嵌入在用户原先的程序之中,在某些场景更符合用户的使用习惯。
说完Capos的使用方式后,我们可以聊下在Capos系统中一些设计的思考:
1、Scheduler的调度job和offer match策略,如下图所示:
缓存offer。当scheduler从Mesos中获取offer时候,Capos scheduler会把offer放入到cache,offer在TTL后,offer会被launch或者归还给Mesos,这样可以和作业和offer的置放策略解耦。
插件化的调度策略。Capos scheduler会提供一系列的可插拔的过滤函数和优先级函数,这些优先级函数对offer进行打分,作用于调度策略。用户在提交作业的时候,可以组合过滤函数和优先级函数,来满足不同workload的调度需求。
延迟调度。当一个作业选定好一个offer后,这个offer不会马上被launch,scheduler会延迟调度,以期在一个offer中match更多作业后,再launch offer。获取更高的作业调度吞吐。
2、Metadata的raft-base key value store
多个scheduler之间需要有一个分布式的kv store,来存储作业的Metadata以及同步作业的状态机。在scheduler downtime切换的时候,新的scheduler可以接管,做一些recovery工作后,继续工作。
基于Raft实现的分布式一致性存储。Raft是目前业界最流行的分布式一致性算法之一,Raft依靠leader和WAL(write ahead log)保证数据一致性,利用Snapshot防止日志无限的增长,目前Raft各种语言均有开源实现,很多新兴的数据库都采用Raft作为其底层一致性算法。Capos利用了etcd提供的raft lib, 实现了分布式的一致性数据存储方案。etcd为了增强lib的通用性,仅实现了Raft的核心算法,网络及磁盘io需要由使用者自行实现。Capos中利用etcd提供的rafthttp包来完成网络io,数据持久化方面利用channel并行化leader的本地数据写入以及follower log同步过程,提高了吞吐率。
Capos大部分的模块都是Golang开发,所以目前的实现是基于etcd的raft lib,底层的kv存储可以用BoltDB,Badger和LevelDB。有些经验可以分享下,在调度方面我们应该关注关键路径上的消耗,我们起初有引入StormDB来自动的做一些key-value的index,来加速某些带filter的查询。后来benchmark之后发现,index特别在大规模meta存储之后,性能下降明显,所以目前用的纯kv引擎。在追求高性能调度时候,写会比读更容器达到瓶颈,BoltDB这种b+ tree的实现是对读友好的,所以调度系统中对于kv的选型应该着重考虑想LevelDB这种lsm tree的实现。如果更近一步,在lsm tree基础上,考虑kv分离存储,达到更高的性能,可以考虑用badger。不过最终选型,需要综合考虑,所以我们底层存储目前实现了BoltDB、Badger和LevelDB这三种引擎。
3、编程方式的AppMaster
简单的作业可以直接把json描述通过REST API提交运行,我们这边讨论的是,比较复杂场景的SaaS,可能用户的workload是一种分布式小系统,需要多个Container资源的运行和配合。这样需要Capos提供一种编程方式,申请资源,按照用户需要先后在资源上运行子任务,最终完成复杂作业的运行。
我们提供的编程原语如下:
Capbox.go capbox是Capos中资源的描述:
AppMaster可以用这些API申请资源,释放资源,获取资源的状态更新,在此基础上可以实现灵活的调度。
Task.go task也就是可以在Capbox上运行的task,如下图所示:
在资源基础上,appmaster可以用api启动/停止作业,appmaster也可以复用资源不断的启动新的作业。基于以上的api,我们可以把广告模拟系统,AI框架tensorflow,xgboost等分布式系统运行在Capos之上。
4、Capos对比下Netflix开源的Titus和Kubernetes
Netflix在今年开源了容器调度框架Titus,Titus是一个Mesos framework,titus-master是基于fenso lib的Java based scheduler,meta存储在cassandra中。titus-executor是Golang的Mesos customized executor。因为是Netflix的系统,所以和AWS的一些设施是绑定的,基本上在私有云中不太适用。
Kubernetes是编排服务方面很出色,在扩展性方面有Operator,Multiple Scheduler,CRI等,把一切可以开放实现的都接口化,是众人拾柴的好思路,但是在大规模调度短作业方面还是有提升空间。
Capos是基于Mesos之上的调度,主要focus在大规模集群中达到作业的高吞吐调度运行。
在分布式调度编排领域,有诸多工业界和学术界的作品,比如开源产品Mesos,Kubernetes,YARN,调度算法Flow based的Quincy,Firmament。在long run service,short term workload以及function call需求方面有Service Mesh,微服务,CaaS,FaaS等解决思路,私有云和公有云的百家争鸣的解决方案和角度,整个生态还是很有意思的。绝技源于江湖、将军发于卒伍,希望这次分享可以给大家带来一些启发,最后感谢Capos的individual contributor(字母序):chenyu.zheng、fei.liu、guiyong.wu、huahui.yang、shangyan.zhou、wei.shao。
Q&A
Q:Capos如何处理健康检查?之前了解到,Mesos内置的健康检查不是特别完善。
A:目前Capos focus的作业大部分都是短作业类型,所以我们目前就是通过容器的退出码来判断success或者fail,如果你说的健康检查是针对服务的,一般实现是支持多种健康检查的方式,bash,http等,然后为了大规模容器运行情况下的可用性,建议这种健康检查的发起client和服务instance是在一台机器上,或者是一个Pod中,发现不健康通过某种机制上报,或者退出Container,但是需要控制Threshold以免整个服务downtime。这样可以探测instance的健康,整个服务的健康,可以在通过外部的一些子系统去check。
Q:关于调度方面,分享中只提到了使用了一系列的可插拔的过滤函数和优先级函数,我想问下能否具体描述下如何被调度的?和yarn里使用的Fair Schedule或者DRF算法的异同有哪些?因为对于多种资源维度的调度是一个很复杂的问题,希望知道Hulu这方面有什么心得和思考?
A:目前实现是,会针对一个请求,首先根据过滤函数比如一些constraints进行offer过滤,然后剩下的offer apply所有的优先级打分函数,进行打分,打分的时候,会根据一个请求和offer的资源,算CPU和mem的比例,选取出dominate的resource进行主要评分,然后选取最优的offer进行bind,bind之后不会马上调度,而是会delay scheduler,这样一般在比较繁忙的情况下,一次offer launch可以启动多个tasks,这是对于大规模吞吐的考虑。 以上这些实现还是queue-base的调度,借鉴了一些Fair Schedule和drf的思路,具体差别你了解了Capos scheduler策略后,应该就会有自己的想法了。多种资源维度,目前我们是根据dominate resource作为主要评分标准的,当然你也可以看下我最后分享提到的一些flow-base的scheduler算法,比如firmament。希望可以回答你的问题。
Q:Capos是否支持,数据中心之间的备份/切换。比如Zone - A的数据中心出现网络故障,把服务迁移到另一个指定的区域 Zone - B(仍然考虑恢复以后优先部署到 Zone - A)。之前考虑是类似一个Mask的机制,如果故障就加一定的Mask值(比如Opcacity)在某个集群上,然后调度的时候去参考这个Mask值,不知道Hulu有没有类似的需求或者考虑过这样的机制?
A:Capos是on Mesos,Mesos是根据zk做选主,而且Capos scheduler中还有一个raft base key value store,所以这些条件,使得Capos是一个datacenter的解决方案。目前Hulu是有多个DataCenter的,所以看架构组件图,你可以看到,我们有一个Capos portal,在这个组件下,是可以选择不同DataCenter去run workload。所以我们目前对于数据中心的备份和切换,主要是依赖Capos portal这个组件,在Gateway的位置做的控制。
Q:想请问下Capos的鉴权是怎么做的,有没有用户权限认证系统?此外,针对每个用户有没有容器资源使用量的限制?
A:可以翻到之前share的架构组件图,我们有一个Capos portal组件,这个组件是提供Restful API和Portal,我们在这边集成Hulu SSO,然后关联Hulu yellowpages(Hulu的服务权限控制系统),做的用户的认证,我们分成自己的Capos APP, team的APP,别的组无法操作不属于自己的Capos APP。对于Quota的管理,我们做了Queue/Label机制,每个服务会建一个标识,然后在标识底下配置总的资源使用量,以及可以用的机器列表(通配符),用这样的机制控制Capos的用户资源使用。
原文发布时间为:2018-07-20
本文作者:杨华辉
本文来自云栖社区合作伙伴“Golang语言社区”,了解相关信息可以关注“Golang语言社区”