什么是任務調度系統?
A job scheduler is a computer application for controlling unattended background program execution of jobs (from wikipedia)
簡單來說就是你有很多任務, 彼此之間執行必須有一定順序構成一個DAG. 如下圖所示:
為什么需要任務調度系統
數據終究是要拿來計算的. 假設我們從小白開始搭建一套數據計算系統.
-
數據計算第一階段:
- 搭建了 Hadoop, 執行幾個 Hive 腳本統計數據
- 為了簡單, 代碼直接寫到一個 HQL 文件中, 每天晚上 12 點執行, 計算前一天的運營數據
-
大數據系統建設第二階段:
- 數據需求開始增多, 你每天加班加點的開發新的計算, 放到同一個 HQL 文件中
數據計算第三階段: 拆
問題:
-
有一天你發現 HQL 文件已經一千多行了
- 計算邏輯復雜, 牽一發而動全身
- 時不時會有某個計算失敗, 全部計算重新執行很浪費時間
- 老板看你忙不過來, 給你招了幾個小弟一起開發. 需要分拆任務
大 HQL 文件根據邏輯拆成幾十個小的計算文件
每個開發負責一塊業務的計算, 使用簡單的 bash 腳本按順序調用
hive -f
執行幾十個 HQL 文件進行計算小心安排幾十個計算的先后順序, 避免順序顛倒導致計算錯誤
計算變成了一大串鞭炮, 挨個執行
數據計算第四階段: 并發
問題:
任務太多, 串行執行時間太長
一個任務失敗, 后續任務全部等待, 效率很低
review 所有的計算任務, 找到可以并發執行的計算任務
想辦法并發執行互不相干的一些任務, 計算任務僅僅依賴必須依賴的前置任務, 因此計算任務由
一串鞭炮
組成了一個 DAG
因此, 我們對調度系統的需求如下:
- 分解計算任務. 計算任務不單只有 Hive
- 任務執行之間能夠互相依賴, 前置任務失敗, 后續依賴任務不執行
- 盡可能的并行執行任務, 縮短執行時間
- 定時觸發計算任務. daily/weekly/hourly 等
- 任務失敗報警. 對于計算失敗的重要任務, 報警必不可少. 哪個數據工程師沒有半夜起來修復過失敗的計算任務?
從系統的角度來說, 調度系統應該包含以下幾個模塊:
- Scheduler 模塊: 負責調度 DAG, 根據條件觸發任務執行
- 按時啟動任務, 最好支持 Cron 表達式
- 提供任務執行結果上報接口, Executor 模塊通過該接口告知 Scheduler 任務執行狀態. 接口不一定是 restful api, 數據庫可以作為接口
- DAG 執行過程中, 根據已經成功的任務, 決定下一個滿足依賴條件的任務
- 報警模塊. 任務執行失敗后, 根據配置的規則, 給響應的值班人員報警.
- Executor 模塊: 負責根據 Scheduler 指令, 執行對應的任務
- 獲取任務執行接口. Scheduler 通過接口通知 Executor 執行任務
- 重試機制. 提供一定重試機制, 當任務因為網絡抖動等原因失敗時自動重試, 減少報警次數, 也就是減少數據工程師半夜爬起來的次數 LOL.
- 重試次數不能太多, Fail Fast 在 Batch 計算業務中也同樣重要
- 計算任務必須具備冪等性. 例如, 一個 Hive 計算寫成如下方式
-- test.tmp_test_table 用于存儲中間計算結果
CREATE TABLE test.tmp_test_table AS
SELECT data_date,
count(*) AS cnt
FROM test_data
GROUP BY data_date;
是否具備冪等性? 回答是否定的. 因為一旦任務執行成功, 第二次執行時 test.tmp_test_table
已經存在, 任務會報錯 (Table Already Exists).
如何修改? 很簡單, 前面加一句 DROP TABLE IF EXISTS test.tmp_test_table
便可.
- 代碼部署方式. Executor 要執行計算必須有代碼. 那如何獲取計算任務的代碼就是一個問題. 從擴展性來說, Executor 一定要支持分布式部署, 也就是一個 Scheduler 多個Executor.
- 一種方式是, 將計算代碼部署到每個 Executor 節點.
采用這種方式要求使用者必須有自動化部署, 因為計算代碼漏部署或者錯誤部署了一臺 Executor, 很有可能是測試階段由于計算任務沒有被分配到錯誤的 Executor 節點而真正夜間計算時錯誤的 Executor 節點被分配了計算任務導致失敗. - 改進的一種方案是: 代碼僅僅部署到 Scheduler 節點, 每次 Executor 拿到計算任務后, 通過 Scheduler 提供的 API 下載相應的計算代碼.
- 思考: 以上兩種方案解決的僅僅是計算代碼的部署問題, 沒有考慮計算代碼依賴的 Library 的問題. 如果計算代碼中有外部依賴, 比如一些 Native 的 Library, 就需要在每個 Executor 上安裝所有計算代碼依賴的 Library. 最悲催的是: 如果兩個計算任務依賴的 Library 的版本不一樣就悲催了.
- 一種方式是, 將計算代碼部署到每個 Executor 節點.
- Web UI 模塊:
- 查看 DAG,
- 手動修復失敗任務
- 看日志. 任務執行日志查看.
- 不支持 Tail 功能
- 不支持 Grep 功能, 基本上可以斷定是: 不好用!
調度系統的需求明確了, 甚至各個模塊的功能都想清楚了, 我們就可以像"選妃子"一樣, 看市面上有哪些可選的開源項目
開源調度系統
兩個系統都很不錯, 也能夠解決大多數需求. 網上針對兩個的比較也很多, 我就不班門弄斧了.
繼續上文中關于計算代碼依賴 Library 的思考
- 計算任務代碼可以分成兩部分:
- 調度配置文件. 前置任務, 啟動參數等
- 計算代碼文件. 這才是會產生外部 Library 依賴的代碼.
因此, 如果我們把計算代碼裝進 Docker, 不就徹底解決了依賴問題?
-- EOF --