本次LiveVideoStackCon 2021 音影片技術大會 北京站邀請到了新浪微博影片平臺架構師——黃陽全,他將為我們介紹微博影片處理系統的架構演進與雲原生之路上的探索,為什麼選擇自建,以及如何實現基於原有基礎服務的FAAS平臺。為嘗試雲原生架構模式的開發者提供參考。
文 | 黃陽全
整理 | LiveVideoStack
大家好,我是來自微博影片平臺的黃陽全,今天分享的主題是微博影片處理系統雲原生之路。
我在2017年加入微博研發中心,負責微博影片基礎元件的開發與維護,多次參與了微博影片架構升級,主導了微博影片中臺的建設。目前正在建設基於雲原生架構的微博影片處理系統。
本次分享主要分為4部分:
1.背景介紹:主要介紹微博影片,微博影片處理系統,微博影片處理系統所包含子模組,以及它的特點;
2.原影片處理系統架構:針對微博影片處理系統所具有的特點,我們設計出基本滿足需求的原架構,但是隨著業務的發展,逐漸發現了原架構的短板;
3.FaaS平臺的探索與實踐:針對短板,我們在雲原生領域開始瞭如火如荼的FaaS平臺的探索,在原基礎服務之上開發出瞭解決業務痛點的FaaS平臺;
4.總結與未來展望。
一、背景介紹
隨著4G/5G技術和裝置的不斷普及,越來越多的使用者選擇透過影片或者直播來記錄生活,傳遞知識,表達觀點。
微博作為一個即時分享平臺,影片所佔的比例也越來越大,目前微博影片的日釋出量已達到百萬級,日播放量也早已突破十億級。
圖示為一個微博影片從生產到消費經歷的整條鏈路:
首先影片創作者從手機、PC等裝置上傳影片或是開啟直播,影片處理服務接收請求後進行影片的處理。處理完成且稽核通過後,影片釋出,最後使用者能透過播放服務看到影片。
下面放大影片處理服務部分(黃色部分)。
影片處理系統包含四個子模組:影片轉碼,影片內容理解,直播錄製和轉碼實驗室
1、影片轉碼:對於使用者上傳的影片,我們需要將影片轉碼成不同的清晰度(1080p、720p等),以滿足不同場景下使用者的播放需求。對於使用者來說,上傳後影片越快發出,體驗越好,所以影片轉碼有速度要求。另外影片微博的上行有熱點特性,需要有較好的峰值應對能力。
2、影片內容理解:負責提取影片中的內容資訊,轉換成結構化資料,助力於業務發展。內容理解具有資源多樣,演算法多樣,服務多樣的特性。
3、直播錄製:從源站拉取直播流,轉碼後的影片儲存到檔案服務,以供使用者點播觀看。
4、轉碼實驗室:顧名思義,是各種轉碼演算法、影象演算法、內容理解演算法的試驗田。轉碼實驗室是離線處理任務,對實時性要求不高,可能白天需要驗證的演算法,晚上跑,第二天能得出結果就可以。
綜上所述,微博影片處理系統具有大流量、實時性、核心服務、峰值明顯、線上/離線同時存在、資源多樣的特點。
二、原影片處理系統
對於影片轉碼來說,保證轉碼的效率是至關重要的。
這是一個整檔案轉碼和分片轉碼的對比示意圖,整檔案轉碼分為下載、轉碼,上傳。分片轉碼將下載好的影片分片後並行處理,從而提高轉碼速度,可以看到分片轉碼速度遠優於整檔案轉碼速度,特別是轉碼大影片檔案時,這也是業內通用的解決方案。
但是分片轉碼也帶來了新的挑戰:
1、流程編排複雜:如果採用傳統的編碼方式進行分散式分片並行轉碼是比較困難的,另外還要考慮失敗超時重試等情況,整個流程會非常複雜。
2、高併發,低延遲:在微博的場景下,結合前面講到的微博影片的特點,我們還有高併發低延遲的挑戰。
有的同學會疑惑,影片上傳這種上行介面大概是幾百qps,怎麼會達到高併發?這裡解釋一下,假如微博的上傳qps是100,一個影片分成100個分片,需要轉出10個label的清晰度,那麼系統內部需要承載的qps將被放大到10w。
為了解決這兩個問題,我們開發出了流程編排引擎和高效能的排程器。
首先是DAG有向無環圖的框架和Olympiadane。我們採用與Java親和度比較高的Grovvy定義DAG流程。
這是DAG編排後的分片轉碼流程圖,每一個task(綠色節點)是一個單獨的實現類,以此解耦流程和任務,DAG還提供重試重做,視覺化的功能。
有了流程的編排,還需要將任務下發到機器上執行,於是我們開發了TaskScheduler任務排程器。
任務排程器採用任務優先順序佇列和機器優先順序佇列相結合的方式,將排程效能最佳化到一次redis命令執行,支撐轉碼10w級併發,毫秒級排程的需求。
此外,TaskScheduler還具有雙發排程,任務組發、水平伸縮、宕機自動摘除,忙時堆積等特性。
這是任務排程執行的全景圖,有了DAG和TaskScheduler,影片處理任務由DAG描述依賴關係,透過排程器排程到Worker上執行。
以上是原影片處理系統的一些關鍵設計。
那麼,隨著業務的發展,我們遇到了哪些挑戰,系統又隨之顯露了哪些短板呢?
問題一:資源整體利用率不高
算力緊張,但是資源整體利用率不高:業務高速發展同時伴隨著越來越多新演算法的不斷落地,對資源的需求也越來越多,不斷舔磚加瓦的同時,我們也在審視當前的服務資源使用情況。
右圖是某一時刻服務資源利用率的橫截面圖,由於不同的服務模組具有資源差異(計算密集型業務/記憶體密集型系統),和執行時段的差異(業務流量高峰期/凌晨流量低谷),雖然我們儘量做到區域性最優,但還是很難達到全域性最優。
這是線上某臺轉碼機器的CPU利用率的截圖,可以看到,機器CPU利用率存在明顯的波峰波谷。高峰期,機器利用率可高達95%,但是低谷期,利用率還不到10%
在評估服務資源的時候,為了保證系統的可靠性,往往需要根據服務的峰值進行評估,另外還要預留一定的冗餘度。
雖然我們已經做了基於公有云的晚高峰彈性擴縮容,比如每天晚上8點開始擴容,到凌晨流量下降時再縮容。但整體看來,服務還存在著巨大的冗餘與浪費
圖中的金幣部分就是浪費的的資源。
問題二:開發運維效率不高
需求緊急,但是開發運維效率不高。
這是一個真實的案例,產品組大半夜發來訊息:“演算法Demo已經跑通了!似乎可以上線了!明天能灰度了嗎?”。
但後端開發人員遇到的問題遠不止於此:
1.演算法程式碼如何轉成工程程式碼,有沒有坑?
2.程式碼寫在哪個工程裡?會不會有衝突?後期怎麼維護?
3.灰度功能在哪兒加?
4.如何保證系統的可用性?
5.流量熱點怎麼應對?
6.擴縮容怎麼做?
這些問題都需要考慮。
在當前服務模式下,從演算法到上線,再到有資料,需要經歷哪些步驟呢?
首先,需要將演算法翻譯成工程程式碼,因為負責演算法的同學關注演算法本身的正確性多於工程方面,所以他們提供的演算法可能並不能直接應用於工程。然後進行適配開發,加入到現有的工程中,假設轉碼後有幾萬行程式碼,這就需要考慮AB測試,相容性,後期維護,高可用等問題。透過測試後,即可打包為線上映象並上線,上線完成後,還需要考慮服務保障、擴縮容等,最後才能得到效果資料
總結可得在開發效率方面遇到的三個問題:
1、開發縱深太深,新手難以上手;
2、開發流程複雜;
3、運維難度大。
如果無法妥善處理這三個問題,結果就會如左圖所示只有工程開發在默默挖坑。
三、FaaS平臺的探索與實踐
現有的服務模式似乎難以解決資源利用率低及開發運維效率低的問題,於是我們開啟了對FaaS平臺的探索與實踐。
FaaS的全稱是Function as a service,是為使用者提供開發、執行和管理的函式服務平臺
以承載”函式”的方式來啟動基礎應用框架,並對應用實現資源編排、自動伸縮、全生命週期覆蓋等保障,從而實現雲服務的Serverless。
由IaaS發展到現在的FaaS(左圖),開發者需要關注的內容越來越少,可擴容單元越來越小。
介紹了FaaS後,我們將遇到的痛點和FaaS平臺可以解決的問題進行以下對比。
痛點一:開發縱深太長;FAAS平臺可以消除技術壁壘,理論上只需要瞭解函式的輸入輸出就可以,開發人員沒有其他心智負擔;
痛點二:開發流程複雜;FAAS可以提供部署、釋出、監控“一條龍”的服務,並提供穩定性保障;
痛點三:運維難度大,體現在服務保障,宕機處理等方面;FAAS平臺免運維,全託管;
痛點四:資源整體利用率不高;透過平臺化的思想,整合資源,共享冗餘,實現按需排程,從而提高整體資源利用率。
對於FaaS平臺的選型,我們有三個選擇:雲廠商、開源FaaS框架和自建。
1、雲廠商:由於只能覆蓋公有云的場景,微博場景下暫時不考慮;
2、開源FaaS框架:根據對常用FaaS框架進行調研的結構顯示,在場景、語言以及適配上有一定的侷限性,現有系統遷移起來難度比較大,而且有些框架還不夠成熟。
於是我們選擇了自建FaaS平臺,透過複用現在的基礎設施和框架,打造適配微博處理系統的FaaS平臺。
以下是基於原框架基礎構建FaaS平臺的步驟:
第一步,將DagTask抽象為function,使其能夠獨立部署運維,上文提到DagTask是單獨的實現類,與原專案程式碼有耦合。假設內容理解工程中有幾十個task,會使工程程式碼量膨脹,增加開發人員的心智負擔。而在Function的設計中,開發人員只需關注輸入輸出,開發更簡單高效;
第二步,原DagTask會依賴整個服務的部署,轉碼時整個服務中會部署幾十個task且task的升級和擴容都不能獨立進行。Function的設計中完全解決了這一問題,它提供了函式級別的部署運維,可獨立伸縮。
另外,原DagTask僅支援Java,Function不限制語言,目前已支援了Java、go、python,為演算法人員提供便利。
第二點升級是將原DAG流程服務化。原DAG流程採用grovvy描述、SDK依賴的方式。那麼在升級DAG流程時,需要上線兩次,首先全量上線class檔案,否則當task被排程到沒有此class的機器上時,會出現“class Not Found”。
WeiboFlow採用yaml檔案描述DAG,透過服務化管理DAG版本,對版本迭代更友好。
此外,由於原DAG流程採用了grovvy描述、SDK依賴的方式,與專案程式碼耦合。服務化之後可與專案程式碼徹底解耦。
完成以上兩個重要改造之後,基本形成了FaaS平臺雛形。
將FaaS平臺分為兩層,第一層是服務化的WeiboFlow函式編排層,負責函式的編排流程管理,第二層是WeiboFunction提供的雲函式層,負責具體的函式排程執行。
這是FaaS平臺的任務排程鏈路圖,主要分為三部分:
1、WeiboFlow,上文提到是DAG服務化的版本,主要功能是對DAG進行版本管理及DAG的執行。
2、WeiboFunction,主要負責函式管理及部署管理,其中的重要元件Task Scheduler是複用了原有的Task Scheduler從而進行函式的排程。
3、具體執行的node節點,其中的重要元件FunctionLet負責接收任務,與WeiboFunction保持心跳,上報函式資訊和機器槽數。
藍色虛線表示任務的呼叫鏈路,應用可直接呼叫WeiboFlow的submit介面提交完整的轉碼流程或內容理解流程,也可以直接呼叫WeiboFunction執行某一個函式。
WeiboFlow收到呼叫請求後,開始執行DAG上的節點,每一個節點可對應一個Function,呼叫到WeiboFunction執行,Function收到請求後,使用Task Scheduler下發排程任務,將優先順序最高的任務和最優的機器進行匹配後排程到具體的機器上執行,具體的node,FunctionLet收到請求後進行函式執行及回撥。以上就是整個呼叫鏈路。
介紹完FaaS平臺後,再回到最初的問題,它是如何解決以上兩個問題的呢(資源利用率低、開發運維效率低)?
3.1 提高資源利用率
3.1.1 整合管理所有資源
FaaS平臺將不同機器型號,資源型別統一抽象為槽數,比如8核16G抽象為2000個槽,16核32G抽象為4000個槽,同時將GPU和FPGA裝置也抽象為相應的槽數。還可對此轉換公式進行配置,統一了資源的度量衡。
透過這一層抽象,FaaS平臺能夠統一管理排程多型別的資源。
FaaS平臺還實現了動態資源標籤化,以此實現機器資源的隔離,結合上文提到的多佇列排程器,即可充分利用資源。
3.1.2 動態擴縮容
FAAS平臺實現了基於函式維度的動態擴縮容,左圖顯示了原晚高峰的擴縮容,右圖顯示了基於函式的動態擴縮容。
得益於函式啟動的速度更快,動態擴縮容更貼合服務許可權。
我們將動態擴縮容功能分為四層:決策、聚合、Function擴縮容和Node擴縮容。
決策層:在做服務冗餘度決策時,可根據服務冗餘量或當前佇列堆積情況,動態計算服務冗餘度,還可根據時間策略,透過某一時刻的流量情況進行決策,人工干預也算作一種決策。
聚合層:聚合決策層下發的不同策略後做出最終的決策——服務最終的擴縮容策略。
Function擴縮容:在服務容量足夠的情況下進行動態的函式擴縮容。
Node擴縮容:當服務容量滿載,有些服務沒有空餘槽時,會觸發Node層的擴縮容。Node層由微博的DCP平臺打造而來,目前DCP平臺可以達到5min,2000臺機器,基本滿足我們的要求。
動態擴縮容能夠提升高峰期的資源利用率,那麼如何解決常備資源量以下的浪費呢?
方式一:分時複用,在A服務的冗餘度達到某一水平時,對A服務進行自動縮容,再部署B服務,以此達到分時複用的目的。
此方法可應用於轉碼服務和轉碼實驗室服務(無論線上或離線)。白天轉碼實驗室的任務由少量機器處理,先進行堆積,到凌晨時,轉碼服務縮容,部署轉碼實驗室,消耗佇列中待處理的任務。
方式二:挖礦。空閒資源代表“礦”,執行完即銷燬的Function代表“挖礦工具”。
執行完即銷燬的Function:雲函式的服務模型分為兩種,常駐程序型和用完即毀型。常駐程序型的函式不會被銷燬,而用完即毀型的函式在一次呼叫完畢後就會被銷燬。我們將Gif轉碼任務設計為用完即毀型,因其執行週期較短,Gif任務也常作為“挖礦工具”。
如何“挖礦”呢?
這是1~5,五臺伺服器,Node1有4000個slots,Node2有3500個slots。假設5臺伺服器都部署了A服務,此時A服務只佔3000個slots,那麼會出現1000個slots的冗餘。如何減少冗餘呢?
假設要對Node1~4進行挖礦,只需要給1~4打上B的tag,此時呼叫器透過心跳感知這些機器,會將函式B排程到機器1~4上執行。
如果Node1空閒1000個slots,而函式B的一個請求佔100個slots,那麼Node1上可以並行跑10個函式B,Node2可以並行跑5個函式B,透過呼叫器實現“指哪挖哪,哪裡有礦挖哪裡,有多少挖多少”。
進一步探索“挖礦”——如何更高效率地“挖礦”呢?
執行完即銷燬的function採用冷啟動的方式,(如圖)先start up,再run stack,如此反覆,每次都需要重新start up,累積下來對機器的損耗較大。
於是我們進行了以下最佳化,採用熱啟動的方式,一次函式啟動後,如果在最大空閒時間內有請求進來,就繼續執行任務,不被銷燬;如果超過最大空閒時間內無請求進入,那麼函式執行環境會被銷燬,下次任務到來時需要重新啟動。透過以上步驟達到連續挖礦的目的。
最佳化後的效果如圖,使整個服務的資源曲線更加貼合服務的流量,提高了資源利用率。
3.2 提高開發運維效率
1、FaaS平臺提供了開箱即用的模板工程實現統一工程化及標準化。
一個雲函式對應一個gitlab的專案,函式開發與釋出都圍繞單個專案進行CI/CD,高效且安全。從而使開發人員更關注業務邏輯,達到提高開發效率的目的。
2、FaaS平臺提供了雲函式的高可用保障。
這是任務從WeiboFunction排程到具體機器上執行的細緻流程圖:
在FunctionLet和Function之間利用QueQiao進行通訊,FunctionLet在接收到任務的時候,寫入TaskQueue,Function讀取TaskQueue進行函式邏輯處理,處理完畢後將結果寫入ResultQueue,FunctionLet讀取ResultQueue中的結果,並返回。
這樣的設計如何提供高可用保障呢?
1)假設WeiboFunction宕機或者脫網導致回撥失敗,此時無需重新執行所有任務,已執行完的任務會將結果寫入ResultQueue中,FunctionLet回撥失敗後也會將回調失敗的任務重新寫入ResultQueue中,等待WeiboFunction恢復後,重新將執行完成的ResultQueue進行回撥,已執行完畢的任務不會丟失或被再次執行。
2)假設FunctionLet上線重啟或者服務異常,已執行完畢的任務也會被寫入佇列,等待FunctionLet恢復後重新回撥。
3)假設Function掛了,FunctionLet將停止接收新的任務,FunctionLet對Function有具體函式的保護,重新啟動函式,執行Queue中任務進行回撥。
那麼假設Node節點整個都掛了,且處於不可恢復的狀態時,執行完畢的任務會丟失嗎?
答案是否定的,我們在Task Scheduler層設計了派發重試及故障轉移的功能。除了node上各個元件的高可用,還保證了node級別的高可用。
當任務被派發到執行機器上時出現了超時的情況,任務會從Task Scheduler的執行任務佇列中取出並選擇一臺當前活躍而且空閒度最高的機器重新派發,以實現派發重試的功能。
當Node無法恢復時,會被移出執行器佇列,後續的任務不再被派發到這臺機器,以實現故障的轉移。
WeiboFunction還提供了Function級別的調諧保活機制,根據申明的服務節點個數重新找到合適的節點,啟動服務,以保證服務達到申明的最終狀態。
3、下沉通用功能。
我們將常用的比如限流降級,AB測試等下沉到平臺來實現,這樣使用者不用自己實現,只需要在FAAS後臺透過配置的方式即可操作。
1)AB測試:可透過WeiboFlow的DAG配置來實現,如右圖所示,將80%流量打到圖1,20%流量打到圖2,開發人員透過配置即可實現流量的分發。
2)任務動態優先順序:每個任務都有對應優先順序,在轉碼服務中,假設使用者發博的任務是480P,那麼此480P任務的優先順序就相對較高,可以透過動態調整任務節點的優先順序,並配合排程,先執行優先順序高的任務。
3)任務動態權重:一個h265的任務通常需要佔用更多的slots,此時可以透過任務動態權重功能達到資源的平衡利用。
4)限流降級:為每個節點配置限流降級的閾值。
5)錯誤處理:節點錯誤後可以配置重試頻率及重試次數。
6)大小圖、父子圖:針對複雜的流程圖,提供了大小圖和父子圖的方式,每一個節點都有可能是另外一個完整的圖,可以理解為對節點進行foreach的操作,分片完畢後,如有100分片,就對分片進行foreach迴圈,每一個迴圈都有一套處理邏輯,而處理邏輯是可以複用的,這就可以透過大小圖的方式解決。
7)多種語義控制:目前提供了pass/foreach/return/suspense/choice等語義,可支援複雜的流程編排。
4、Faas平臺提供了多維度的可觀測性。
右圖1是執行完畢的轉碼DAG圖,截自Weibo Funciton後臺,展示了整個執行鏈路,節點出現問題時會變紅,便於追蹤鏈路,排除故障。此外。每個節點還有統計資訊,執行時間、橫向或縱向的Metrics。點選每個節點,會出現右圖2所示的詳細資訊,其中包含整個執行關鍵日誌,便於使用者調看。
5、FaaS平臺重建了開發流程。
開發人員在程式碼編寫完畢後,透過本地測試構建映象,註冊或升級函式,再進行遠端測試,最後釋出函式,函式即可彈性執行。
透過使用者維護WeiboFlow流程或者其他事件可觸發執行函式。
四、總結與未來展望
微博影片處理系統在具有實時性、大流量、核心服務、峰值明顯、資源多樣、線上/離線等特點的背景下,為了解決高併發,低延遲及流程編排複雜的問題,我們開發出了DAG編排引擎及任務排程器。
但隨著業務的發展,陸續出現了資源利用率低,研發運維效率低的問題,於是我們開啟了雲原生架構中的探索,開發了高彈性,全託管的FaaS平臺,透過資源整合、按需排程、減小開發粒度,下沉通用能力以解決利用率和運維效率的問題。
最終達到的效果,主要分為兩方面:
1、降低成本:叢集負載標準差最佳化30%,影片轉碼彈性成本了降低44%,透過分時複用、挖礦的方式將演算法迭代週期從40天最佳化至6天。
2、提高效率:支援演算法的上線週期由天級或者周級最佳化至小時級。舉一個真實的案例,在奧運會期間,我們收到了識別某logo的需求, 從中午接到需求到下午上線完成得到資料,僅用了幾個小時。
未來,我們希望FaaS平臺能夠支援更多的場景,比如在WeiboFunction增加負載均衡層,從而支援傳統微服務場景。
其次是冷啟動最佳化,雖然目前已經支援常駐記憶體的方式減少冷啟動帶來的消耗,但在需要頻繁冷啟動的場景下,還會造成較大的損耗,未來我們會在冷啟動方面投入更多時間精力。
最後是智慧化運維,在FaaS的運維場景下加入服務畫像,根據服務畫像動態調整服務容量,達到更高的利用效率。
以上是本次的分享,謝謝!