☰
Azkaban solo-server 单机任务调度实战:解包、配置、跑通与避坑
2026/10/17 10:45:34 网站建设 项目流程

简介:Azkaban 是一款由 LinkedIn 开源的工作流执行引擎,常用于大数据处理任务的调度与管理。这份 azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz 是其单服务器部署版本,已编译打包,解压即可运行,适合小型团队、个人开发者以及需要快速搭建作业调度环境的入门用户。包体约 42.28MB,为 gz 压缩格式,上游未提供具体文件清单,但按单机版结构通常包含 Web 服务、执行器与内嵌 H2 数据库等核心组件,无需额外编译。该版本将全部服务集中于同一台机器,安装配置简单,可通过 Web 界面创建项目、定义作业依赖、设计工作流并设置定时调度,同时查看执行状态与日志,便于调试排错。目前已有 256 人学习下载,可作为理解 Azkaban 调度模型与工作流编排的轻量实践入口。需注意 SNAPSHOT 属开发中版本,生产环境建议改用稳定版或集群部署。

1. 从一份 azkaban-solo-server 包说起:单机任务调度到底能扛多少活

手里这份azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz,是 Azkaban 的单机执行版打包产物。Azkaban 本身是 LinkedIn 开源的批量工作流任务调度器,靠.job文件和flow依赖描述把一堆散脚本串成有向无环图,按依赖顺序跑。solo-server 模式把 web 控制台、执行器、元数据库全塞进一个 JVM 进程,用内置 H2 存状态,开箱即跑,不需要 MySQL、不需要 executor 集群。它适合谁?适合手上有一批 shell、Python、SQL 脚本要按顺序和依赖跑、又不想一上来就搭分布式调度的人——本地开发验证、小团队日批、CI 里跑一次性流水线,都是它的舒适区。但单机就是单机,并发和持久化的边界得先摸清楚,后面几章我会把解包、配置、跑通、踩坑一条条拆开。

2. 解包与目录结构:先看清 solo-server 里装了什么

2.1 解包与目录布局

拿到 tar.gz 第一步不是急着启动,而是先看它把什么打进去了。solo-server 的包结构相对固定,解压后大致是这么几层:

# 解压到指定目录,避免污染当前工作区 mkdir -p /opt/azkaban && tar -zxvf azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz -C /opt/azkaban # 进入解压后的根目录,查看结构 cd /opt/azkaban/azkaban-solo-server-0.1.0-SNAPSHOT ls -la

典型目录长这样:

目录/文件作用是否要改
bin/启停脚本start-solo.sh、shutdown-solo.sh一般不动
conf/azkaban.properties、log4j.properties、global.properties必改
lib/全部依赖 jar,含 azkaban core、H2、jetty不动
plugins/job 类型插件目录,如 jobtype、common按需
executions/运行时生成的执行日志与临时目录自动生成
projects/上传的项目包解压落点自动生成
logs/服务端日志自动生成

conf/azkaban.properties是核心,solo-server 默认用 H2 内嵌库,配置项里database.type=h2、h2.path指向本地文件。这里有个容易忽略的点:H2 文件路径如果是相对路径,会相对于启动时的工作目录解析,所以启动脚本里通常会cd到安装根目录再拉起 JVM。你要是手动java -jar启动,工作目录不对,数据库文件就落到别处,重启后任务历史"凭空消失",这是新手最常见的翻车点之一。

2.2 关键配置项逐个说清

打开conf/azkaban.properties,solo-server 模式下真正影响行为的配置没几个,但每个都得知道改了会怎样:

# 时区,影响调度时间和日志时间戳,国内部署务必改 azkaban.default.timezone=Asia/Shanghai # web 服务端口,默认 8081,被占用就换 jetty.port=8081 # 数据库类型,solo 模式固定 h2 database.type=h2 # H2 数据文件路径,建议写绝对路径,避免工作目录漂移 h2.path=/opt/azkaban/data/azkaban # 执行器线程数,决定同时能跑多少个 flow executor.flow.threads=30 # 单次执行保留的最大日志大小,超了会截断 executor.max.log.size=102400 # 是否允许用户上传自定义 job 类型,生产建议 false azkaban.server.allow.user.jobtype=false

executor.flow.threads这个参数最容易被误解。它限制的是同时处于运行态的 flow 数量,不是单个 flow 里的 job 并发数。单个 flow 内部的 job 并发由 flow 定义里的依赖关系决定,Azkaban 会按 DAG 拓扑序推进,能并行的 job 会并行提交。所以你把flow.threads调到 100,不代表一个 flow 里 100 个 job 能一起跑,只代表能同时接 100 个 flow。单机 CPU 和内存有限,这个值调太大反而会让 JVM 频繁 GC,我一般按CPU 核数 × 2起步,压测后再定。

executor.max.log.size单位是字节,默认 100KB 左右,跑长任务时日志很容易被截断,排查问题时看不到关键报错。常见做法是调到 10MB 级别,同时配合 log4j 的滚动策略,别让单个日志文件无限涨。

3. 跑通第一个 flow:从 .job 文件到依赖编排

3.1 写一个带依赖的三节点 flow

Azkaban 的任务单元是.job文件,本质是 key=value 的属性文件,type=command表示跑 shell 命令。依赖关系靠文件名前缀加数字来声明:a.job里写dependencies=b,就表示 a 依赖 b。下面写一个"下载 → 处理 → 校验"的三步 flow:

# 建一个项目目录 mkdir -p ~/azkaban-demo && cd ~/azkaban-demo # 第一步:准备数据 cat > 01_prepare.job <<'EOF' type=command command=echo "prepare data at $(date)" && mkdir -p /tmp/azk-demo && echo "raw" > /tmp/azk-demo/raw.txt EOF # 第二步:处理数据,依赖第一步 cat > 02_process.job <<'EOF' type=command command=cat /tmp/azk-demo/raw.txt | tr 'a-z' 'A-Z' > /tmp/azk-demo/out.txt && echo "processed" dependencies=01_prepare EOF # 第三步:校验结果,依赖第二步 cat > 03_verify.job <<'EOF' type=command command=grep -q "RAW" /tmp/azk-demo/out.txt && echo "verify ok" || (echo "verify failed" && exit 1) dependencies=02_process EOF # 打包成 zip,Azkaban 只认 zip zip -r demo.zip *.job

三个.job文件加一个 zip 包,这就是 Azkaban 项目的最小交付形态。dependencies的值是不带.job后缀的文件名,写错了 Azkaban 在解析阶段就会报"找不到依赖",flow 根本提交不上去。command里如果有多条命令,用&&串联,任何一条返回非零,整个 job 就标记为 FAILED,后续依赖它的 job 不会执行——这是 Azkaban 的失败传播机制,也是它比裸 crontab 强的地方。

3.2 启动服务并提交执行

配置改完、zip 打好,启动 solo-server:

# 用官方脚本启动,它会处理好工作目录和 classpath cd /opt/azkaban/azkaban-solo-server-0.1.0-SNAPSHOT bin/start-solo.sh # 确认进程和端口 ps -ef | grep AzkabanSingleServer | grep -v grep netstat -tlnp | grep 8081

启动脚本内部会cd到安装根目录,再用java -cp "lib/*:conf" azkaban.soloserver.AzkabanSingleServer拉起主类。看到AzkabanSingleServer started且 8081 端口监听,就说明起来了。浏览器打开http://<机器IP>:8081,默认账号密码都是azkaban,登录后Create Project→ 上传demo.zip→ 进入 flow 页面点Execute。

执行完在Executions页能看到三个 job 的拓扑图和各自日志。如果03_verify失败,点进去看它的日志,会看到verify failed,同时02_process是 SUCCESS、01_prepare是 SUCCESS——失败定位到具体节点,这是调度器相对脚本堆叠的核心价值。

提示:solo-server 默认没有开启多用户权限体系,所有项目对登录用户可见。多人共用时要么自己加前置鉴权,要么别把它暴露在公网。

3.3 用 API 触发,接进现有流水线

手动点页面只能验证,真正落地要能程序化触发。Azkaban 提供 REST API,solo-server 同样可用。先拿 session id,再触发执行:

# 1. 登录拿 session.id SESSION=$(curl -s -k -X POST http://127.0.0.1:8081 -d "action=login&username=azkaban&password=azkaban" | python3 -c "import sys,json;print(json.load(sys.stdin)['session.id'])") echo "session=$SESSION" # 2. 触发指定项目的 flow 执行 curl -s -k -X POST http://127.0.0.1:8081/executor -d "action=execute&session.id=$SESSION&project=demo&flow=demo&disabled=[]" # 3. 查询最近一次执行状态 curl -s -k "http://127.0.0.1:8081/executor?action=fetchExecFlow&session.id=$SESSION&execid=1"

action=execute里的project和flow参数,project是你在页面上建的项目名,flow默认等于项目名(Azkaban 会把项目里所有 job 按依赖拼成一个同名 flow)。disabled=[]是禁用节点列表,传空数组表示全跑。返回的 JSON 里带execid,拿这个 id 去fetchExecFlow查状态,status字段是SUCCEEDED、FAILED、RUNNING之一。把这段塞进 CI 的 post 阶段,就能实现"代码合并后自动跑批"。

4. 避坑与排查:solo-server 上最容易翻车的五件事

4.1 重启后任务历史全没了

现象:服务重启后,之前跑过的执行记录、项目列表全部消失,像新装的一样。原因:h2.path配的是相对路径,或者启动时工作目录不是安装根目录,H2 数据文件落到了临时目录,重启后 JVM 换了工作目录,找不到旧库。解决:把h2.path改成绝对路径,例如/opt/azkaban/data/azkaban,并确保该目录存在且启动用户有写权限。改完先停服务,把旧数据文件迁过去再启。

4.2 flow 一直卡在 PREPARING 不动

现象:点了执行,状态停在 PREPARING 或 RUNNING 但日志不更新。原因:executor.flow.threads被占满,新 flow 排队;或者某个 job 的命令在等标准输入、等锁、等网络,把执行线程挂住了。解决:先看executor.flow.threads当前值和正在跑的 flow 数,适当调大;再进具体 job 日志看最后一行停在哪。命令里凡是可能交互的,加</dev/null或-y参数,别让脚本等输入。

4.3 中文日志乱码

现象:job 日志里中文显示成问号或方块。原因:JVM 默认字符集不是 UTF-8,或者azkaban.default.timezone没配导致时间戳格式异常连带影响编码。解决:在start-solo.sh的 java 启动参数里加-Dfile.encoding=UTF-8,同时把azkaban.default.timezone=Asia/Shanghai配上。改完重启,新执行的日志就正常了,历史日志不会追溯修复。

4.4 上传 zip 报 "Job file not found"

现象:上传项目包时提示找不到 job 文件,或 flow 解析失败。原因:zip 打包时把.job文件套了一层父目录,Azkaban 要求.job文件在 zip 根目录下;或者dependencies写成了带.job后缀的名字。解决:进到.job所在目录再zip -r demo.zip *.job,别在上级目录打包整个文件夹。dependencies只写文件名主体,不带后缀。

4.5 单机跑大批量任务时 OOM

现象:跑几十个 flow 后 JVM 抛 OutOfMemoryError,服务假死。原因:solo-server 把 web、executor、H2 放一个 JVM,默认堆内存偏小,flow 多了之后执行上下文和日志缓冲把堆吃满。解决:在启动脚本里调大-Xmx,比如-Xmx2g,同时把executor.flow.threads控制在合理范围,别让并发无上限。真要跑大批量,solo-server 不是终点,该上分布式 executor 就上。

5. 进阶技巧:把 solo-server 当本地调度沙箱用

solo-server 最被低估的用法,是当本地调度沙箱——在把 flow 推到生产集群之前,先在单机上把依赖关系、失败传播、重试逻辑验证一遍。我一般会这么做:本地起一个 solo-server,把生产 flow 的.job文件原样拷过来,只改command里的路径和连接串,跑通再推。这样能提前暴露 90% 的依赖写错、命令拼错、环境变量缺失问题,比在生产上试错成本低得多。

再进一步,可以配合azkaban.properties里的executor.max.log.size和 log4j 滚动策略,把每次执行的日志按execid归档,写个小脚本定期把executions/下的日志同步到对象存储,做执行审计。solo-server 的executions/目录结构是<project>/<flow>/<execid>/,按这个层级遍历就能批量捞日志:

# 归档最近 7 天的执行日志,按项目名分目录 find /opt/azkaban/azkaban-solo-server-0.1.0-SNAPSHOT/executions -type d -name "[0-9]*" -mtime -7 | while read d; do proj=$(echo "$d" | awk -F/ '{print $(NF-3)}') execid=$(basename "$d") mkdir -p /backup/azkaban-logs/$proj cp -r "$d" /backup/azkaban-logs/$proj/$execid done

这段脚本按目录层级反推项目名和执行 id,把日志复制到备份目录。-mtime -7控制只归档最近 7 天,避免全量拷贝拖慢磁盘。生产上我会把它挂到 crontab 每天凌晨跑一次,配合对象存储的生命周期策略做冷备。

还有一个验证技巧:改完azkaban.properties后别急着重启,先用bin/shutdown-solo.sh优雅停,确认进程退出再启。solo-server 的 H2 在异常退出时可能留下锁文件,下次启动报 "Database may be already in use",这时候删掉h2.path同目录下的.lock.db文件再启就行。这个坑我踩过一次,排查了半小时才反应过来是锁文件没释放。

从那以后我每次动配置,都强制走一遍"停服务 → 改配置 → 确认无残留进程和锁文件 → 启动 → 跑一个最小 flow 验证"的流程,再也没出现过重启后数据对不上的情况。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询