基於Kubernetes的瓜子云的任務排程系統
很大的挑戰。
接下來我講詳細介紹一下瓜子云的任務排程系統搭建所遇到的問題和解決方案。
需求
瓜子最早的時候,任務排程用的是Crontab,後來由於資料倉庫的複雜排程需求,我們引入了Airflow。Airflow支援DAG依賴,失敗重試,歷史狀態記錄,log收集等多種非常使用的功能。
Airflow有很多問題:
- Airflow的Worker需要手動搭建,可擴充套件性不好。
- Job程式碼更新之後,需要手動部署到Worker上,非常繁瑣。
- Airflow Worker的環境太多,由各個團隊自行維護,維護成本太高。
- 瓜子云平臺搭建之後,所有機器都會被回收,各業務線擁有的機器將會很少,Worker將會沒有地方部署。
此外,我們還希望排程系統有如下的功能:
- DAG之間的依賴
因為資料倉庫的ETL非常複雜,沒有任何人能夠完全掌控整個流程,我們需要把整個ETL切成很多小的DAG,這些DAG之間是有互相依賴的。 - 自動擴容縮容
瓜子這樣的特點,晚上有大量批量任務需要跑,白天每個小時,每一分鐘都會有增量任務需要跑。 - 環境隔離
瓜子的語言多種多樣,每個團隊都有很多不同的Job在不同的環境上跑著,管理很混亂,還有可能互相影響。
介於這樣的問題,我們準備把排程系統部署到Kubernetes上,利用Kubernetes的環境隔離,自動擴容縮容的特性。
Airflow的原始架構

Airflow分為Master節點和Worker節點兩種。Master節點有Scheduler和Web兩種服務,Worker上有Executor一種服務。
我們從任務的排程過程來看看他們是怎麼工作的:
- Scheduler讀取DAG配置檔案,將需要執行的Job資訊發給RabbitMQ,並且在MySQL裡面註冊Job資訊。
- RabbitMQ裡面按照環境有很多channel,Scheduler的Job會根據需要執行的環境發到相應的channel裡面。
- Executor消費RabbitMQ相應的channel,進行執行,執行結果更新到MySQL中,並將log暴露到Executor的某個http埠上呼叫,並存入MySQL中。
- Web讀取MySQL裡面的Job資訊,展示Job的執行結果,並從MySQL中獲取log的url,展示log。
- Web上發現執行錯誤的Job可以點選重試,直接傳送Job給RabbitMQ裡,並改變MySQL裡面Job的狀態。
Airflow上雲的問題
Airflow上雲有很多問題,我們這裡只列舉一些比較麻煩的問題來說一下。
1. Scheduler HA
Airflow不支援多個Scheduler,多個Scheduler一起啟動時會報錯,所有Scheduler都會掛掉。當我們在Kubernetes上滾動更新時,需要先啟動一個新的Scheduler,然後再幹掉舊的Scheduler。這樣就不可避免會出現多個Scheduler並行的情況。
2. 配置更新
Job配置更新後,所有元件自動更新最新配置的問題。Airflow中所有元件都需要拿到DAG配置才能正常工作。其實原理上大可不必,可能是Airflow設計的時候沒考慮到分別部署的情況吧。
3. Web訪問Worker
Web需要通過Worker的HOSTNAME來訪問Worker上的log,但是Kubernetes中不支援通過HOSTNAME來訪問。
4. Worker不同環境
Job需要在不同環境中執行,不可能在Kubernetes中為所有環境單獨搭建長期執行的Pod。
問題的解決
1. Scheduler HA
我們引入了ZooKeeper,在Airflow Scheduler啟動時去監聽ZooKeeper下的/airflow/scheduler。
如果發現下面有個running的key,就說明已經有Scheduler在運行了,然後一直監聽,直到running timeout。
如果發現沒有,就可以啟動Scheduler,然後在/airflow/scheduler下注冊running,把自己的資訊,作為value。每隔5s註冊一下,該running timeout時間設為30s。
這樣就解決了HA的問題。
2. 配置更新的問題

配置更新的配置流程為:
- 我們自己寫了一個Watcher的元件,通過連線Git的Webhooker,監聽git merge資訊,一旦收到merge的資訊,就會把Git的commit hash值存入etcd的/medusa/airflow/config 裡面。
- 我們在Scheduler旁邊放一個sidecar —— Confd,兩個容器作為一個Pod,共享一個資料夾作為airflow的DAG配置資料夾。
- Confd監聽etcd的/medusa/airflow/config key,發現更新就觸發git pull操作。
這樣子,我們就拿到了最新的配置檔案。通過相同的方式部署Web和Worker即可。
3. Web訪問Worker的問題
這個問題,我們在Airflow原始碼裡面改了一點東西,用IP地址代替HOSTNAME解決了問題。
只需要修改models.py 裡面的這行程式碼就好。

4. Worker不同環境
我們的解決方案是,不在Worker裡面放任何環境,只負責由給定的image和script來生成Kubernetes job xml,並啟動Job和監控。我們將在下面重點介紹。
Airflow雲上架構
經過上述改動後,雲上Airflow的架構就改成了下圖這樣

整個任務排程流程為:
-
Scheduler讀取任務配置資料夾資訊,發現有個任務需要執行。所有的執行命令都是:
kjob --image myjob:latest --script 'hive -e test.sql'
這個樣子的,也就是所有任務都通過KJob來執行。
- Worker裡面我們用Golang寫了一個KJob的指令碼,內部做了如下幾件事
- 通過傳入的兩個引數image和script,生成job.yml
- 通過job.yml 啟動Kubernetes Job
- 一旦Job開始正常執行,監聽log
- Job完成,獲取job的狀態並返回成功與否
這個樣子,我們就把環境依賴的事丟給開發者自行維護了。
這時的任務更新流程如下圖:

我們寫了一個med-sdk,其功能是把程式碼打成Docker映象,並且push到Docker Registry裡面。這裡我就不詳細展開了,有興趣的可以看我的之前的分享。
詳細流程為:
- 如圖右側,任務程式碼改動後,會自動觸發med-sdk構建Docker映象,併發布到Docker Registry裡面,映象以latest作為version,確保每次都拉取最新版的映象。
- 如圖左側,Airflow配置改動後,Watcher會收到Git的merge資訊,並更新ETCD。Scheduler,Worker會更新相應的配置檔案。
- Worker收到最新Job之後會拉取最新的映象部署服務。
整個Airflow上Kubernetes的難點算是處理完了。
感謝大家的傾聽。
Q&A
Q:請問下自動觸發med-sdk構建Docker映象,med-sdk是什麼開源專案,能介紹下麼?
A:med-sdk是瓜子自行開發的一個工具,用於把程式碼打成Docker映象包。每個Git裡面只需要新增一個med.yml就可以實現。
Q:請問為什麼要整合Kubernetes?
A:Airflow的Worker需要手動搭建,可擴充套件性不好;Job程式碼更新之後,需要手動部署到Worker上,非常繁瑣;Airflow Worker的環境太多,由各個團隊自行維護,維護成本太高;雲平臺搭建之後,所有機器都會被回收,各業務線擁有的機器將會很少,Worker將會沒有地方部署。
Q:Airflow處理的排程量是什麼規模,也就是批量任務會不會阻塞,一次併發有多少Pod,多少容器例項,一套Kubernetes Master能否扛得住,方便給個數據量進行參考嗎?
A:目前瓜子每天有2000個任務。任務的執行地點都是在Kubernetes上的,不會阻塞。併發的Pod個數是由同時處理的Job數定的,Airflow的Worker有設定一個Worker可以同時跑幾個Job。我們併發Pod有20個。一套Kubernetes可以抗住我們的規模。資料量不好給,因為任務的計算量不好估算,有的大有的小。
Q:為什麼不考慮Celery之類的任務佇列?
A:首先是我們之前選用的是Airflow,用Python寫的DAG,非常符合我們的需求,我們的DAG需求很大,比如資料倉庫,所以選擇了Airflow。
Q: 有做過類似軟體的對比麼,差異在哪?
A:Kubernetes目前被Docker官方支援。Mesos用C寫的,不好運維。Rancher社群不夠大。其實功能大家都支援,主要是社群。
Q:併發的容器數量是多少,實際的Docker例項個數量級,20個Pod可大可小。方便給個參考嗎?謝謝!
A:我們測過每臺機的上限在100個,我們的機器是128G,24cores。我們Airflow的Worker有20個Pod。
本文轉自kubernetes中文社群- ofollow,noindex">基於Kubernetes的瓜子云的任務排程系統