調(diào)度:Docker 安裝與工作流實(shí)戰(zhàn))
簡(jiǎn)介Azkaban Solo Server 單機(jī)版編譯包面向需要快速搭建工作流調(diào)度環(huán)境的大數(shù)據(jù)開(kāi)發(fā)者、運(yùn)維人員及個(gè)人學(xué)習(xí)者用于解決 Hadoop 作業(yè)與 Shell、Java 任務(wù)按依賴(lài)順序自動(dòng)執(zhí)行的問(wèn)題。資源為 azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz壓縮包約 42.28MB解壓后可直接運(yùn)行無(wú)需自行編譯。包內(nèi)包含 Web 服務(wù)、執(zhí)行器與內(nèi)嵌 H2 數(shù)據(jù)庫(kù)等組件通過(guò) conf 配置與 bin 腳本即可完成初始化、啟動(dòng)與訪(fǎng)問(wèn)適合小型團(tuán)隊(duì)或個(gè)人在單機(jī)上驗(yàn)證任務(wù)調(diào)度流程。目前已有 256 人學(xué)習(xí)下載。借助該包讀者可快速體驗(yàn)項(xiàng)目創(chuàng)建、作業(yè)定義、依賴(lài)編排、定時(shí)調(diào)度及日志監(jiān)控等完整環(huán)節(jié)并對(duì)照配置項(xiàng)理解單機(jī)部署的目錄結(jié)構(gòu)與運(yùn)行機(jī)制為后續(xù)遷移至集群模式或排查調(diào)度異常提供實(shí)踐基礎(chǔ)。1. 從 azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz 說(shuō)起單機(jī)調(diào)度器到底能扛多少活手里拿到一個(gè)azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz第一反應(yīng)往往是「這玩意兒解壓完能不能直接跑」。能跑而且 solo-server 模式就是 Azkaban 官方給單機(jī)場(chǎng)景留的口子它把 Web Server 和 Executor Server 塞進(jìn)同一個(gè) JVM 進(jìn)程元數(shù)據(jù)默認(rèn)落在本地 H2 文件庫(kù)不需要 MySQL不需要多節(jié)點(diǎn)協(xié)調(diào)。對(duì)于只有幾臺(tái)機(jī)器、幾十個(gè)定時(shí)任務(wù)、又不想維護(hù)一套分布式調(diào)度集群的團(tuán)隊(duì)來(lái)說(shuō)這個(gè) tar.gz 反而是最省心的起點(diǎn)。它解決的問(wèn)題很具體把散落在 crontab 里的腳本收攏成有依賴(lài)關(guān)系、有重試、有日志、有失敗告警的工作流。適合誰(shuí)數(shù)據(jù)量不大、任務(wù)鏈路不復(fù)雜、運(yùn)維人手緊張的小團(tuán)隊(duì)或者你只是想在本機(jī)先把 Azkaban 的 job 語(yǔ)法和調(diào)度邏輯摸熟。但 solo-server 的邊界也很硬——它不適合高并發(fā)觸發(fā)、不適合多執(zhí)行器橫向擴(kuò)展這一點(diǎn)后面會(huì)反復(fù)提到。2. 解壓即用的 solo-server目錄結(jié)構(gòu)、啟動(dòng)鏈路與 JVM 參數(shù)2.1 tar.gz 解開(kāi)后到底有什么先把包解開(kāi)別急著啟動(dòng)先看清楚里面裝了什么。不同構(gòu)建來(lái)源的目錄名可能略有差異但 solo-server 發(fā)行包的結(jié)構(gòu)基本固定tar -zxvf azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz cd azkaban-solo-server-0.1.0-SNAPSHOT ls -la典型輸出里你會(huì)看到這幾個(gè)關(guān)鍵目錄目錄/文件作用是否要改bin/啟動(dòng)、停止腳本核心是start-solo.sh/shutdown-solo.sh一般不改conf/azkaban.properties、log4j.properties、global.properties必須改lib/所有依賴(lài) jarsolo 模式已打包齊全不動(dòng)plugins/job 類(lèi)型插件如 jobtype、common按需加sql/H2 建庫(kù)腳本首次啟動(dòng)自動(dòng)執(zhí)行不動(dòng)executions/運(yùn)行時(shí)生成存放執(zhí)行日志不動(dòng)解壓后先確認(rèn)conf/azkaban.properties存在這是整個(gè) solo-server 的命門(mén)。如果這個(gè)文件缺失或路徑寫(xiě)錯(cuò)啟動(dòng)腳本會(huì)在初始化階段直接拋異常日志里通常是一句Cannot find azkaban.properties。2.2 啟動(dòng)腳本背后做了什么solo-server 的啟動(dòng)不是簡(jiǎn)單java -jar它走的是bin/start-solo.sh腳本內(nèi)部會(huì)拼裝 classpath、設(shè)置-Dazkaban.home和-Dazkaban.conf兩個(gè)系統(tǒng)屬性然后調(diào)用azkaban.soloserver.AzkabanSingleServer這個(gè)主類(lèi)。手動(dòng)模擬一遍能幫你排查啟動(dòng)失敗# 進(jìn)入解壓目錄后手動(dòng)拼一條啟動(dòng)命令方便看真實(shí)報(bào)錯(cuò) java -Dazkaban.home$(pwd) \ -Dazkaban.conf$(pwd)/conf \ -cp lib/*:plugins/* \ azkaban.soloserver.AzkabanSingleServer邏輯說(shuō)明azkaban.home告訴程序工作根目錄在哪executions/、projects/都會(huì)基于它生成azkaban.conf指向配置目錄程序會(huì)去這里讀azkaban.properties。classpath 里lib/*和plugins/*缺一不可少了 plugins 會(huì)導(dǎo)致 job 類(lèi)型注冊(cè)失敗提交任務(wù)時(shí)報(bào)Job type not found。參數(shù)說(shuō)明如果你機(jī)器內(nèi)存小默認(rèn) JVM 堆可能偏大可以在start-solo.sh里找到JAVA_OPTS或類(lèi)似變量改成-Xmx512m。solo 模式單進(jìn)程堆給 512M 到 1G 足夠跑幾十個(gè)輕量任務(wù)給太大反而拖慢 GC。2.3 第一次啟動(dòng)必須改的三個(gè)配置打開(kāi)conf/azkaban.properties下面三項(xiàng)不改大概率出問(wèn)題# 1. 時(shí)區(qū)不設(shè)會(huì)導(dǎo)致調(diào)度時(shí)間整體偏移 azkaban.default.timezoneAsia/Shanghai # 2. Web 端口默認(rèn) 8081被占用就換 jetty.port8081 # 3. 元數(shù)據(jù)落盤(pán)位置solo 模式用 H2 本地文件 database.typeh2 h2.path./h2db時(shí)區(qū)這項(xiàng)是血淚經(jīng)驗(yàn)不設(shè)azkaban.default.timezone容器或服務(wù)器 UTC 環(huán)境下你配的0 2 * * *會(huì)在凌晨 10 點(diǎn)觸發(fā)排查半天才發(fā)現(xiàn)是時(shí)區(qū)。h2.path用相對(duì)路徑時(shí)基準(zhǔn)是azkaban.home所以別在別的目錄啟動(dòng)腳本否則 H2 文件會(huì)生成到意想不到的地方。啟動(dòng)成功后訪(fǎng)問(wèn)http://localhost:8081默認(rèn)賬號(hào)密碼通常是azkaban/azkaban首次登錄后立刻改掉。3. 用 Docker 安裝 azkaban solo-server鏡像構(gòu)建與數(shù)據(jù)卷掛載3.1 為什么 solo-server 反而適合容器化熱搜里docker安裝azkaban出現(xiàn)頻率很高原因很實(shí)在solo-server 單進(jìn)程、無(wú)外部數(shù)據(jù)庫(kù)依賴(lài)天然適合打成鏡像。容器化之后H2 數(shù)據(jù)、執(zhí)行日志、項(xiàng)目包都能通過(guò) volume 掛出來(lái)升級(jí)時(shí)換鏡像即可不用在宿主機(jī)上折騰 Java 環(huán)境。但要注意solo-server 是有狀態(tài)的容器刪了 H2 文件就沒(méi)了所以數(shù)據(jù)卷必須掛。3.2 寫(xiě)一個(gè)能復(fù)現(xiàn)的 Dockerfile不要直接找網(wǎng)上來(lái)路不明的鏡像自己寫(xiě)一個(gè)最可控。假設(shè) tar.gz 和 Dockerfile 在同一目錄FROM eclipse-temurin:8-jre # solo-server 對(duì) JDK8 兼容最好別盲目上高版本 WORKDIR /opt/azkaban # 拷貝并解壓發(fā)行包 COPY azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz /tmp/ RUN tar -xzf /tmp/azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz -C /opt/azkaban --strip-components1 \ rm -f /tmp/azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz # 時(shí)區(qū)避免調(diào)度偏移 ENV TZAsia/Shanghai EXPOSE 8081 # 前臺(tái)啟動(dòng)保證容器不退出 CMD [bin/start-solo.sh, , tail, -f, /dev/null]邏輯說(shuō)明--strip-components1把解壓出來(lái)的頂層目錄去掉讓內(nèi)容直接落在/opt/azkaban這樣azkaban.home就是固定路徑。CMD里用start-solo.sh啟動(dòng)后接tail -f是因?yàn)閱?dòng)腳本本身會(huì)后臺(tái)化進(jìn)程然后退出容器會(huì)跟著停必須留一個(gè)前臺(tái)進(jìn)程撐住。參數(shù)說(shuō)明基礎(chǔ)鏡像選eclipse-temurin:8-jre而不是latest是因?yàn)?Azkaban 0.1.0 這類(lèi)老版本在高版本 JDK 上可能遇到反射和模塊化限制。如果你確實(shí)要用 JDK11得額外加--add-opens參數(shù)屬于自找麻煩。3.3 構(gòu)建、掛載、啟動(dòng)的完整命令# 構(gòu)建鏡像 docker build -t azkaban-solo:0.1.0 . # 準(zhǔn)備宿主機(jī)目錄用于持久化 mkdir -p /data/azkaban/{h2db,executions,projects,logs} # 啟動(dòng)容器把有狀態(tài)的目錄全部掛出來(lái) docker run -d --name azkaban-solo \ -p 8081:8081 \ -e TZAsia/Shanghai \ -v /data/azkaban/h2db:/opt/azkaban/h2db \ -v /data/azkaban/executions:/opt/azkaban/executions \ -v /data/azkaban/projects:/opt/azkaban/projects \ -v /data/azkaban/logs:/opt/azkaban/logs \ azkaban-solo:0.1.0邏輯說(shuō)明四個(gè) volume 對(duì)應(yīng)四類(lèi)狀態(tài)。h2db是元數(shù)據(jù)庫(kù)丟了所有項(xiàng)目和歷史都沒(méi)了executions是執(zhí)行日志排查失敗任務(wù)全靠它projects是上傳的項(xiàng)目包解壓目錄logs是服務(wù)端日志。只掛h2db不掛executions容器重建后歷史執(zhí)行記錄會(huì)指向空目錄頁(yè)面點(diǎn)進(jìn)去看不到日志。參數(shù)說(shuō)明-e TZAsia/Shanghai和配置文件里的時(shí)區(qū)雙保險(xiǎn)。端口映射如果宿主機(jī) 8081 被占改成-p 18081:8081容器內(nèi)始終是 8081。3.4 容器里改配置的正確姿勢(shì)配置文件在鏡像里直接docker exec進(jìn)去改容器一重建就白改。正確做法是構(gòu)建時(shí)用COPY覆蓋或者運(yùn)行時(shí)掛載單個(gè)配置文件docker run -d --name azkaban-solo \ -p 8081:8081 \ -v /data/azkaban/conf/azkaban.properties:/opt/azkaban/conf/azkaban.properties \ azkaban-solo:0.1.0掛載單個(gè)文件前宿主機(jī)上那份azkaban.properties必須已經(jīng)存在且內(nèi)容完整否則 Docker 會(huì)把它當(dāng)目錄創(chuàng)建啟動(dòng)直接失敗。這是新手最常翻的車(chē)之一。4. 提交第一個(gè)工作流job 文件語(yǔ)法、依賴(lài)與調(diào)度參數(shù)4.1 最小可運(yùn)行 job 長(zhǎng)什么樣Azkaban 的 job 就是一個(gè).job后綴的 properties 文件。建一個(gè)目錄放兩個(gè)有依賴(lài)的任務(wù)# first.job typecommand commandecho first job at $(date)# second.job typecommand commandecho second job runs after first dependenciesfirst邏輯說(shuō)明typecommand表示用 shell 執(zhí)行command字段。dependenciesfirst聲明依賴(lài)Azkaban 會(huì)先跑first成功后才跑second。依賴(lài)名是 job 文件名去掉.job不是command內(nèi)容。參數(shù)說(shuō)明command里可以寫(xiě)多條命令用連接但更推薦把邏輯寫(xiě)進(jìn)獨(dú)立腳本再調(diào)用job 文件保持干凈。$(date)這種變量在 command 里能正常展開(kāi)因?yàn)樽罱K是交給 shell 執(zhí)行的。4.2 打包上傳與觸發(fā)把兩個(gè).job文件打成一個(gè) zip注意是 zip 不是 tar.gzAzkaban 只認(rèn) zipzip workflow.zip first.job second.job在 Web 界面Projects里新建項(xiàng)目上傳workflow.zip然后點(diǎn)Execute Flow。如果想用命令行觸發(fā)solo-server 也支持curl -k --data actionexecuteprojectmyprojectflowfirst \ http://localhost:8081/executor邏輯說(shuō)明actionexecute觸發(fā)指定 flowproject是項(xiàng)目名flow是 flow 名。solo 模式下 flow 名默認(rèn)取第一個(gè) job 或顯式配置的azkaban.flow。參數(shù)說(shuō)明命令行觸發(fā)需要先登錄拿 session.id上面這條是簡(jiǎn)化示意實(shí)際要帶session.id參數(shù)。生產(chǎn)里更常用的是 Web 界面或 API 封裝別裸調(diào)。4.3 調(diào)度參數(shù)怎么設(shè)才不踩時(shí)區(qū)坑在 flow 配置里設(shè)置定時(shí)核心是 cron 表達(dá)式和時(shí)區(qū)# 在 flow 的 schedule 配置中 azkaban.schedule.cron0 0 2 * * ? azkaban.schedule.timezoneAsia/Shanghai邏輯說(shuō)明cron 是 Quartz 風(fēng)格六位秒 分 時(shí) 日 月 周。0 0 2 * * ?表示每天 2 點(diǎn)整。注意 Azkaban 用的是 Quartz cron不是 Linux crontab 的五位格式多一個(gè)秒位這是最容易寫(xiě)錯(cuò)的地方。參數(shù)說(shuō)明azkaban.schedule.timezone不寫(xiě)就取azkaban.properties里的全局時(shí)區(qū)。如果全局和 flow 級(jí)都設(shè)了flow 級(jí)優(yōu)先。建議全局設(shè)一次flow 級(jí)不重復(fù)設(shè)減少不一致。5. solo-server 避坑與排查五個(gè)真實(shí)翻車(chē)現(xiàn)場(chǎng)5.1 啟動(dòng)報(bào) H2 數(shù)據(jù)庫(kù)鎖現(xiàn)象重啟容器后服務(wù)起不來(lái)日志里Database may be already in use或Lock file exists。原因上一次進(jìn)程沒(méi)正常退出H2 的.lock文件殘留或者兩個(gè)進(jìn)程同時(shí)指向同一個(gè)h2db目錄。解決確認(rèn)沒(méi)有殘留 Java 進(jìn)程后刪掉h2db目錄下的.lock文件再啟動(dòng)。容器場(chǎng)景下docker stop要給足超時(shí)時(shí)間讓 JVM 走完 shutdown hook別直接docker kill。5.2 任務(wù)一直處于 Preparing 不執(zhí)行現(xiàn)象flow 提交后卡在 Preparingexecutor 不接活。原因solo 模式下 Web 和 Executor 同進(jìn)程如果azkaban.properties里 executor 端口配置和實(shí)際不符或者executor.port被防火墻攔內(nèi)部通信就斷了。解決檢查executor.port配置solo 模式默認(rèn)走本地回環(huán)一般不會(huì)被防火墻影響但如果你改過(guò)azkaban.properties里的 host 綁定可能綁到了錯(cuò)誤網(wǎng)卡?;謴?fù)默認(rèn)配置對(duì)比測(cè)試。5.3 中文日志亂碼現(xiàn)象job 輸出里的中文在 Web 界面顯示成問(wèn)號(hào)或方塊。原因JVM 默認(rèn)字符集不是 UTF-8或者 job 腳本輸出編碼和頁(yè)面解析編碼不一致。解決啟動(dòng)參數(shù)加-Dfile.encodingUTF-8同時(shí)在azkaban.properties里確認(rèn)沒(méi)有覆蓋編碼相關(guān)配置。job 腳本里顯式export LANGen_US.UTF-8或zh_CN.UTF-8。5.4 上傳 zip 后提示項(xiàng)目為空現(xiàn)象zip 上傳成功但項(xiàng)目里看不到任何 flow。原因zip 打包時(shí)把.job文件套了一層目錄Azkaban 只掃描 zip 根目錄下的.job文件。解決打包時(shí)進(jìn)入 job 文件所在目錄再執(zhí)行zip不要在上級(jí)目錄打包。用unzip -l workflow.zip確認(rèn)文件在根層。5.5 定時(shí)任務(wù)不觸發(fā)現(xiàn)象cron 配好了到點(diǎn)沒(méi)反應(yīng)。原因時(shí)區(qū)偏移、cron 位數(shù)寫(xiě)錯(cuò)、或者 flow 沒(méi)有處于Schedule狀態(tài)只是保存了。解決先看azkaban.default.timezone再看 cron 是不是六位最后確認(rèn) flow 頁(yè)面上的調(diào)度開(kāi)關(guān)是打開(kāi)狀態(tài)。三個(gè)都對(duì)了還不觸發(fā)去logs目錄看 Quartz 相關(guān)日志。6. 從 solo 到可維護(hù)驗(yàn)證調(diào)度是否真的按預(yù)期跑solo-server 跑起來(lái)容易難的是確認(rèn)它「按你以為的方式」在跑。我一般會(huì)做一個(gè)最小驗(yàn)證閉環(huán)建一個(gè)每分鐘觸發(fā)、只輸出當(dāng)前時(shí)間的 job觀(guān)察一小時(shí)內(nèi)日志時(shí)間戳是否連續(xù)且間隔穩(wěn)定。# heartbeat.job typecommand commandecho heartbeat $(date %Y-%m-%d %H:%M:%S)配0 * * * * ?每分鐘一次跑一小時(shí)后去executions目錄翻日志。如果時(shí)間戳出現(xiàn)跳變或重復(fù)說(shuō)明調(diào)度線(xiàn)程被阻塞或時(shí)區(qū)有問(wèn)題。這個(gè)笨辦法比看任何文檔都直接。再進(jìn)一步把關(guān)鍵 flow 的執(zhí)行結(jié)果通過(guò)command里的curl推到一個(gè)你自己的接收端形成最簡(jiǎn)告警。solo-server 沒(méi)有內(nèi)置告警但 job 本身就是 shell能做的事很多。我吃過(guò)虧的地方在于早期覺(jué)得 solo 夠用把所有任務(wù)堆上去直到某天一個(gè)長(zhǎng)任務(wù)把 executor 線(xiàn)程占滿(mǎn)后面所有 flow 全部排隊(duì)。后來(lái)養(yǎng)成習(xí)慣給每個(gè) job 加timeout類(lèi)的前置判斷長(zhǎng)任務(wù)拆細(xì)solo 的線(xiàn)程池就那么大別把它當(dāng)分布式集群使。如果你現(xiàn)在只是想讓調(diào)度先跑起來(lái)、把 crontab 收編solo-server 這個(gè) tar.gz 完全夠用但心里要清楚它的天花板在哪等任務(wù)量和并發(fā)上來(lái)遷移到多執(zhí)行器模式是遲早的事。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取