azkaban-solo-server tar.gz包部署指南:单机任务调度从零到可运行 简介这是一份面向中小型团队或个人开发者的Azkaban单机版部署包名为azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz可用于大数据工作流的可视化创建、定时调度与依赖执行。包内为已编译的服务器程序与配套脚本用户下载解压后即可在单台机器上运行省去源码编译环节适合快速上手任务调度与工作流管理。文件总数暂无明细压缩包整体约42.28MB主要包含可执行脚本、配置文件和依赖组件覆盖服务启动、数据库初始化与Web界面访问等基础使用需求。该资源目前已有256人学习下载适合初学者用于本地实验环境搭建也便于有经验的开发者快速验证Azkaban的调度机制。借助该包可体验项目分组、作业依赖编排、定时触发、执行状态与日志查看等核心功能是了解Azkaban运行方式、进行评估选型或开展大规模调度方案前技术验证的实用素材。1. azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz一个压缩包就能跑起的单机调度到底靠不靠谱假设你负责的团队只有一台服务器却要管十几条定时任务每天凌晨跑报表、同步订单、清理临时表。你不想一上来就搭 Hadoop 生态也不想被 Yarn 调度折磨。azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz 就是为这个场景准备的解压、配一下端口、启动一个能看执行日志、能编排依赖、能失败重试的调度系统就出现在浏览器里。它是 Azkaban 的单机模式内置 H2 数据库和 Web 容器所以文件名里的 solo-server 就是它的形态tar.gz 则是最常见的分发方式。这个包适合自己开发自测也适合中小团队先把调度跑起来再决定要不要上集群。SNAPSHOT 三个字母容易吓到人但只要你按下面这套步骤做它一样能把活干稳。2. 为什么选 solo-server先弄清 Azkaban 的三种部署模式再决定用不用这个 tar.gz2.1 三种部署模式solo / two-server / multi-executor 的取舍Azkaban 官方把部署分成了三种形态名字起得直白。solo-server 就是只有一个进程把 Web 界面、执行引擎、数据库全部塞进同一个 JVM 里。two-server 则拆成两个独立进程一个是 Web 服务一个是 Executor 执行器数据库可以换成 MySQL这是很多生产环境的起步配置。multi-executor 是在 two-server 的基础上部署多个 Executor 节点让不同任务分摊到多台机器。用一张表看区别最直接部署模式进程数默认数据库使用场景主要问题solo-server1H2开发测试、小规模内部任务单点故障并发有限two-server2MySQL生产单机调度需要额外维护两个进程multi-executor2NMySQL生产集群调度需要管理多节点状态这个 tar.gz 包属于第一种名字里的 solo-server 已经写明白了。它最大的价值是零外部依赖不用装 MySQL不用配置 Redis不需要 ZooKeeper。只要有 JDK 8解压就能跑。但它的弱点也集中在单进程上Web 容器崩了执行器跟着挂H2 数据库的连接并发一高就锁死。所以我的建议是拿它做功能验证、跑小批量任务、当作学习工作流的工具这些都够用。真要给核心业务做生产调度至少换成 two-server 加 MySQL那个稳定度完全是两个级别。很多人会问既然目标是省事为什么不用现成的 Docker 镜像来安装 Azkaban原因是官方镜像更新滞后尤其 0.1.0-SNAPSHOT 这种开发期版本基本不会有对应镜像。与其在网上找来历不明的镜像不如自己把 tar.gz 包部署一遍再按照第 6 章的方法做成自己的镜像。掌握了 tar.gz 的目录结构和配置逻辑镜像就只是给这套东西加一层外衣出了问题也知道去里面排查。2.2 tar.gz 包内部长什么样目录结构与自带的依赖拿到 azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz别急着解压先想想它是哪来的。0.1.0-SNAPSHOT 是开发分支的构建产物不是官方发布版。官方发布版会有配套的 release notes 和校验值SNAPSHOT 包通常只有构建时间。目录结构一般是这样的azkaban-solo-server-0.1.0-SNAPSHOT/ ├── bin/ │ ├── azkaban-solo-start.sh │ ├── azkaban-solo-shutdown.sh │ └── internal/ ├── conf/ │ ├── azkaban.properties │ ├── azkaban-users.xml │ ├── global.properties │ ├── log4j.properties │ └── plugin.properties ├── lib/ │ ├── azkaban-core-*.jar │ ├── azkaban-solo-server-*.jar │ └── 第三方依赖 jar ├── plugins/ ├── web/ │ ├── dist/ │ └── image/ └── start.shbin 目录负责启动和停止conf 是整个调度的配置中心lib 里除了 Azkaban 自己的 jar还有 H2 驱动、Jetty 容器、Log4j 等一大批依赖。这些依赖是否齐全决定了这个包能不能在无网环境下跑起来。SNAPSHOT 包经常出现依赖缺漏的情况比如某个 commons-xxx.jar 没打进去结果启动时抛 NoClassDefFoundError。遇到这种报错不用慌去 lib 下对比一下同版本发布包的 jar 清单把缺的补上。这是最稳妥的做法前提是你手边有网络环境能够拉取 Maven 依赖。还要注意SNAPSHOT 包和正式 release 的差异不仅体现在 jar 上。开发分支的配置模板往往不全比如 conf 里可能没有 azkaban-users.xml只有 users.xml 的样例。因此拿到包后的第一件事不是启动而是先把 conf 目录里的文件列出来和官方文档里的配置项做对照。没有的文件自己新建不要指望启动脚本会自动生成。这个习惯能帮你省掉很多莫名其妙的错误。2.3 前置条件Linux x64 的 Java 8 到底该用什么方式装Azkaban 0.1.0 系列是基于 Java 8 编译的所以宿主机上必须有 JDK 8。这里没有商量余地装 Java 11 甚至 Java 17 都可能在启动阶段直接失败就算侥幸跑起来也会在任务执行时遇到反射调用被拒的问题。我的习惯是下载 Linux x64 平台的 JDK 8 tar.gz 包用它专属的目录安装不污染系统自带的 java 命令。# 假设已下载 jdk-8u202-linux-x64.tar.gz sudo mkdir -p /usr/local/java sudo tar -zxf jdk-8u202-linux-x64.tar.gz -C /usr/local/java sudo ln -s /usr/local/java/jdk1.8.0_202 /usr/local/java/jdk8 # 写进 /etc/profile 并加载 cat /etc/profile EOF export JAVA_HOME/usr/local/java/jdk8 export PATH$JAVA_HOME/bin:$PATH EOF source /etc/profile # 验证版本 java -version参数这里说一下tar 的 -C 指定解压目标避免文件散落ln -s 建一个固定路径的软链之后升级 JDK 只要改软链指向不用动所有脚本把环境变量写进 /etc/profile 而不是单个用户的 ~/.bashrc是为了让通过 service 或 cron 启动的进程也能拿到正确的 JAVA_HOME。source 之后 java -version 应该能看到 1.8 字样。如果输出的是其他版本多半是 PATH 里有更靠前的 JDK用 which java 查一下路径再调整 PATH 顺序。如果宿主机是麒麟 V10 这样的国产系统解压 tar.gz 还要多留一个心眼。某些安全策略会让非系统目录下的可执行文件没有执行权限解压后先用 chmod x 把 bin 目录下的脚本加上执行权限。另外麒麟系统默认启用 SELinux 的话需要在解压后的目录上执行 restorecon -R否则启动脚本可能被拦截报错信息还是抽象的 Permission denied。这些和 Azkaban 本身无关但都是我在现场排过的坑。3. 用 tar.gz 包部署 azkaban-solo-server从解压到看到执行流水的完整步骤3.1 解压与目录初始化先把包上传到服务器我一般放在 /opt 下方便后面做软链和系统服务。解压这一步没什么难度但目录规划一定要做对。# 创建统一的应用目录 sudo mkdir -p /opt/azkaban # 解压到 /opt/azkaban得到版本号目录 sudo tar -zxf azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz -C /opt/azkaban cd /opt/azkaban/azkaban-solo-server-0.1.0-SNAPSHOT/ ls -latar 的 -z 代表 gzip 压缩-x 是解压-f 指定文件名。解压后的目录名带版本号这样同一台机器可以同时保留几套目录做版本对比。ls 执行后先确认 bin 目录里的脚本有没有 x 权限。SNAPSHOT 包常因打包环境不同而丢失权限位一个常见的修复命令如下chmod x bin/*.sh mkdir -p logs data chown -R azkaban:azkaban /opt/azkabanlogs 目录放运行日志data 目录放 H2 数据库文件。这个包启动脚本不会自动创建这两个目录如果你偷懒不建启动后它会尝试在工作目录下创建然后你就得去解压目录里翻找。更建议的做法是单独建一个系统用户 azkaban把整个应用目录的属主改成它不要让 root 去跑调度任务。毕竟任务里可能包含 shell 脚本用 root 身份执行一个不小心就是灾难。chown 那行就是把权限交给专用用户。3.2 修改 solo-server 配置端口、数据库、用户所有核心配置都在 conf/azkaban.properties。这个文件是 Azkaban 的命根子改动之前一定先备份一份带时间戳的副本。cp conf/azkaban.properties conf/azkaban.properties.bak.$(date %s)然后打开 azkaban.properties优先确认下面这几项。它们是启动的前置条件不是优化项database.typeh2 h2.path./data/h2 jetty.port8081 azkaban.tzAsia/Shanghai executor.host127.0.0.1 executor.port12321 executor.flow.threads8database.type 必须是 h2因为 solo-server 自带的就是 H2 驱动改成 mysql 反而需要额外引入连接器。h2.path 指定数据库文件落盘位置我用相对路径 ./data/h2好处是数据和目录绑在一起备份直接打包目录。jetty.port 是 Web UI 的对外端口默认 8081如果你想改成 80需要保证没有其他进程用 80。azkaban.tz 这个参数必须显式设成 Asia/Shanghai否则调度时间会差 8 小时这在后面第 4 章还会重点说。用户认证在 conf/azkaban-users.xml。默认用户是 azkaban / azkaban这个大家都知道所以第一次登录后第一件事就是改密码或者加自己的用户。加管理员的方式是在 users 节点下加一行user usernameadmin passwordadmin123 rolesadmin /注意 roles 必须写 admin否则登录进去看不到所有项目。如果你只想给业务人员开只读账号roles 可以写 metrics这类账号可以看执行情况但不能执行任务。改完 XML 同样需要重启进程才会生效。3.3 启动与停止start 脚本的正确用法启动前确认一下环境变量然后执行启动脚本。这个脚本是前台运行的所以我们要用 nohup 或直接后台挂起。# 确认 JAVA_HOME 已生效 echo $JAVA_HOME # 启动后台运行并记录启动日志 bin/azkaban-solo-start.sh logs/start.out 21 # 等 10 秒再检查Jetty 初始化和执行器注册都需要时间 sleep 10 jps -l启动脚本内部会读取 JAVA_HOME 并拼出完整的 java 命令。solo-server 是单进程但 jps 里可能看到两个名字一个是 Web 容器一个是 Executor。这是因为它们在同一个 JVM 里但用了不同的主类入口。如果你只看到一个也正常重点是 jps 输出里不能有带FAILED的字样。启动后立刻查看日志tail -n 50 logs/azkaban.log看到 Server running on port 8081 或者 Azkaban Server started 就算成功。如果日志里有 Exception多半是配置里的端口被占或者 h2.path 指向的目录无写入权限。停服时不要 kill -9。这个教训我重复过很多次H2 数据库对异常终止没有太多自我保护机制直接 kill 很容易留下锁文件下一次启动直接报 Database already in use。用官方脚本关闭bin/azkaban-solo-shutdown.sh关闭脚本会先通知执行器停止接受新任务然后优雅关闭数据库。如果你实在找不到 shutdown 脚本那就先 kill 主进程等 5 秒再检查进程是否退出最后再 kill 残余进程。但无论如何正常切换版本时不要走这条粗暴路线。3.4 访问 Web UI第一次登录要改的默认信息启动成功之后打开浏览器访问http://服务器IP:8081。注意不要通过代理访问Azkaban 对代理跳转不友好容易登录后卡在空白页。默认用户名密码是 azkaban / azkaban。登录后先点右上角用户名修改密码。然后我要你做的第一件事不是急着建项目而是先确认页面上的时间和本地时间一致。如果页面上显示的时间比你的电脑慢 8 小时回头去改 azkaban.tz。创建一个简单项目来验证整条链路。在项目列表点击 Create Project然后上传一个 zip 包。zip 包里的内容是一个 job 文件比如# test.job typecommand commandecho hello azkaban这个文件用任意文本编辑器写好压成 test.zip上传。上传后点进入项目再点击 Execute Flow页面会跳转到执行页面状态变成 Success 就说明 Web 到 Executor 再到 H2 数据库的全链路已经通了。如果状态一直停在 Preparing那是 executor 端口或者 host 配置有问题具体排查会在第 5 章讲到。4. 必调的 5 个参数与验证清单让 solo server 不在第二天崩掉4.1 Java 堆内存与 GC 调优SNAPSHOT 包默认内存的玄学SNAPSHOT 包的启动脚本往往没有预留足够的 JVM 内存。我见过一个团队拿它跑几十个任务第二天就 OOM日志里全是 GC overhead limit exceeded。打开 bin 目录下的启动脚本找到 JAVA_OPTS 那行改成下面这样export JAVA_OPTS-Xms256m -Xmx2048m -XX:MaxMetaspaceSize512m -XX:UseG1GC-Xms256m 让 JVM 启动就分配最小堆避免运行中反复扩容。核心是 -Xmx2048msolo-server 的 Web 端和 Executor 共用堆内存页面响应、任务日志、Flow 状态全在这块内存里。任务一多256 兆或 512 兆根本不够。而 -XX:MaxMetaspaceSize512m 是因为 Azkaban 会加载大量的插件类和外部存储类元空间容易爆。G1GC 是 JDK 8 里最成熟的垃圾回收器比默认的 Parallel GC 在堆内存波动大时更平稳。改完脚本后重启进程然后观察一周内的 Full GC 频率。常见做法是在启动脚本里追加-Xloggc:logs/gc.log这样能看到 GC 明细。如果 gc.log 里老年代频繁增长说明任务的临时对象太多这时候优先调低 executor.flow.threads而不是无脑加堆内存。有人说这是一种玄学因为有没有效果很难速见。但我的判断方法很简单看 JVM 是否出现连续 Full GC以及任务在高并发时段是否偶发卡顿。只要这两个现象不出现内存就算够用。反过来如果每跑一次大任务就卡死先别怀疑代码回去检查堆参数。4.2 时区与调度时间为什么任务总是差 8 小时这个坑几乎每个人都会踩一次。现象是 Web UI 上配置定时任务明明选定的是每天 8 点结果第二天看执行记录是凌晨 0 点。原因有两个azkaban.tz 没设置JVM 默认读取的是系统时区而很多云主机默认时区是 UTC。Azkaban 的调度引擎在计算下次触发时间时以 azkaban.tz 为准不读 Web 端的显示时区。所以配置要分两层做。第一层是 azkaban.properties 里显式写azkaban.tzAsia/Shanghai第二层是宿主机系统时区也要改尤其是重启后sudo timedatectl set-timezone Asia/Shanghai注意改完配置必须重启 Azkaban。这部分参数不会热加载只改文件不重启等于白改。验证是否生效最简单的办法是在 Web UI 建一个每 5 分钟触发一次的调度看它的运行时间是否和本地时间一致。以前面那个例子来说如果设置 8 点而执行时间是在上午 8 点整说明时区对了。如果还是差 8 小时检查系统里是否还存在其他时区干扰点比如 JVM 启动参数里的-Duser.timezone。4.3 数据库文件位置与备份H2 数据的后悔药solo-server 用 H2 存储项目、定时器、执行记录。H2 数据落在你定义的 h2.path 目录下文件通常是 azkaban-db 或 h2 的.mv.db 格式。这个文件一旦损坏所有工作流失效。很多人直到磁盘满了才发现这个文件占了几个 G但部署初期就该给备份留一条退路。备份 H2 最安全的方式是先停服再拷贝文件。不停服直接复制很容易复制到一个处于中间状态的文件恢复时直接报错。bin/azkaban-solo-shutdown.sh sleep 5 cp -r data>log4j.appender.fileorg.apache.log4j.RollingFileAppender log4j.appender.file.Filelogs/azkaban.log log4j.appender.file.MaxFileSize100MB log4j.appender.file.MaxBackupIndex5MaxFileSize 是单个文件的触发体积达到 100MB 自动滚动MaxBackupIndex 是保留的.1、.2这类备份文件数量。这样磁盘占用最多也就 600MB 左右排错时还能从旧文件里找线索。如果你需要用日志做审计可以调大到 1GB 并保留 10 个备份但一定要警惕日志存储和 H2 数据文件都在同一个磁盘别让日志把数据盘的余量吃光。4.5 验证清单用这个包之后要检查的七件事部署完不是结束了我每次弄完一套 solo-server 都会按下面这个表过一遍。它能把配置错误、环境问题提前暴露出来避免第二天一早被业务方轰炸。检查项命令或操作预期结果Java 版本java -version输出 1.8进程状态jps -l存在 Azkaban 相关进程Web 端口curl -I http://localhost:8081返回 HTTP 200执行器端口netstat -tlnp看到 12321 监听系统时区date输出 CST数据库文件ls -l data/h2*有近期修改的 .mv.db 文件任务链路上传 command job 并执行状态 Successcurl 返回 200 是最直观的成功信号。如果返回 503多半是 Web 都起来了但 Executor 没有注册成功这在 solo-server 里也出现过原因是 executor.port 被占用或者 executor.host 配置指向了无法解析的地址。netstat 检查端口时注意确认是 TCP 而不是 UDP。所有的检查项都不要只看某一项综合通过才能判断这套系统具备交付条件。5. 避坑0.1.0-SNAPSHOT 包最常见的 5 个翻车现场5.1 启动报 Failed to start Jetty端口被占用但 netstat 显示没有进程现象执行启动脚本后日志里出现Failed to start Jettylsof 查看 8081 端口却没有进程在监听。原因你看到的 8081 没被占用但 Jetty 尝试绑定的时候还有另一个 TCP 端口冲突。这种情况经常发生在 Java 的临时端口区间或 IPv6 与 IPv4 绑定不一致时。还有可能是启动脚本里配置了jetty.hostname为主机名而主机名在 /etc/hosts 里解析到了 IPv6 地址::1Jetty 默认绑定 IPv6 失败回退不到 IPv4。解决在 azkaban.properties 里显式增加两行jetty.hostname0.0.0.0同时在启动脚本的 JAVA_OPTS 里追加一个参数export JAVA_OPTS$JAVA_OPTS -Djava.net.preferIPv4Stacktrue这会让 JVM 强制走 IPv4 网络栈。改完重启问题基本消失。如果还报端口被占换一个不常用的高位端口比如 18081避免和别的开发工具的默认端口抢。5.2 明明装了 JDK 8启动还是 UnsupportedClassVersionError现象在 shell 里输入 java -version 显示 1.8但执行启动脚本时却报UnsupportedClassVersionError: org/azkaban/... Unsupported major.minor version 52.0。原因启动脚本里把 JAVA_HOME 写死成了另一个路径或者系统通过 update-alternatives 把 root 用户的 java 指向了别的 JDK而 Azkaban 进程以其他用户身份运行读到的是不同的路径。解决不要依赖 PATH 传递。直接修改启动脚本在顶部写死export JAVA_HOME/usr/local/java/jdk8 export PATH$JAVA_HOME/bin:$PATH注意要放在脚本最前面覆盖掉任何系统级的环境变量。然后用su - azkaban -c echo $JAVA_HOME验证一下目标用户能否读到这个路径。如果你用的是 systemd 服务还要检查 /etc/systemd/system 里的 Environment 配置。这是典型的用户环境差异导致的翻车现场看起来像 JDK 问题实际上是脚本里的路径黑匣子。5.3 H2 数据库文件 already in use任务一跑就卡死现象启动没有任何问题登录 Web UI 也正常但一执行 Flow任务就卡在 Ready 状态日志出现Database may be already in use: Locked by another process。原因最常见的是上次进程被 kill -9H2 的文件锁没有释放。另一个原因是有两个 azkaban-solo-server 进程同时指向了同一个 data 目录比如你不小心从两个路径各启动了一次。H2 的锁不是网络锁是进程内文件锁一旦持有锁的进程没了锁文件不会自动消失。解决先用ps -ef | grep azkaban把所有相关进程找出来逐个 kill。然后到 data 目录下删除.lock.db和.trace.db文件。最后检查你启动时的工作目录到底在哪里因为 h2.path 如果配成相对路径./data/h2不同工作目录会指向不同的锁文件。更稳妥的做法是把 h2.path 改成绝对路径比如/opt/azkaban/data/h2这样能避免多个实例误写同一组文件。改完再启动任务就能正常跑了。5.4 麒麟 V10 解压 tar.gz 却提示 No such file or directory现象在麒麟 V10 服务器上解压了这个 tar.gz进入目录执行 bin/azkaban-solo-start.sh报错No such file or directory但 ls 显示文件明明存在。原因这个报错不是文件不存在而是脚本的 shebang 指向的/bin/bash路径不存在或者脚本有错误的换行符。SNAPSHOT 包如果在 Windows 或编辑不当的 macOS 上打包脚本会带上 CRLF 回车符Linux 解释器会把/bin/bash\r当作解释路径自然找不到。麒麟 V10 的 bash 通常还在 /bin/bash所以更多是 CRLF 问题。解决用 head -1 看一下脚本第一行然后用 dos2unix 或 sed 处理head -1 bin/azkaban-solo-start.sh sed -i s/\r$// bin/*.sh chmod x bin/*.sh处理后再次执行。这个方法对 tar.gz 包里所有 shell 脚本都适用包括 internal 目录下的那些辅助脚本。国产系统上还有一种情况是安全组件拦截了非系统目录的脚本执行这种一般修改目录挂载属性或恢复 SELinux 上下文能解决。总之报 “No such file or directory” 时先怀疑格式不要怀疑文件缺失。5.5 任务执行卡在 Preparing 状态执行器不可达现象上传项目成功点击 Execute Flow状态长时间停在 Preparing最后超时失败。原因solo-server 模式里 Web 端和 Executor 端通过 executor.port 通信。如果你在配置里写了executor.hostexample.com而该域名解析不到本机回环地址Web 端找不到执行器。还有一种情况是服务器防火墙默认只放行了 Web 端口没有放行 12321 这个内部通信端口。解决把 executor.host 设为 127.0.0.1executor.port 设为 12321同时防火墙放行 TCP 12321sudo firewall-cmd --add-port12321/tcp --permanent sudo firewall-cmd --reload如果你用的是云服务器安全组规则里也要加这一条。另外SNAPSHOT 包的执行器注册逻辑有一个已知的问题就是启动时如果 Web 还没完全就绪执行器可能注册失败。解决方法是启动后等 15 秒以上再访问页面别不等日志就急着点 Execute。我在现场用过最土但有效的方法启动脚本后加sleep 20再检测端口。6. 把 solo-server 容器化并推送到私有仓库从 tar.gz 到一键复用的进阶玩法当你手头多台机器都要部署同一个 azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz一遍遍解压、改配置、验证权限太浪费时间。我更推荐把它做成镜像用 Docker 跑起来。这样不仅环境一致后续升级 JVM 也好控制。前提还是这个 tar.gz 包本身没问题容器只是给包加了一层外壳。写一个基础的 DockerfileFROM openjdk:8-jre-slim COPY azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz /opt/ RUN tar -zxf /opt/azkaban-solo-server-0.1.0-SNAPSHOT.tar.gz -C /opt/ \ chmod x /opt/azkaban-solo-server-0.1.0-SNAPSHOT/bin/*.sh \ mkdir -p /opt/azkaban-solo-server-0.1.0-SNAPSHOT/logs \ /opt/azkaban-solo-server-0.1.0-SNAPSHOT/data WORKDIR /opt/azkaban-solo-server-0.1.0-SNAPSHOT EXPOSE 8081 12321 CMD [bin/azkaban-solo-start.sh]这个 Dockerfile 有几个关键点。base 镜像选择 openjdk:8-jre-slim镜像体积更小而且自带 Java 8 环境避免宿主机 JDK 版本差异。COPY 之后用 tar 命令解压到 /opt并修复脚本权限、预创建 logs 和 data 目录。WORKDIR 切到解压目录CMD 直接执行启动脚本。注意这里没有把端口暴露到宿主机只是声明了容器内端口真正运行时要加-p 8081:8081 -p 12321:12321。构建并推送到私有仓库的命令是最常见的两步docker build -t registry.internal/azkaban-solo-server:0.1.0 . docker push registry.internal/azkaban-solo-server:0.1.0如果你的 Kubernetes 集群是 KubeKey 之类的一键部署工具装出来的那么镜像推送到私有仓库后部署应用时只需要在 Pod 的 image 字段填上私有仓库地址并设置imagePullPolicy: IfNotPresent就能避免多台 Worker 各自解压 tar.gz。这里我要讲一个自己养成的习惯凡是 SNAPSHOT 版本我都不直接用官方构建产物上生产。要么从源码用 Gradle 重新构建一遍再打包要么在根目录下先生成好校验值。因为 SNAPSHOT 包没有经过严格的发布流程运行时的问题往往在任务类型一多才暴露。但如果你只是自测那这条路足够顺畅了。我在几百台服务器上部署过各种版本的 Azkabansolo-server 虽然不能扛住大规模并发但它让我把任务编排、依赖管理、失败重试这些核心概念彻底摸透了。后来迁移到 two-server 时几乎没花时间学习配置差异也就集中在数据库和几个连接参数上。如果你也是第一次接触 Azkaban我建议你先把这个 tar.gz 包跑起来部署一次、跑两个任务、改几处配置比看任何教程都管用。希望这些经验能帮到你省掉我当年踩过的那一堆坑。本文还有配套的精品资源点击获取