Golang在阿里巴巴调度系统Sigma中的实践
作者簡介
李 雨 前?
花名叫鷹緣
系統軟件事業部調度系統。
關鍵詞
Golang
調度系統
Sigma,阿里巴巴自有的內容
實踐交流
工程
?
1.取材
資源調度領域Sigma
?主要思路是說資源調度領域的Sigma,共性的借鑒性的東西,阿里特有的就不講,更多在Q&A里面。因為涉及到實踐會聚焦工程的問題,所以我會講一些架構設計與語言的選擇,和并發模式下面任務粒度怎么樣去選擇,還有一些比較大型、綜合的解決方案。
2. 工程問題
? 所有的工程問題,突出背后的故事,有幾個線索,第一個線索就是跟規模化相關,阿里的規模很大,背后就要支撐很大的規模。另外就是阿里還有很大一塊就是上云,包括雙十一,很多東西跟云相關。
???另外,我們踩過的坑,針對這些坑的解決方案跟大家分享一下。
? 最后就是在Golang當中的bug,最怕低級錯誤引起的。
???Sigma的業務有兩塊,一塊是對內,一塊是對外,對內是所有的bu都接入了Sigma的系統,我們的規模會有100萬級別。Sigma的內部有一個logo,很容易理解,就是數學求和,Sigma是很大的生態系統,需要很多人和系統配合完成。
???這個業務分了幾個層次,上層業務偏運維,下層業務偏系統,從接入層,到中心的Master,到Slave,到最底會有一個Pouch。
這是sigma的架構
可以看出來,顏色有三塊,一塊是Sigma的,一個是0層,還有一塊是關于Fuxi的,通俗的理解就是Sigma管在線,然后是Fuxi管離線的,中間是一個協調層,這樣理解起來就跟Mesos比較接近一點。這個架構肯定不是最優的,但是它存在有它客觀的原因。
我會從Sigma這邊抽象的四個案例。我會給大家展示是什么的同時,講背后我們的故事。
案例1
???首先看一下APIServer
? 我們平常寫代碼經常會接觸到這些事情,在業務領域、調度領域稍稍不同,背后要做發布、擴容、銷毀、啟停、升級,還有云化,特別是雙十一到阿里云買服務器,所以會有混合云需求。我們規模很大,就會要求所有簡單的事情,怎么樣讓它在規模化的場景下面依然能夠工作。我們調度系統跟運維有關,核心的內容就是怎么樣做到運維友好。還有就是我們做在線的容器服務,肯定要做到高可用,還有一致性。
解決思路
1. 數據一致性
數據的一致性上面我們是用etcd/redis,我們會用一個實時+全量的方式做到數據的一致性
2. 狀態的一致性
想做到很好是很難的,但是我們把狀態的一致性轉為存儲一致性來做,就會降低處理問題的難度。
3. 簡單的
我們沒有追求技術看起來非常完美的方案,先把業務推起來能夠用就好了。
4. 高可用-無狀態
我們要做到前面說的高可用,有幾種方案,一個是多Master結構,還有無狀態,還有就是快速的failover,我們希望做到無狀態。
5. 降級-搶占
規模大了以后就會有一個問題,很多人都要資源,這個時候肯定會有一些稀缺的資源,系統要支持降級搶占。
6. 內外兼容:一個團隊兩塊牌子
要做到上云,所有的思考都要考慮到對內對外是一班人馬,一個團隊會有兩個牌子,這是我們整個出發點的思路。
???思路之后我們選擇由APIServer把前面的發布、擴容等都放在task,丟到Redis里,底層的Worker消費一些任務,做到無狀態,得出來的結論就是架構整體的設計大于語言的選擇。
???數據一致性里面,如果能做到了實時,數據應該就是一致性的,為什么還要加全量來彌補這個過程?背后一個重要的原因就是物理機的硬件或者是軟件會經常出故障,系統多了之后,很難保證故障的事件跟整個鏈路100閉環起來,就導致總有一些地方的數據不知道為什么不一致,有很多硬件、軟件的故障導致實踐的層面做不到一致性,就只好加一個全量彌補。還有另外一種方案不要全量,我們定時同步,但是有一個問題,時間窗口怎么定,定幾分鐘。同步的一瞬間,數據會有一些比對,這個時候加鎖,成本維護會多一點。
? 狀態一致性問題轉為存儲一致性的問題最好的就是轉為存儲一致性,我們為什么說要面向etcd,在很大的系統里面有很多的設備系統,沒有辦法面向事務去做,最好是分布式的存儲把這些問題兜掉。
? 為什么說簡單夠用,后面會有數據給大家看一下,要承載的量很大,沒有辦法做到95%以上才上線,大家等不及,可能做到85%就要上線了。
???還有就是降級,前面我提到了阿里這么多的BU,每一個BU都提了很多的預算,去采購,這就浪費很多資源,這中間有一個共享的buffer,肯定會有搶占,這個在開源里面也會有一些策略。
???內外兼容,如果是創業公司會有體會,一部分東西是基于內部的服務器,還有一些是購買上云的服務,這個時候管理起來,不可能上云的是一套管理方式,自己內部又是一套方式,至少在開發理解上是一模一樣的,在設計和架構的時候要屏蔽很多的差異。基于這樣的思考,我們覺得這種方式相對來說會比較好一點,不然嘗試換其他的方式,問題解決可能會稍微麻煩一點。
???前面講了設計的架構,接下來講的案例是一個真實的場景。
? 我們在阿里要做一次發布,比如說一開始要拉鏡像,就要關閉告警,可能有一些應用要下線了,還要停止應用。然后開始容器升級,還要做業務邏輯的檢查,檢查完之后才把告警打開。不管是上云還是內部,這套流程肯定是公共的,具體對接的時候又不一樣,內部和云上不一樣,云上提供一個SDK,云上的接口的異步性的流轉和內部也不一樣,所以要抽出來。
???內部和阿里云上面都是這樣的事情,阿里云比較特別的是什么呢?在before會做一些處理,我們核心的架構是很固定的,在阿里主流的語言是JAVA,我們討論架構的時候,沒有局限于上來就用JAVA,Golang,或者別的,我們把架構想清楚,之后才選擇用什么樣的開發語言。
???這是我們數據的表現
這個表現是任務量,數字隱去了,這個大概是雙十一的時候,發布的量非常大,平常的量比較小,可以看到它有周期性的規則,因為阿里的體量有發布窗口期,那一天整個的發布量大了,會有一個全局的管控。
案例2
?? 調度里面核心的模塊之一是Scheduler,在很多物理機上面選最佳的物理機,把容器布上去,常規的做法就是要有過濾鏈,過濾完之后會有一個權重的鏈,最后拿到我想要的機器上去創建這個容器。因為規模大,一個請求在一個機房里面去掃,這一個機房可能就是萬級別的節點,肯定要考慮到性能的優化,所以篩選的規模比較大一點。它是一個鏈式的,首先要選擇哪些服務器作為點,在這一塊,我們的想法就是要并發起來,怎么樣從工程上面做一個實踐。綠色的框是順序過程,紅色的框是并發的階段,或者是關鍵資源的鎖的過程。每一個并發的粒度看成是一個物理機,相當于每一個物理機去篩選這個是否要,這是一種模式。
???另外一種模式是說在整個機器的頂層加一個glock,是順序還是并行都不用管,這個鎖加在上面,前面的鎖是加在下面,兩者看起來好像沒有太大的區別,測試下來發現性能不一樣。有幾個因素,一個是下面要做過濾鏈或者做weight,看開銷;還有每一次篩選的規模,比如一天下來有十萬的請求,每個請求背景物理機是好幾萬的規模還是幾百的規模,最后對整體的性能要求是不一樣的。
粗粒度并發的性能比較好,我們選擇了第二種場景。深入的思考的時候,我們發現第一種場景行不通,原因是有些全局的資源沒有在一個協程里面更改,另外一個協程不能立即可見。
案例3
???Golang最近幾年非常的火爆,但是在阿里大的氛圍里面更強調的是JAVA語言,我們引入Golang不可能一上來就大規模,需要有一個成功的案例,或者是小規模實踐的過程。在這種環境下面,我們想讓Golang有一席之地,首選的方式就是如何做到快速的打磨,跑得很慢的時候,語言構架系統可能就會被淘汰掉了。這是我們上一個版本,有大概五個鏈路,有一個架子,每一條鏈路是做的過程當中,一步一步完善出來。今天我們把鏈路搬出來跟k8s做比較的時候,很多地方都是相通的,但是具體框架的編碼實踐上面來說是有很大的差異。我們早些時候摸索的這些東西,可能就是業務驅動或者概念驅動,沒有真正做到工程或是回饋社區的驅動。未來我們換了一種方式,我們可能是以工程的方式驅動,就是回饋社區。
案例4
???我講一下怎么解讀這個圖。分幾層,上面這一層更強調的怎么樣編排任務,中間的這一個層是講整個容器的;縱向有三個部分,最左邊是講怎么樣調度的,中間是一個容器的引擎,再后面是容器的運行時。
???在PouchContainer里面,官方寫了很多的Features,這些Features是源于阿里真實的實踐。為什么叫富容器,容器經典的代表大家可能想到Docker,那Docker之前呢?比如阿里的虛擬機比較流行,從虛擬機過渡到容器,這么大的規模,需要適應運維的習慣,要有這樣的感知、理念,這個時候容器的技術肯定要做很厚。然后再編排,現在我們了解的有k8s,大家覺得很完美,但是那在k8s之前是什么,阿里的體量并不是2015年那一刻才長大的,2015年之前就很大了。
???我們有一個強隔離,為什么說強隔離?我們平時和別人探討問題的思路有兩種思路,第一種思路是一上去就排查,把問題解決掉。還有就看自己的系統,拼命的證明不是我的問題。在這個時候你強隔離就好說了,問題排查或者是黑盒子,特別是規模很大的時候就需要隔離很強,每個人在我的領域范圍內很容易定位。從去年到今年,阿里做了很多混部的宣傳,混部沒有強隔離也有問題的。
???還有就是為什么要引入P2P?最早P2P是在流媒體里面,為什么又跟容器關聯起來了,就是因為互聯網里面有很強的思維,如果你慢的話,別人就會忍無可忍的。量少的話比工作更快一點。但是規模大的時候沒有辦法快,在鏈路上面來,包括業界也是一樣,連路瓶頸已經在拉鏡像,由此阿里很自然的就推了P2P加速。
???還有內核的兼容,外界有一個說法——CTO說阿里的商業成功,掩蓋了阿里的技術成功,這個話確實有道理。有些業務我們是在2011年的時候拿到2.6版本,現在業務最新的到了4.10,那些老的業務每天服務的人群量很小,不能說這個業務不賺錢或者沒有前景。把它下掉了也不行,要給一個緩沖期,該升級了,這個沒有辦法一步到位。這個時候要做規模化的升級,或者技術的換代,內核必須要做兼容。
???前面的內容只是把阿里巴巴自己的問題解決了,并沒有把這些賦能給社區,所以要做標準化的思考,這個是為了未來把好東西回饋社區,所以必須要做標準化的兼容,再反過頭來比這張圖,就知道這個架構怎么思考,為什么要兼容CRI。
代碼1
??前面我說最難調的bug是低級錯誤造成的,這個低級錯誤什么意思呢?
Golang一開始很多人用map循環的時候,就受到了指針變量的誤用,典型的表現就是,一個對象循環之后就會發現中間的所有的值一模一樣,大家首先想到的是業務邏輯是不是不對,根本不會想到for循環是不是出了問題。后來我們發現我們對語言本身的理解還不是特別的到位,犯了低級的錯誤,這個PPT下面有詳細的代碼案例。
代碼2
????還有map對象的異步序列化,現在看來本質上是我們用法不對,當時理解跟業務場景有關,我們每次篩選服務器有上萬的規模,為什么選擇這條服務器,要用日志把它記下來。后面的優化、迭代要進行消息分析。我們就想到異步做這個事情,把對象存在異步里面去刷盤。但是我們犯了致命的錯誤,就是Goroute對同一個map執行了讀寫并發,這樣就出現了map讀寫的沖突。如果知道對象是共享的資源,我們就會加鎖,但是這種場景下面我們沒有考慮到這個資源也會導致問題。
? 業務場景,特別是異步對象持久化的,要有這樣一個意識,規模大的時候要考慮這樣一個問題。
代碼3
???我們做的過程當中發現有一個資源泄露的問題,是場景導致的,我們起了很多Goroute,我們有個主任務,主任務會起很多子任務,子任務做一些循環的操作。到阿里云買服務器,阿里云是異步接口,現在起很多任務去買服務器,買完之后把請求發貨去,它返還我一個異步的ID,我再請求他狀態執行結果。比如說我先請求,完了之后它分配我一些資源,啟動這些資源。比如發起申請,然后start,看看這個start是不是完成,start也是有一個過程。比如阿里90分鐘再建一個淘寶,要拿到資源,肯定要很快。
? ?我們為什么會遇到泄露呢?我們主任務要申請200個實例,我會發起200或50個并發的請求到阿里云,但是不可能等200個請求全部完成了,200個來了15個就已經向業務交付,用戶的體驗就很好。但是這種滾動式的交付,不可能無休止的等待,你得一分鐘之內完成。
? 除了滾動式的交付,還有總體任務時間的控制,這就涉及到兩級的Goroute,第一級Goroute是總任務,子任務也是Goroute,這就會涉及到泄露的問題。假設主任務超時了,就不管子任務,子任務一直在跑就會把資源耗完。
???如果大家對k8s里面的代碼,關于定時任務的框架有所了解的話,就會有更強的體會。當時我們如果看到這個的話就會借鑒他的方式,就不會采取我們自己造輪子的方式。這個案例最重要的就是以后寫Goroute的時候要注意是不是有泄露。
代碼4
???我們為什么要做Pod打散?Pod概念在阿里有幾級,第一級是物理層級,第二級是機框打散,往上還有核心交換機的打散。比如我有兩臺服務器,其中有一臺服務器掛掉以后,可用性一下子降到了50%,其實創業公司還是可以接受的。但是在阿里,如果今天掛了1%,就有非常多人投訴,所以要求非常高,所以哪怕是1%的流量受到損失了,客服的量就一下子多很多。硬件故障和軟件故障是天然的,我們無法規避怎么辦呢?所有的服務不要扎堆,物理機,核心交換機上面也不要扎堆,這就是Pod打散。
???Pod打散背后還有一層思考,比如說這臺機器已經有了相同的Pod,另外一臺Pod一定不會往里面部署,有很強的隔離,要么是0,要么是1,這個打散是強制的。但是這里講的Pod打散不是強制的,是盡力交付。默認情況下盡可能地打散,如果打散不了,只能在一起,但是一旦有打散的機會,一定給你打散,我們的打散是在得分之后再做的事情,這個跟k8s不一樣,k8s是強硬的,要打散就一定打散,要不然交付不成功。
???阿里的服務量很大,服務器各個地方都有,其實它可以有一定的容忍度。假設有一萬個實例,可以允許1%的實例有波動偏差,這個時候Pod就可以滿足它。之所以能夠接受就是因為它的體量太大了,有扛波動的能力,這個跟我們接觸到的k8s完全不一樣,這是規模化的視角看到的東西。
3.總結
設計層面:整體架構層面優先語言選擇
性能層面:任務粒度選擇
數據驅動:狀態一致性轉移為存儲一致性
語言理解:Map異步序列化,Map循環指針
多層并發:可控超時
調度算法:規模下pod打散
Pouch Container:擁抱開源,回歸社區
【提問環節】
提問者1
???提問:我想請教一下,你是利用redis實現數據一致性,數據一致性代替狀態移植,這個什么意思?
???李雨前:比如說在k8s里面是面向結果交付的,我要三個實例我把數字改了你就可以擴出來,但是在k8s沒有出來之前,阿里已經存在了這些需求,那個時候是面向過程的,什么意思呢?我要拉鏡像,我要關報警,這一系列的動作從容器交付周期來說,就是事務的一部分,這個時候是有狀態的。比如說,你想一種方式是每一個狀態都帶一個ID,一連串的串回去,不可能是并發的。每一個狀態都是并發的往前做,到一個階段再并發做另外一個階段的事情,這個我們理解為是有狀態的,這些狀態我們做的時候,做不出來很好效果。
? ?為什么要放在redis,我們做之前嘗試過一點一點去做,最后我們怎么做呢?每一個狀態做完了就丟到redis里面去。下一個任務做的時候就知道上一個任務是什么,只要狀態滿足了就做下一個事情,最后你后面寫很多的任務,做的事情就很簡單,上一個狀態任務是什么就完了,中間做什么事情,也不需要看到全局的交互關系,這個就放到存儲,包括維護的一致性。
???提問:是不是變成異步任務,每完成一個任務就存下來?
???李雨前:可以這樣理解。
???
提問者2
???提問:其實Sigma的思路好像跟k8s有點像,我不知道Sigma有沒有像k8s這種封裝嗎?是怎么暴露服務的?
???李雨前:你覺得他們兩個很像,那說明你對k8s或者Sigma有不錯的理解。實際上包括Sigma,包括mesos,包括Omega,包括yarn,這些東西你比較的時候,很多地方有相似之處,從整體上架構來理解,他們確實是相似的,但是我前面提到了Sigma肯定是在K8s出來之前,那個時候就有自己演進的思路和方式,解決的問題都是一樣的。第二個問題,我們有沒有社區里面大家看到的方式方法,比如說前面講的pouch最后一項就是標準化的兼容,這已經反映了我們跟社區一些好的東西已經要開始吸收,把社區好的理念拿到阿里的場景進行實踐,實踐好了就回饋給社區,從這社區里面學到的東西有很多很多,我們也努力向社區做一些反饋。
???
提問者3
???提問:Sigma底層可以用pouch,docker這種嗎?
???李雨前:可以的,前面我說了現在出了一個CRI,那個已經兼容了,實現了CRI的協議,你用起來是一樣的,你把Pouch起來的話,你用Remoteruntime配一下就可以了,我推薦你看一下pouch官方文檔,里面有一些案例。
?
2018年的 Gopher Meetup 將在深圳開啟巡回第一站,這一次邀請了很多新的講師給大家一起交流分享Go的使用經驗?
點擊閱讀原文報名參加
總結
以上是生活随笔為你收集整理的Golang在阿里巴巴调度系统Sigma中的实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图像模式识别 (二)
- 下一篇: 清华同方服务器做系统,清华同方云服务器底