Hulu是美國領先的互聯網專業視頻服務平臺,目前在美國擁有超過2000萬付費用戶。Hulu總部位於美國洛杉磯,北京辦公室是僅次於總部的第二大研發中心,也是從Hulu成立伊始就具備重要戰略地位的分支辦公室,獨立負責播放器開發,搜索和推薦,廣告精準投放,大規模用戶數據處理,視頻內容基因分析,人臉識別,視頻編解碼等核心項目。java
在視頻領域咱們有大量的視頻轉碼任務;在廣告領域當咱們須要驗證一個投放算法的效果時,咱們須要爲每種新的算法運行一個模擬的廣告系統來產出投放效果對比驗證;在AI領域咱們須要對視頻提取幀,利用一些訓練框架產出模型用於線上服務。這一切都須要運行在一個計算平臺上,Capos是Hulu內部的一個大規模分佈式任務調度和運行平臺。python
Capos是一個容器運行平臺,包含鏡像構建,任務提交管理,任務調度運行,日誌收集查看,metrics收集,監控報警,垃圾清理各個組件。整個平臺包含的各個模塊,以下圖所示:git
用戶能夠在界面上建立鏡像描述符,綁定github的repo,生成鏡像。以後在界面上建立做業描述符,填上鏡像地址,啓動參數,資源需求,選擇資源池,就能夠運行做業,看做業運行日誌等。這些全部操做也能夠經過restapi來調用,對於一些高級的需求,capos提供golang和python的sdk,可讓用戶申請資源,而後啓動做業,廣告系統就是利用sdk,在capos上面申請多個資源,靈活的控制這些資源的生命週期,一鍵啓動一個分佈式的廣告系統來作模擬測試。github
Capos大部分組件都是用Golang實現的,Capos的核心組件,任務調度運行CapScheduler是今天主要和你們分享和探討的模塊。CapScheduler是一個基於mesos的scheduler,負責任務的接收,元數據的管理,任務調度。CapExecutor是mesos的一個customized executor,實現Pod-like的邏輯,以及pure container resource的功能,在設計上容許Capos用戶利用capos sdk複用計算資源作自定義調度。golang
Capos Scheduler的架構圖以下所示:算法
上圖淺藍色部分是mesos的組件,包括mesos master,mesos agent,mesos zookeeper。mesos做用是把全部單體的主機的資源管理起來,抽象成一個cpu,memory,port,gpu等的資源池,供之上的capos scheduler使用。docker
其中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
首先說明下簡單做業運行流程,這裏的簡單做業,提交的做業經過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策略,以下圖所示:
1.1 緩存offer。當scheduler從mesos中獲取offer時候,capos scheduler會把offer放入到cache,offer在TTL後,offer會被launch或者歸還給mesos,這樣能夠和做業和offer的置放策略解耦。
1.2 插件化的調度策略。capos scheduler會提供一系列的可插拔的過濾函數和優先級函數,這些優先級函數對offer進行打分,做用於調度策略。用戶在提交做業的時候,能夠組合過濾函數和優先級函數,來知足不一樣workload的調度需求。
1.3 延遲調度。當一個做業選定好一個offer後,這個offer不會立刻被launch,scheduler會延遲調度,以期在一個offer中match更多做業後,再launch offer。獲取更高的做業調度吞吐。
2 Metadata的raft-base key value store
2.1 多個scheduler之間須要有一個分佈式的kv store,來存儲做業的metadata以及同步做業的狀態機。在scheduler downtime切換的時候,新的scheduler能夠接管,作一些recovery工做後,繼續工做。
2.2 基於raft實現的分佈式一致性存儲。Raft是目前業界最流行的分佈式一致性算法之一,raft依靠leader和WAL(write ahead log)保證數據一致性,利用Snapshot防止日誌無限的增加,目前raft各類語言均有開源實現,不少新興的數據庫都採用 Raft 做爲其底層一致性算法。Capos利用了etcd提供的raft lib (https://github.com/coreos/etcd/tree/master/raft), 實現了分佈式的一致性數據存儲方案。etcd爲了加強lib的通用性,僅實現了raft的核心算法,網絡及磁盤io須要由使用者自行實現。Capos中利用etcd提供的rafthttp包來完成網絡io,數據持久化方面利用channel並行化leader的本地數據寫入以及follower log同步過程,提升了吞吐率。
2.3 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
3.1 簡單的做業能夠直接把json描述經過restapi提交運行,咱們這邊討論的是,比較複雜場景的SaaS,可能用戶的workload是一種分佈式小系統,須要多個container資源的運行和配合。這樣須要capos提供一種編程方式,申請資源,按照用戶須要前後在資源上運行子任務,最終完成複雜做業的運行。
3.2 咱們提供的編程原語以下, Capbox.go capbox是capos中資源的描述
package client import ( "capos/types/server" ) type CapboxCallbackHandler interface { OnCapboxesRunning(*prototypes.Capbox) error OnCapboxesCompleted(*prototypes.Capbox) error } type RecoveryCapboxHandler interface { GetCallbackHandler(string) (CapboxCallbackHandler, error) } type AMSchedulerLifeCycle interface { Start(*prototypes.AMContext) (*prototypes.RegisterAMResponse, error) Stop() error } type AMSchedulerAction interface { // container resource api CreateCapbox(*prototypes.CapboxRequest, CapboxCallbackHandler) (*prototypes.Capbox, error) ReleaseCapbox(string) error PreviousStateRecovery(*prototypes.RegisterAMResponse, RecoveryCapboxHandler) error PreviousStateDrop(*prototypes.RegisterAMResponse) error GetCapAgentsSnapshot() ([]*prototypes.CapAgent, error) ListenCapboxStateChange() StopListenCapboxStateChange() }
appmaster能夠用這些api能夠申請資源,釋放資源,獲取資源的狀態更新,在此基礎上能夠實現靈活的調度。
Task.go task也就是能夠在capbox上運行的task, 以下圖所示
package client import ( "capos/types/server" ) type TaskCallbackHandler interface { OnTaskCompleted(*prototypes.CapTask) error } type RecoveryTaskHandler interface { GetCallbackHandler(capboxId string, taskId string) (TaskCallbackHandler, error) } type AMCapboxLifeCycle interface { Start() error Stop() error } type AMCapboxAction interface { // task management api StartTask(*prototypes.Capbox, *prototypes.CapTaskRequest, TaskCallbackHandler) (*prototypes.CapTask, error) StopTask(*prototypes.Capbox, string) error ListTask(*prototypes.Capbox) ([]*prototypes.CapTask, error) PreviousStateRecovery(*prototypes.RegisterAMResponse, RecoveryTaskHandler) error ListenCapTaskStateChange() StopListenCapTaskStateChange() }
在資源基礎上,appmaster能夠用api啓動/中止做業,appmaster也能夠複用資源不斷的啓動新的做業。基於以上的api,咱們能夠把廣告模擬系統,AI框架tensorflow,xgboost等分佈式系統運行在Capos之上。
4 capos對比下netflix開源的titus和kubernetes
4.1 netflix在今年開源了容器調度框架titus,Titus是一個mesos framework, titus-master是基於fenso lib的java based scheduler,meta存儲在cassandra中。titus-executor是golang的mesos customized executor。由於是netflix的系統,因此和aws的一些設施是綁定的,基本上在私有云中不太適用。
4.2 Kubernetes是編排服務方面很出色,在擴展性方面有operator,multiple scheduler,cri等,把一切能夠開放實現的都接口化,是衆人拾柴的好思路,可是在大規模調度短做業方面仍是有提高空間。
4.3 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。