日均請求量百億級資料處理平臺的容器雲實踐

錢曙光發表於2016-08-29

宣告:本文為CSDN原創投稿文章,未經許可,禁止任何形式的轉載。
作者:袁曉沛,目前在七牛雲的主要工作是基於容器平臺構建分散式應用,藉助容器的優勢,實現大規模分散式應用的自動化運維以及高可用,以PaaS服務的形式提供伺服器後端應用,同時致力於讓開發者從繁瑣的後端運維工作中解放出來。曾參與盛大網盤EverBox、EMC備份服務Mozy後端儲存的設計、開發工作,主要方向在分散式系統的架構設計、開發、效能調優以及後期運維優化。
責編:錢曙光,關注架構和演算法領域,尋求報導或者投稿請發郵件qianshg@csdn.net,另有「CSDN 高階架構師群」,內有諸多知名網際網路公司的大牛架構師,歡迎架構師加微信qshuguang2008申請入群,備註姓名+公司+職位。

七牛雲研發架構師袁曉沛日前結合七牛雲自定義資料處理平臺業務的容器化實踐,從平臺的業務特點、為什麼容器化、如何實現容器化以及容器實踐的具體效果等角度進行了分享。以下是對他演講內容的整理:

資料處理業務簡介

資料主要有三種處理方式:

  1. 官方資料處理:
    提供基礎的資料處理服務,包括但不限於圖片的轉碼、水印、原圖保護、防盜鏈等,以及音視訊的轉碼、切片和拼接等。

  2. 自定義資料處理:
    允許使用者構建、上傳自定義的私有資料處理服務,並無縫對接七牛雲端儲存上的資料以及其他資料處理服務。

  3. 第三方資料處理:
    一個開放的應用平臺,提供大量功能豐富的第三方資料處理服務,比如圖片鑑黃、人臉識別、廣告過濾、語言翻譯、TTS 等。

圖片描述

圖 1

圖1是資料處理的一個呼叫示例。無論是七牛雲的官方資料處理,還是自定義資料處理,或者第三方資料處理,都是以這種形式呼叫的。最左邊的是一個URL,代表一個檔案,中間綠色的Facecrop是資料處理命令,右邊的200×200是請求引數。

圖片描述

圖 2

如圖2所示,從左邊的人物肖像圖原圖到右邊200×200的小圖片,這個服務可以把人臉從原圖中剪裁出來。通過管道操作,還可以把圖片再存到儲存中,以後直接使用這個小圖,不需要再走資料處理計算。這是一個典型的資料處理呼叫。

官方資料處理的挑戰

第一,請求量非常大。目前,系統每天有近百億的資料處理請求量。年底,可能會在目前近千臺的計算叢集基礎上翻好幾倍,整個存量、增量都非常大;

第二,突發流量非常頻繁。很多客戶是從其它雲遷到七牛雲,首次把大量檔案遷移到七牛儲存後,往往會發起大量的資料處理請求,這會帶來大量突發流量;

第三,CPU密集型計算。目前後端叢集中,絕大部分的機器都是用來跑圖片、音視訊轉碼的,這些都是CPU密集型的計算,這意味著後臺需要很多臺機器,而且CPU的核數越多越好;

第四,IO操作頻繁。IO分為磁碟IO和網路IO,資料處理前,往往需要先把原始檔案從七牛儲存中下載到本地磁碟,所以磁碟IO和網路IO都很頻繁。

官方資料處理的架構演化

圖片描述

圖 3

圖3是資料處理早期的一個架構圖。當時比較簡單,所有請求都經過FopGate,每個節點上都部署了很多圖片、音視訊或者文件處理的Worker,是具體做轉碼的計算服務。整個架構,Worker在閘道器上是靜態配置,增加新的Worker時需要改閘道器配置重新載入,做起來會很麻煩。另外,請求進來時,對應要處理的資料也是通過閘道器服務進入,控制流和資料流放在一起,閘道器整體壓力非常大,當時出了很多問題。

圖片描述

圖 4

圖4是資料處理目前的主體架構。右圖左上角增加了一個Discovery元件,收集Agent上報的資訊,Agent和Worker把自己能做什麼事都上報到Discovery。每增加新的主機Agent和Worker時,不再需要靜態配置,只需指向的該Discovery服務。閘道器可以通過Discovery獲取資訊,根據進入的具體請求,將這個請求路由到不同的Agent,Agent再把請求轉到Worker處理。Agent元件還有負載均衡的作用,請求會選擇後端相應的節點執行,另外也起到下載檔案的作用。下載檔案的資料流不經過閘道器服務,Worker向Agent發起一個下載請求,然後這個Agent到儲存上下載資料,放到自己的快取中。

這個架構解決了一些問題,但是之前提及的挑戰依然存在。主要的問題是FopGate在過載時依然會崩潰,每個主機會過載出問題,造成請求變慢或當機。接下來討論一下如何解決這些問題。

如何應對官方資料處理的挑戰

系統測量

第一,測量FopGate的服務能力。按照線上的配置,針對同比例縮小的叢集做效能測試,測試出FopGate單機最大的請求數和控制程式碼數,根據實際的業務量,確定機器配置和數量。

第二,測量某種資料處理的資源使用正規化。大多資料處理是CPU密集型計算,我們需要找到單個處理的資源使用規律。測試方式如下:取線上約10萬或100萬個請求,針對一臺測試機器壓力測試。比如測Image,測試這臺機器的變數有以下三個:Image 例項數,併發處理數和佇列長度。最終拿到的一個結果是,做圖片剪裁、圖片處理、圖片轉碼等服務,一個程式一個CPU的邏輯核,同時處理一個任務,對它來說是最快的,多個任務同時處理反倒會讓CPU的處理變慢。

第三,測量單例項的服務能力。一個程式實現得很好,可以把多核利用起來。針對這樣的例項,分配多個CPU執行緒、調大併發、並壓測這個的例項,試圖用10萬個請求看整體處理速度,發現整體處理速度並沒有起多個例項,但每個例項限制CPU執行緒數、限制併發好。所以最終的結論是作業系統對於CPU的排程,比程式要好;相比起一個大例項、接受高併發請求,我們更傾向於執行多個例項、但每個例項限制併發。

增加佇列

這個主要的考慮是提高服務質量,避免單例項過載。前面得到結論,一個圖片處理的例項,一個核,併發量為1時最快。我們為每臺節點機器加了一個排隊機制,排隊之後不爭搶資源,整體處理速度反而變快。

另外,從運營角度講,可以用佇列長度來區分免費使用者和付費使用者。比如,可以將免費使用者的排隊長度設定得長一點,比如設成10,意味著最後一個請求要等前面9個請求處理完才能處理。而對於付費使用者,可以將佇列長度調短一點,只有3或者5,這樣平均等待時間就變短。總原則是區別付費使用者和免費使用者,免費使用者保證高可用,但是不能保證高質量和低延遲,因為資源有限;對於付費使用者可以保證高質量,因為可以通過排隊長度控制這個事情。

限流

為什麼有了佇列之後還要限流?兩個原因:

第一,大量長連結影響FopGate效能。因為七牛雲處理最多的是圖片、音訊和視訊三種請求。圖片請求往往比較快,高峰期時可能幾秒、幾十毫秒完成,但是視訊和音訊轉碼往往比較慢,可能需要好幾分鐘或者好幾十分鐘,它取決於一個具體的轉碼引數和轉碼時長。如果不限流,閘道器會維持大量的長連結,累積大量控制程式碼,輕則影響的效能,嚴重的情況下會造成當機。

第二,突發流量,導致佇列過長。佇列太長,有大量任務積壓的情況下,會有任務在處理之前就超時。與其超時,還不如直接限流,拒絕處理不掉的請求。

限流手段有三種:

第一,併發HTTP請求限制。超過這個限制,直接拒絕請求並返回。但這並不是最好的方式,因為超時的請求已經解析處理過,這對於效能是有一定損耗的。最好的做法是用一個訊號量控制TCP accept,比如控制訊號量個數是1萬個,1萬個請求正在併發處理時就不accept 。這是最好的方式,但實現略複雜。

第二,單使用者請求數限制。有些使用者可能會在特殊情況下發起大量的請求,為了不讓他影響別的使用者,系統會限制單個使用者的請求量。

第三,Cmd數限制。主要指具體某個操作,比如圖片檢視,要對Cmd數進行限制。因為不能讓大量同樣的Cmd把資源消耗殆盡,影響其他Cmd的操作。

合理協調IO和CPU

為什麼要合理協調?因為資料處理的流程是:下載、寫盤、處理、寫盤、返回,涉及到網路IO和磁碟IO。整個優化方式總原則是就近計算,將下載和計算部署在一起。目前,新的架構中是混合部署的。本來可以設計,將下載部署在其他機器上,但這樣會增加一次網路的路由次數。所以,從一臺機器的Agent下載後,直接在本機處理,不會再經過一次網路路由。另外一種方式是掛載ramfs,直接把下載內容放在記憶體中。比如記憶體分8G,下載完成後直接放在記憶體中,要用的時候直接進記憶體讀,這樣可以節約磁碟IO的開銷,也能整體加快單個請求處理速度。

前面這些是官方資料處理的一些挑戰、架構演化以及我們採取的一些對策,後面講一下自定義資料處理所面臨的挑戰。

自定義資料處理的挑戰

第一,處理程式由客戶提供,我們不知道客戶在程式裡做了什麼事情,也不知道客戶的程式會使用多少資源,這意味著我們至少要做兩點:一是安全性,不能訪問到別的資源,二是隔離性,單個程式不能無限制使用資源。

第二,業務規模不確定性,無法估算量有多大。這個帶來的挑戰是業務可伸縮性,需要客戶可控。

基於這三個需求:安全性、隔離性、可伸縮性,Docker非常適合這個業務場景,整個自定義資料處理在14年開始基於Docker實現。

圖片描述

圖 5

圖5是自定義資料處理的業務流程:

第一,使用者要建立一個自定義資料處理,即註冊一個UFOP;

第二,使用者在本機上開發自定義資料處理程式。開發這個程式要遵循七牛UFOP正規化;

第三,把這個程式提交一個構建版本;

第四,切換到這個版本,設定例項數。例項啟動後,該UFOP就可以訪問了。

這是迭代的,有一個箭頭直接指回開發自定義資料處理程式,意味著升級程式、構建另一個新版本、切換到新版本例項的一個過程。

業務流程-註冊

圖片描述

圖 6

圖6是註冊的過程,第一個qufopctl是客戶端工具,第二個是reg註冊,第三個ufop-demo是名字,最後-m 2是模式,支援私有和公有模式。

圖片描述

圖 7

圖7是自定義資料處理後端註冊的流程,qufopctl把前面描述的註冊命令,發到ufop-controller,它會進一步走鑑權服務,鑑權成功之後通過Keeper服務存檔到DB。註冊成功後,使用者開始在本地實現UFOP應用。

圖片描述

圖 8

圖8左邊是一個最小的UFOP程式碼,簡單來說就是在UFOP請求路由上加一個UFOP請求,拿到待處理檔案URL後下載對應檔案,然後獲取檔案的型別和長度,並作為結果返回。右上角是我們定義的一個描述性檔案。2014 年,我們做的時候Docker還沒有那麼火,所以沒有直接用Dockerfile,而是自己定義描述模式。第一行是引用源;第二行是構建指令碼,把外面的程式改成可寫;第三是執行,怎麼執行這個程式。右下角是要打包成tar包的本地目錄。

業務流程-構建

圖片描述

圖 9

如圖9所示為構建命令,是把本地的程式上傳到服務端,並構建一個版本。

圖片描述

圖 10

圖10是構建的後端。整個後端的過程如下:qufopctl把程式tar包上傳到Kodo,發起構建請求,再把構建請求轉發到構建機器,接著把UFOP描述檔案轉化為Dockerfile,基於Dockerfile構建Docker image,最後推送到Docker Registry。

下面是構建後端我們踩過的一些小坑。

使用Debian映象服務

原先使用AppRox,是Debian包下載、快取服務,但實際上經常下載超時,而且下載出錯後,自己沒辦法辨別本地檔案存在問題,需要手動清除,比較糟糕。後來,搭建了一個映象源。映象源的做法是首次全量下載,全部下載完畢後,設定一個定時增量同步的時間,在凌晨,一天一次,每天更新的量只有幾十兆,幾分鐘便可完成。從此,再也沒有出現過這方面的問題。

避免Docker構建快取

圖片描述

圖 11

Docker的構建快取比較容易出錯。圖11第一個是錯誤用法:第一步,下載一個jdk的壓縮包;第二步,建立一個目錄,將壓縮包解壓到這個目錄,並且把原來的壓縮包刪除。分了兩條命令,但是容器其實會對每一條命令做快取。

假如我們第一次執行這兩條命令時是沒有問題的。第二次執行時,我們覺得第二條命令中OPT目錄不好,想到另外一個目錄,於是對第二條語句做了更改,那麼就會出問題。為什麼?因為第一條命令沒有任何變化,所以容器構建系統認為,沒有任何的變化就不去執行,於是直接跑第二條命令,第二條在前一次執行時,已經將壓縮包刪除,因此會跑失敗。這種錯誤很容易犯,解決方法是把壓縮包的下載、目錄的建立、解壓縮和刪除壓縮包,都放在同一條命令裡。當這條命令被修改後,每次都會重新執行,就不會出現這個問題了。

調整例項數

圖片描述

圖 12

圖片描述

圖 13

接下來是調整例項數,前面已經構建完成,程式已經上傳到伺服器端。qufopctl resize ufop-demo -n 3,這條命令會增加3個例項。

整個後端的過程如圖13所示,qufopclt傳送請求到ufop-controller,中心排程器Scheduler下面有很多個node,執行著我們實現的Daemon服務,所有容器都是由Scheduler下發給某個Daemon,這些Daemon啟動對應的容器。Daemon會把所在機器剩餘資源的情況,包括CPU、記憶體和磁碟,實時報給Scheduler,Scheduler會根據例項的規格要求找到相應滿足要求的node。這些容器例項建立成功後,Daemon會把具體的埠資訊返回到Scheduler,Scheduler再通過Keeper服務持久化,這樣就結束了。Docker Registry是Docker的映象倉庫。

升級例項

圖片描述

圖 14

到前面這一步,UFOP已經執行起來並且可以使用,如圖14所示為升級例項的過程。我們有一個Upgrade的命令,-r是一個例項,現在要做的是升級前兩個例項,後端會發生什麼事情呢?

圖片描述

圖 15

圖15所示為一個灰度升級階段。首先原始階段有三個例項,假設每個版本都是V1,現在要升級兩個例項,後端的做法是增加兩個例項,例項4和例項5,這兩個對應的版本是V2,圖15中橙色部分。等V4和V5徹底被啟動後,我們將老的例項刪除,它的結果是原來的一個V1, 兩個V2,整體灰度升級過程就是這樣子。

有些細節值得注意:

第一,新例項WarmUp,往往剛開始很多內部元件沒有初始化,像記憶體池、執行緒池、連線池需要初始化,所以初始請求處理得往往比較慢,但是又不能不發請求,因為不發請求永遠熱不起來。做法是設定預熱時間段,定時間段的權重,保證在這個時間段預熱好之後就承擔正常的流量。

第二,老例項CoolDown。如果直接刪除老例項,對服務的可用性沒有影響,因為老的請求正在被處理。做法是:使用Docker StopWait,當用Docker Stop停止一個容器時,它預設的行為是先發一個SIGTERM,如果不指定StopWait時間,就會馬上發一個SIGKILL訊號,這樣你的容器將被直接殺死。若一個容器正在處理請求,那麼我們希望能有一段時間讓我們將請求處理完後,優雅的關閉,否則會影響可用性。所以使用這個命令時,可以設定StopWait時間,若等待超過設定的時間,卻發現容器沒有優雅關閉,再發SIGKILL訊號。另外在業務層面,老例項在CoolDown時,應該設成一個關閉中的狀態,避免新的請求再打進去。

第三,保留足夠的計算冗餘。這個是如何考慮的呢?因為升級需要考慮步長,如果升級的步長大於計算的冗餘例項數,意味著剩餘的等待例項會承擔更多的請求,多於預期的請求會影響服務的質量,因為請求的時間會變長。所以選用的升級步長應該小於冗餘的例項數。

資料流

圖片描述

圖 16

剛才講的是控制流,包括如何註冊、如何建立一個新的例項。而這些資料流的請求過來是什麼樣的?這涉及到一些內部元件,即雲端儲存的伺服器。伺服器後面有資料處理,自定義資料處理會從之前說的狀態服務中獲取某一個後端例項列表,由它做負載均衡傳給後端。在後端資料流的維度上,每臺機器還有一個Fetcher,它接收到請求後會把請求路由到本機,並執行例項。同時,它會做資料下載的工作,我們出於對使用者隱私安全的考慮做了一些混淆,不允許他直接下載,而是轉化成本地的。這是資料流的流程。右邊有一個元件叫DiskCache,是一個快取叢集。

圖片描述

圖 17

圖17是資料鏈路的V2版本,是一個思路,還沒有完全實現。唯一的改動是在例項之間加了一個佇列,當然這個佇列是可選的。當前所有請求都放在這個佇列中,由UFOP例項獲取請求,處理完成後將結果返回,這是一步處理的過程。這個佇列有什麼作用?前面提到,調整例項可以做手動伸縮,沒有說自動伸縮。而這個佇列可以做自動伸縮。

自動伸縮設定

自動伸縮需要使用者做一些配置,一個是預設的例項數,也就是第一次使用者上傳一個自定義資料處理的版本,上傳完成後調整例項數,調整成10個,還要設定一個平均單例項待處理任務數的配置,假如,這個設定是10,當佇列裡面有小於100個待處理任務數時是不需要伸縮的,但是當佇列裡面的任務數大於100,比如到110就要伸縮了,因為單個例項平均待處理的任務數已經超過,這個時候再增加一個例項是最適合的選擇。通過對佇列的監控,可以達到自動伸縮。

自動伸縮的後端

圖片描述

圖 18

圖18是自動伸縮的後端。最上面中間有一個Scaler元件,會實時監控佇列長度,拿到每個佇列的配置,即平均單任務待處理任務數。它根據這兩個資訊,可以實時地發出調整例項的請求,比如平均單任務處理數超過預想時,它可以自動增加一個例項,並告訴排程器,排程器會找相應的節點啟動例項,啟動後告訴Keeper,Keeper會把這些資訊記錄下來。回到前面的資料流,及時從裡面獲取資訊,達到自動伸縮的目的。

如何應對自定義資料處理的挑戰

解決方案:

第一,安全性。這一部分做法比較簡單。自定義資料處理單個物理機上的容器數約幾十個,這與CPU的核數有關。可以設定某個範圍的埠不互訪,以達到容器不能互訪的目的,從而獲得安全性;

第二,隔離性。可以限定某一個容器只用指定配額的CPU和記憶體,這取決於使用者的配置;

第三,可伸縮性。首先實現了一個簡單高效的容器排程系統,它是支援秒級伸縮的;其次是暴露伸縮API,使用者可以手動伸縮達到伸縮目的;最後是利用佇列長度,達到自動伸縮的目的。


2016年9月22日-23日,SDCC 2016大資料技術&架構實戰峰會將在杭州舉行,兩場峰會大牛講師來自阿里、京東、蘇寧、唯品會、美團點評、遊族、餓了麼、有贊、Echo等知名網際網路公司,共同探討海量資料下的應用監控系統建設、異常檢測的演算法和實現、大資料基礎架構實踐、敏捷型資料平臺的構建及應用、音訊分析的機器學習演算法應用,以及高可用/高併發/高效能系統架構設計、電商架構、分散式架構等話題與技術。
9月4日24點前仍處於最低六折優惠票價階段,單場峰會(含餐)門票只需499元,5人以上團購或者購買兩場峰會通票更有特惠,限時折扣,預購從速。(票務詳情連結)。

相關文章