本文翻譯自Azkaban官網文檔 Azkaban Containerized Executions - Design Doc
作者/關鍵貢獻者: Arvind Pruthi , Janki Akhani , Shardool , Deepak Jaiswal , Aditya Sharma , Abhishek Nath
老架構(裸金屬架構)背景/概覽
- 每一套Azkaban集羣都包含如下組件:一個
web server
,一組部署在物理機上的executor server
,一個mysql(用於保存job
狀態和歷史記錄) - 每一個
executor server
大約可以並行執行10個Flow
(任務流),具體取決於每個Flow
的消耗的資源 executor server
定期(一般1秒)從mysql拉取處於待執行狀態的Flow
,mysql相當於是一個隊列
Azkaban Executor Server的職責
分發任務
Executor Server
定期從mysql拉取flows- 構建flow:解析所有的配置、在內存中構建一個圖、下載依賴文件、分配計算資源(線程池、執行目錄等)
- 觸發調度
調度
- 爲了保證任務並行執行,每一個
executor server
爲每一個flow
都維護了一個獨立的線程池 - 從hadoop name node獲取hadoop tokens以便提交YARN應用
- 每一個
job
都是用admin賬號獨立加載 - 在內存中維護
flow
的狀態機,並及時更新到mysql中,最後把flow
產生的日誌都寫到mysql中
管理Flow
executor server
對外暴露AJAX API,提供暫停、強制停止、恢復等操作,超過10天的flow
會被強制停止。
管理日誌
AJAX API支持流式日誌,可以實時觀察執行中的flow
/job
,當flow
/job
結束後,所有的日誌都會以15MB每塊的大小保存到數據庫中。
部署
- 部署
executor server
,先把新版代碼準備好,啓動前會先完成一些必要的測試,以便驗證環境符合預期 - 測試通過後,把
executor server
置爲不可用模式,確保不會從數據庫拉取flow
(particular executor
除外) - 啓動新版代碼,狀態變爲可用,恢復任務的執行
裸金屬架構的問題
互相影響(無資源隔離)
Azkaban支持多種Job類型,也允許用戶自己上傳代碼,所以很容易發生部分任務佔用過多的資源(CPU、內存、磁盤),進而拖垮整個executor server
,影響此server上的所有任務
彈性伸縮/維護問題(僵化的架構)
網站可靠性對彈性伸縮能力具有強烈的訴求,數十年前的架構就已經存儲了。但是裸金屬架構並不能從最近的新技術上獲益,這在雲計算領域已經司空見慣了。
缺少金絲雀發佈系統
Azkaban的executor server
是所有計算的門戶,除了它自身的功能代碼、配置、任務類型之外,還屏蔽了hadoop、安全管理器、spark等基礎計算設施。當前缺少金絲雀機制以支持細粒度的新功能驗證。 根據Azkaban在Linkedin的使用的經驗看,在缺少合適的金絲雀系統的時候,每次滾動發佈代碼是多麼的痛苦。
無視YARN內部的任務隊列
Azkaban有自己的任務隊列和分發機制,這樣可以最大限度的發揮executor server
的能力,但是它自己的隊列與YARN的隊列並不匹配,這經常導致YARN集羣過載。
發佈的問題
發佈過程中的環境驗證環節可能要持續10天,在這期間executor server
都是不可用的,並且CPU和內存是閒置的。一次發佈如果有問題,需要executor server
帶病運行的情況下,不斷的嘗試修復,可能導致GC暫停或者OOM,並且會污染其他的指標。
容器化的關鍵需求
- Azkaban的
web server
動態的爲每一個flow
創建一個容器,也就是爲每一個flow
提供一套完全獨立的運行環境。 - 快速響應激增的資源需求(可伸縮的架構)
- 提供一種機制以支持Azkaban各個組件各自獨立進化
- 把發版的控制權交給對應的用戶:運行平臺、Azkaban本身、用戶Job
- 讓用戶自己選擇Azkaban或者
JobType
的版本(在更新基礎設施的時候尤其有用)
- 提供一套垂直的金絲雀系統以支持
Azkaban/jobtypes
和運行平臺完全控制各自的代碼更新
未來的擴展
- 爲多個組件開發細粒度的金絲雀系統以支持各自獨立發版
架構概覽
- Azkaban採用
Disposable Container
(一次性容器)的模式,這意味着每當flow
被調度之前都會創建一個新的POD,並且在flow
結束之後銷燬。 - 資源隔離體現在
flow
級別(而非job
),jobs
或subflows
都是flow
的一部分;Job
級別的資源也探索過,但是最終放棄了:
- 它會極大的破快原來的架構,爲了實現
job
級別的資源隔離,大部分的代碼都需要重寫; - 爲每一個
job
創建一個pod會造成過多的資源消耗,還有一種做法是在一個POD裏面包含多個容器,但是這也會導致大部分flow
和job
相關的代碼被重寫;也許未來會重新考慮這個設計;
- 創建POD的時候使用默認的cpu、內存資源,也可以通過參數指定需要申請的資源
- 在這樣的設計下,
web server
必須部署在k8s之外,這樣做並不妨礙它與flow
和log
之間的通信,它們之間的通信通過Ingress Controller
實現,這樣就不需要導出Flow
的POD了; - 爲了實現上述第三點需求,
flow
的pod的執行環境必須動態創建:
- 在任務分發的環節,會提供動態選擇組件版本的功能,以便提供
executor server
的運行環境 - 一系列的容器初始化工作將通過衆多組件的不同版本的組合來完成
- 動態選擇的功能可以用來實現組件的金絲雀發佈
- 需要一些Admin API來完成鏡像的管理工作
詳細設計
鏡像管理
- 使用docker鏡像來創建
flow
的執行環境,爲了實現上述第三個需求,要使用預製的container模板來創建POD,下文的分發邏輯
一節會詳述; - Azkaban的運行環境由以下依賴組成:
依賴類型 | 描述 |
---|---|
平臺依賴 | Hadoop、Hive、Spark、Pig、Dali、Ksudo等 |
Azkaban核心 | Azkaban代碼包、配置、安全項,由Azkaban管理 |
JobTypes | JobType開發人員開發的代碼/配置,由Azkaban管理,如KafkaPushJob , SparkJob 等 |
- Azkaban核心在基礎鏡像(RHEL7)上加一層,形成
Azkaban基礎鏡像
- 其他的平臺依賴、JobTypes各自在
Azkaban基礎鏡像
之上新增獨立的層。爲了保持鏡像體積小,節約下載時間,可以使用busybox
或者alpine
技術來實現 - 有的開發者的
job-types
鏡像需要特別定製,不依賴Azkaban。比如Kafka Push Job
FROM container-image-registry.mycorp.com/rhel7-base-image/rhel7-base-image:0.16.9
ARG KPJ_URL=https://artifactory.mycorp.com/kafka-push-job/kafka-push-job/0.2.61/kafka-push-job-0.2.61.jar
RUN curl $KPJ_URL --output ~/kafka-push-job-0.2.61.jar
- 每個
job-type
都會基於一個公共的預製鏡像,這個預製鏡像會把所有的代碼包和配置文件都放到容器的外部捲上,此外部卷也會被掛載到應用容器(基於Azkaban基礎鏡像
) job-type
開發者使用鏡像管理API
構造job-type
鏡像,構造出來的鏡像可以作爲默認的job-type
鏡像,flow
的開發者可以使用DSL指定job-type
鏡像的版本flow
執行的過程中使用version-set
和version-number
來唯一標識它的依賴的組件的狀態;這也有助於在測試環境復現失敗的flow
,便於排查問題
鏡像管理API
API使用流程圖
數據庫ER圖
分發邏輯
狀態流程圖
kubernets的安全性
初始化容器
運行Flow的容器
Ingress控制器
日誌
狀態頁面
使用裸金屬架構解決上述問題如何?
在kubernets上debug Azkaban
待解決事項
- 擱置了通過參數指定
flow
使用的鏡像的版本 - 擱置了通過參數設置版本
- 擱置了通過參數指定
flow
容器需要的CPU和內存 - debug問題
- 更多關於配置的技巧