影片生產大映象最佳化實踐

陶然陶然發表於2023-02-20

  本文介紹了愛奇藝影片生產技術團隊針對大映象的多種最佳化方案,充分利用現有的 Docker 映象分層相關技術,在其基礎上進行最佳化和創新,在開發人員無感知的情況下,最佳化映象大小,同時提升了構建速度,減少了分發所需要的頻寬。

   01 背景

  愛奇藝影片轉碼服務透過容器技術進行分散式任務的提交與執行,依賴 Docker 映象進行版本釋出以及部署,然而隨著版本的迭代,Docker 映象內容越來越多,體積越來越大,對映象的儲存、分發、拉取造成了較大的影響。

  目前 Docker 映象的設計是按照 Dockerfile 分層儲存,會快取已有的映象層,構建新映象時,如果某映象層無變動,就直接使用,無需重新構建和分發。在傳統的映象中,可以將頻繁變動的程式碼放入最底層,將環境、工具等無需變動的檔案放置在上層,利用 Docker 層快取機制,提升構建速度。例如,圖1所示的映象在每次更新時,往往前面數層可以保持不變,只需要重新構建最後一層,大小僅在 100MB 左右,構建十分迅速。然而愛奇藝影片轉碼服務映象等一些大型映象無法根據分層結構進行最佳化,在實際使用中存在諸多問題。  

圖1 傳統 Docker 應用場景

  影片轉碼環境所使用的 Docker 映象採用 All in One 的方式,包含各種圖片、影片編解碼工具、業務流程程式碼、素材檔案、推理模型等等,變動均非常頻繁;映象大小達到 18GB,體積較為龐大;版本頻繁迭代,每週構建多次,發版部署不止一次;映象單次構建時間非常長,環境、工具、程式碼等都在頻繁變動,無法按照變動頻率對 Dockerfile 分層,底層的佔用空間就非常大,每次構建甚至一個小小的檔案改動都需要重新構建多層。例如,在圖2所示場景中,每次素材檔案的改動,均會引發 15GB 的檔案更新,構建時間長,分發所需頻寬較大;部署節點多,釋出的新版映象需要在全國各個DC上千個節點中部署執行,對 Docker Register 造成巨大壓力,例如,新版映象部署在 5000 個節點上,下載速度約 10MB/s,則峰值頻寬可達到 48.8G,成本極高,與此同時,映象部署會佔用昂貴的跨 DC 專線資源,容易對公司其它業務造成影響。  

圖2 影片轉碼 Docker 應用場景

   02 解決方案

  1、差異化構建

  按照 Docker 映象的分層原理,Dockerfile 將在每層檔案發生變化時,會重新構建此層與此層之上的所有層。差異化構建拋棄了原有的 Dockerfile 構建方式,不重新構建任何舊層,而是基於已經分發的舊映象建立新層,將新版本的增量更新檔案放入新層中,提交新層後就得到了新的映象,在這種情況下,只需分發最後一層即可,原理如圖3所示。  

圖3 映象增量更新原理

  具體實現如下:需要一個 Base 映象,即依賴映象,包含了專案除了程式碼與檔案之外的全部依賴項。這個 Base 映象可以是 Docker 官方團隊釋出的基礎系統映象,也可以是透過 Dockerfile 編譯的映象,甚至可以是團隊的歷史映象。接著,透過執行 Base 映象,建立臨時容器,獲取程式碼以及相關檔案的部署目錄的對映關係,將新版本檔案掛載入容器臨時目錄內。透過遞迴遍歷計算目錄的檔案 Hash 差異,根據檔案差異,將差異檔案寫入容器中,提交容器新層,完成新版本映象的構建。構建完成後,需要在構建系統中記錄新映象(新層)的版本號。

  在構建系統中記錄當前版本之後,下次構建時,可用本次版本的映象(或一個已被分發的映象版本)作為 Base 映象,重複以上構建步驟,每次構建都將疊加屬於此版本的僅有差異檔案的新層。如圖4所示。

  在檔案比對的步驟中,由於程式碼、檔案管理大部分採用了 Git 等版本管理手段,可以透過記錄舊層中程式碼提交的版本,直接透過版本管理工具計算歷史版本與當前版本的檔案差異,代替遞迴計算檔案 Hash 的方式獲得差異檔案列表,進一步提高構建效率,構建流程如圖5。

  當映象檔案層數大於最大層數(127 層),或者需要更新映象的相關依賴,可重新構建 Base 映象。  

圖 4 差異化構建流程  

圖 5 Git 差異化構建流程

  2、用時下載

  映象越來越大,但是並不是映象中的每個檔案都會被用到,這些檔案可以剝離映象,在使用時再下載。如轉碼服務可以在 CPU 和 GPU 上執行,但部分工具或者一些依賴庫如 CUDA 庫只在 GPU 環境中才被使用。一般的解決方案是將 CPU 與 GPU 的 Base 映象分離,再分別進行構建。然而,隨著映象的成倍增加,在構建、拉取過程中會消耗許多資源,且需維護多個版本。透過用時下載,可在執行容器請求特定資源時,資源再被下載並軟鏈至容器中,從而節省構建映象所需資源。  

圖6 用時下載流程

  由於容器被隨機分配在宿主機上,無法確定宿主機上是否已存在所需資源,因此,當容器索取資源時,需要首先進行資源比對。在宿主機上建立快取目錄並掛載在容器中,當確認宿主機快取目錄沒有資源,或資源不完整、版本不對時,將請求遠端倉庫進行下載。為防止多個任務同時下載造成冗餘,程式獲取到資源時需加檔案鎖阻塞。下載結束後,對比 md5 值,保證資源完整即可釋放鎖。

  所有可分離資源的最新版本會上傳至遠端倉庫中進行維護。映象中將記錄資源資訊,其中包括資源名稱、資源所在路徑、md5 值、遠端地址。透過 md5 值確認資源可用後,將資源軟鏈至容器中,任務可正常執行。

  3、映象去重

  映象構建所涉及的業務繁雜,有些工具或之前在映象中用 YUM/APT 安裝的庫已經不再使用,但由於工具缺乏明確的責任人,很難得到及時清理,久而久之,映象體積愈加臃腫。映象去重是指透過執行任務,明確任務所需依賴,從而使映象所使用資源體積達到最小。具體實現方式為對映象進行 coverage test,透過 ldd 動態連結,找到執行程式所需的所有動態庫,與 Dockerfile 中安裝的工具做比較,未使用的動態庫可視為該版本已不需要,可刪除進行去重,如圖7所示。  

圖7 映象去重流程

  4、節點預熱&灰度分發

  映象分發到所有節點,往往需要大量時間,且在分發瞬間會耗費大量頻寬。我們首先在 CI/CD 流程中,在版本透過測試後,自動對節點進行預熱,提前分發映象至各節點。分發過程中,按每次 10% 節點的比例拉取最新的 Docker 映象層,防止分發請求瞬間將專線頻寬佔滿。透過灰度分發和節點預熱雙措並舉,可使頻寬峰值降低 85% 以上。  

圖8 CI/CD 流程

  5、Dragonfly

  Dragonfly 是開源的映象分發系統,透過 P2P 的方式加速分發,經過我們的測試,Dragonfly 在例項較多、Docker 映象較大的場景下,利用 Dragonfly 進行 P2P 下載,可有效提高分發效率,節省網路頻寬,但會增大例項所在物理機的 net.out 頻寬,且穩定性有待提高。目前我們會在跨專線、跨公有云的場景下進行使用。

   03 總結及展望

  透過對映象的多維度最佳化,映象大小從 18G 降至 9G,同時,映象構建時間從 30 分鐘降至 5 分鐘以內,分發和儲存所需成本也隨之降低。映象最佳化對於開發人員無感知,無需瞭解本發明的構建原理,因此,可快速應用於各種新舊專案的映象構建中。未來我們將繼續深入研究,持續精簡映象體積。

來自 “ 愛奇藝技術產品團隊 ”, 原文作者:影片生產技術團隊;原文連結:http://server.it168.com/a2023/0220/6790/000006790119.shtml,如有侵權,請聯絡管理員刪除。

相關文章