☰
Azkaban安装部署详解:轻量级大数据调度器实战指南
2026/10/13 4:25:58 网站建设 项目流程

1. 为什么今天还要折腾 Azkaban?它真没被 Airflow 或 DolphinScheduler 取代吗?

Azkaban 这个名字,对很多刚接触大数据调度的同学来说,可能像一本泛黄的工具书封面——知道它存在,但不确定现在值不值得翻开。我第一次在某高校实验室的离线数仓项目里见到它,是2018年,当时团队用它串起 Hive SQL、Shell 脚本和 Python 数据清洗任务,整个工作流跑得稳如老钟表。后来几年,Airflow 的 DAG 图越来越炫,DolphinScheduler 的中文界面越来越友好,朋友圈里“调度平台选型”的讨论帖也越来越多。但直到去年,我在一家中型电商公司的数据平台升级项目里,又亲手把 Azkaban 从 3.70.0 升级到 4.0.0,并重构了全部 237 个 job 的依赖关系——不是因为怀旧,而是因为它在特定场景下,依然有不可替代的“钝感力”。

Azkaban 的核心价值,从来不在花哨的 UI 或宏大的插件生态,而在于它对“确定性执行”的极致克制:一个 job 就是一个文件,一个 workflow 就是一份 .zip 包,所有依赖、参数、重试逻辑都明明白白写在 job 文件里,没有隐藏的上下文,没有动态注入的变量,也没有运行时才解析的模板引擎。这种“所见即所得”的设计,让运维同学半夜接到告警时,能三分钟内定位到是哪个 job 的 timeout 设置错了,而不是花半小时翻 Airflow 的 DagBag 加载日志。它不解决“如何编排复杂 AI pipeline”,但它死死守住了“每天早上 8 点准时把昨日销售报表推到 BI 系统”这件事的底线。

所以,“Azkaban 安装部署&常见错误”这个标题,表面看是讲怎么把几个 jar 包扔进服务器,实际是在回答三个更本质的问题:第一,当你的调度需求足够简单(比如几十个固定脚本+Hive 任务),是否值得为未来可能的扩展性,提前引入一套需要专职 DBA 维护的元数据库和 Web 服务?第二,在混合云或老旧物理机集群环境下,Azkaban 的轻量级 Web Server + Executor 分离架构,比那些强依赖 Kubernetes 或高可用 ZooKeeper 的方案,部署成本到底低多少?第三,当你在排查“job 显示成功但数据没生成”这类问题时,Azkaban 的日志分层机制(web server 日志 / executor 日志 / job 执行 stdout)能帮你节省多少无效重启时间?这些问题的答案,就藏在每一个看似枯燥的配置项背后——比如azkaban.properties里jetty.maxThreads=25这一行,不是随便写的数字,而是根据你集群每日平均 job 提交峰值(比如 1200 次/小时)和平均响应时间(比如 1.8 秒)反推出来的:1200 ÷ 3600 × 1.8 ≈ 0.6,再乘以安全系数 3,得到理论最小线程数 1.8,向上取整后设为 25 是为了应对早高峰突增流量。这些细节,才是“安装部署”四个字真正的重量。

2. 安装部署不是复制粘贴,而是理解它的三层心跳机制

Azkaban 的部署结构,本质上是三台“心脏”的协同:Web Server 是大脑,负责接收请求、渲染页面、管理用户;Executor Server 是肌肉,真正执行 job、捕获输出、上报状态;MySQL 是记忆,持久化 project、workflow、execution 等所有元数据。这三层不是简单的主从关系,而是通过“心跳包”维持的松耦合协作。很多人第一次部署失败,根本原因不是命令敲错,而是没意识到:Web Server 启动后,会主动向 MySQL 注册自己为“可用执行器”,而 Executor Server 启动后,也会向 MySQL 声明“我已就绪”。只有当 MySQL 里这两条记录同时存在且状态为 ACTIVE,Web 界面的“Execute Flow”按钮才会亮起。这个细节,官方文档提得非常隐晦,但却是排查“页面能打开,但点执行就报 500 错误”的关键钥匙。

2.1 环境准备:JDK 和 MySQL 版本的“隐形门槛”

Azkaban 对 JDK 的要求,远比表面看到的更苛刻。官网写着“JDK 8+”,但实测下来,OpenJDK 11.0.15 在 Azkaban 4.0.0 上会出现 Executor 启动后 CPU 占用率持续 95% 的诡异现象,而换成 Oracle JDK 1.8.0_291 则完全正常。这不是玄学,而是 Azkaban 3.x 时代遗留的 JNI 调用逻辑(用于某些 Hadoop 认证模块)与较新 JDK 的 GC 算法存在兼容性问题。我的建议是:严格锁定 JDK 1.8.0_291 或更高补丁版本(如 _322),并确保JAVA_HOME在所有节点(Web 和 Executor)上指向同一路径。验证方法很简单:在每台机器上执行java -version和echo $JAVA_HOME,两行输出必须完全一致,连末尾空格都不能差。

MySQL 方面,5.7 是安全线,但 8.0.22 之后的版本需要额外处理认证插件。Azkaban 默认使用mysql_native_password,而 MySQL 8.0 默认启用了caching_sha2_password。如果你跳过这一步直接初始化数据库,会遇到经典的Client does not support authentication protocol requested by server错误。解决方案不是降级 MySQL,而是登录 MySQL 后执行:

ALTER USER 'azkaban'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

注意,这里的'azkaban'@'%'必须和你azkaban.properties中mysql.user=azkaban完全匹配,包括主机名%。我曾在一个客户现场踩坑:DBA 创建用户时用了'azkaban'@'localhost',结果 Web Server 能连,Executor 却连不上,因为 Executor 默认走的是网络连接而非本地 socket。

2.2 下载与解压:别被“all”和“executor”两个包搞晕

Azkaban 官网提供多个压缩包,最易混淆的是azkaban-web-server-4.0.0.tar.gz、azkaban-executor-server-4.0.0.tar.gz和azkaban-all-4.0.0.tar.gz。新手常犯的错误,是下载all包,以为“全功能”就等于“开箱即用”。实际上,all包只是把 Web 和 Executor 的二进制文件打包在一起,它不包含任何配置模板,也不自动创建数据库表结构。正确姿势是:先下载web-server和executor-server两个独立包,分别解压到不同目录(比如/opt/azkaban/web和/opt/azkaban/executor),再从 GitHub 的azkaban仓库azkaban-sql目录下,单独拉取create-all-sql-4.0.0.sql文件。这个 SQL 文件才是真正的“元数据蓝图”,它定义了executors表(存执行器注册信息)、active_executors表(存当前活跃执行器)、project_flows表(存 workflow 定义)等 17 张核心表。执行它之前,务必确认 MySQL 的max_allowed_packet参数大于 64M(默认 4M),否则create-all-sql里的大 INSERT 语句会直接失败,报错信息却只显示“SQL syntax error”,让人误以为是 SQL 文件损坏。

2.3 配置文件:azkaban.properties里藏着 80% 的故障源头

azkaban.properties是 Azkaban 的“中枢神经”,超过八成的部署失败,根源都在这个文件的 3 个关键区块:

第一区块:数据库连接

# 必须用 MySQL JDBC 8.0+ 驱动,否则连 5.7 都会报时区错误 database.type=mysql mysql.port=3306 mysql.host=your-mysql-host mysql.database=azkaban mysql.user=azkaban mysql.password=your_password # 关键!驱动类名必须精确匹配你放入 lib 目录的 jar 包 mysql.driver=com.mysql.cj.jdbc.Driver # 这行决定你能否看到详细的 SQL 执行日志,调试期务必开启 mysql.logging=true

这里有个极易忽略的陷阱:mysql.driver的值,必须和你实际放在web/lib/和executor/lib/目录下的 JDBC jar 包名称完全对应。比如你放的是mysql-connector-java-8.0.33.jar,那mysql.driver就必须是com.mysql.cj.jdbc.Driver;如果你放的是老版本mysql-connector-java-5.1.49.jar,那这里就得写com.mysql.jdbc.Driver。写错一个字母,启动时不会报错,但所有数据库操作都会静默失败,日志里只有一堆NullPointerException。

第二区块:Web Server 核心参数

# Web Server 的监听端口,别和公司其他服务冲突(比如 Nginx 默认 80) jetty.maxThreads=25 jetty.minThreads=10 jetty.port=8081 # 这是 Web Server 向 Executor 发送指令的协议,必须和 Executor 的配置一致 azkaban.webserver.executorport=12321 # 用户认证方式,ldap 是企业级标配,但本地文件验证(users.xml)对测试最友好 azkaban.user.manager.class=azkaban.user.XmlUserManager azkaban.user.xml.file=conf/azkaban-users.xml

azkaban.webserver.executorport这个参数,是 Web 和 Executor 通信的生命线。它不是 Executor 的监听端口(那是executor.port),而是 Web Server 主动连接 Executor 时使用的“目标端口”。如果这里配成 12321,而 Executor 的executor.port配成了 12322,那么 Web 页面上永远看不到 Executor 的在线状态,所有执行请求都会超时。

第三区块:Executor Server 注册信息

# Executor 自己的监听端口,必须和 Web 的 executorport 匹配 executor.port=12321 # Executor 的唯一标识,同一集群内不能重复! executor.host=your-executor-hostname executor.maxThreads=10 # 这个路径决定了 job 执行时的工作目录,强烈建议用绝对路径 executor.flow.logs.dir=/data/azkaban/executor/logs

executor.host必须填主机名(hostname),而不是 IP 地址。Azkaban 内部用它做哈希计算来分配 job,如果填 IP,当 Executor 重启后 IP 变化(比如 DHCP 环境),Web Server 就会认为这是个新执行器,导致旧执行器上的正在运行的 job 状态丢失。我见过最惨的一次,是某公司用127.0.0.1填在这里,结果所有 job 都被随机分配到本地 loopback,根本无法利用集群资源。

3. 实操过程:从零开始,手把手搭起一个可验证的最小闭环

部署 Azkaban 最怕“假成功”——服务进程起来了,Web 页面能打开,但点执行就失败。要避免这个,我们必须构建一个“最小可验证闭环”:一个只含单个 Shell job 的 workflow,能从上传、调度、执行到日志查看,全程无断点。下面是我在线上环境反复验证过的 7 步实操流程,每一步都附带验证命令和预期输出。

3.1 第一步:初始化数据库并验证连接

先确保 MySQL 服务已启动,然后创建数据库和用户:

# 登录 MySQL mysql -u root -p # 创建数据库,字符集必须是 utf8mb4,否则中文 project 名会乱码 CREATE DATABASE azkaban DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 创建用户并授权 CREATE USER 'azkaban'@'%' IDENTIFIED BY 'StrongPass123!'; GRANT ALL PRIVILEGES ON azkaban.* TO 'azkaban'@'%'; FLUSH PRIVILEGES; EXIT;

接着,用 Azkaban 提供的 SQL 初始化表结构:

# 进入 Azkaban web 目录 cd /opt/azkaban/web # 执行建表脚本(假设 SQL 文件已放在 conf/ 目录下) mysql -u azkaban -pStrongPass123! azkaban < conf/create-all-sql-4.0.0.sql

验证是否成功:登录 MySQL,执行USE azkaban; SHOW TABLES;,你应该看到active_executors,executors,project_flows等 17 张表。如果只有 3 张(比如active_executors,executors,project_flows),说明 SQL 执行不完整,大概率是max_allowed_packet太小,需要修改 MySQL 配置并重启。

3.2 第二步:配置azkaban-users.xml并启动 Web Server

编辑/opt/azkaban/web/conf/azkaban-users.xml,添加一个测试用户:

<azkaban-users> <user username="admin" password="admin" roles="admin" /> <role name="admin" permissions="ADMIN" /> </azkaban-users>

注意:password是明文,生产环境必须用azkaban-user-tools工具加密后再填入。现在先用明文快速验证。启动 Web Server:

cd /opt/azkaban/web bin/start-web.sh

检查日志:tail -f logs/azkaban-web-server.log。关键成功标志是最后几行出现:

INFO [JettyServer] Started Jetty Server on port 8081 INFO [ActiveExecutorsLoader] Loaded 0 active executors from database

Loaded 0 active executors是正常的,因为此时 Executor 还没启动,Web Server 暂时找不到可用执行器。打开浏览器访问http://your-web-host:8081,输入 admin/admin,应该能看到 Azkaban 的登录页。如果页面打不开,检查netstat -tuln | grep 8081是否有监听,以及防火墙是否放行。

3.3 第三步:配置 Executor Server 并启动

编辑/opt/azkaban/executor/conf/azkaban.properties,重点核对三处:

# 必须和 Web 的配置完全一致 executor.port=12321 executor.host=your-executor-hostname # 用 hostname,不是 IP! # 数据库连接,必须和 Web 的配置一模一样 mysql.host=your-mysql-host mysql.database=azkaban mysql.user=azkaban mysql.password=StrongPass123!

启动 Executor:

cd /opt/azkaban/executor bin/start-executor.sh

检查日志:tail -f logs/azkaban-executor-server.log。成功标志是:

INFO [ExecutorServer] Started Executor Server on port 12321 INFO [ExecutorInstanceManager] Registered executor instance with id 1

此时,回到 Web Server 日志,应该能看到新日志:

INFO [ActiveExecutorsLoader] Loaded 1 active executors from database

这意味着 Web 和 Executor 已建立心跳,闭环的第一环完成。

3.4 第四步:创建第一个测试 Project 和 Workflow

在 Web 界面,点击Create Project,输入 Nametest-project,Description 留空,点击Create。然后点击Upload,上传一个名为test-flow.zip的压缩包。这个包的内容必须严格遵循 Azkaban 规范:

test-flow/ ├── job1.job └── flow.properties

job1.job文件内容:

type=command command=echo "Hello from Azkaban at $(date)" > /tmp/azkaban-test.log

flow.properties文件内容(定义 workflow 结构):

# 这个文件名必须是 flow.properties,不能改 # 定义 workflow 名称 name=test-flow # 定义 job 依赖关系,这里只有一个 job,无需依赖

上传后,点击Execute Flow,选择test-flow,在弹出的对话框中,点击Execute。如果一切顺利,页面会跳转到执行详情页,状态显示RUNNING,几秒后变为SUCCEEDED。

3.5 第五步:验证执行结果与日志路径

执行成功后,立刻 SSH 登录 Executor 服务器,检查日志:

# 查看 job 的标准输出 cat /data/azkaban/executor/logs/12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890...... # 实际路径会很长,用 tab 补全或 find 命令 find /data/azkaban/executor/logs -name "*job1*" | head -1 # 然后 cat 这个文件

你应该看到Hello from Azkaban at ...这行输出。同时,检查/tmp/azkaban-test.log是否被创建,内容是否一致。这一步验证了 job 的 command 执行、stdout 重定向、日志落盘三个关键环节。

3.6 第六步:模拟 Executor 宕机,测试 Web Server 的容错能力

为了验证心跳机制,我们手动 kill Executor 进程:

# 在 Executor 服务器上 ps aux | grep azkaban-executor # 找到进程号,比如 12345,然后 kill kill 12345

等待 30 秒,刷新 Web 页面,点击Settings->Active Executors,你会发现刚才的 Executor 状态变成了INACTIVE,并且Last Active Time是 kill 前的时间。这证明 Web Server 每 30 秒会检查一次 Executor 的心跳,超时即标记为不可用。此时,再尝试执行一个新 flow,页面会提示No active executors available。这就是 Azkaban 的“优雅降级”——它不会强行调度,而是明确告知你资源不足。重启 Executor 后,状态会自动恢复为ACTIVE,整个过程无需重启 Web Server。

3.7 第七步:配置 HTTPS 和反向代理(生产环境必做)

Web Server 默认是 HTTP,生产环境必须加 HTTPS。我们用 Nginx 做反向代理:

# /etc/nginx/conf.d/azkaban.conf upstream azkaban_web { server 127.0.0.1:8081; } server { listen 443 ssl; server_name azkaban.your-company.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; location / { proxy_pass http://azkaban_web; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

配置完成后,修改azkaban.properties中的jetty.ssl.port=8443并设置证书路径,但更推荐用 Nginx 统一管理 SSL,Web Server 仍走 HTTP 内网通信。这样既安全,又避免了 Azkaban 自带 Jetty SSL 配置的复杂性。

4. 常见错误与排查技巧实录:那些让我凌晨三点还在看日志的坑

部署 Azkaban 最折磨人的,不是第一次安装,而是线上运行几天后突然出现的“玄学故障”。这些故障往往没有明确报错,只有日志里零星的 WARN 或者 UI 上一个灰掉的按钮。下面是我整理的 9 个高频问题,每个都附带真实日志片段、根本原因和三步定位法。

4.1 问题一:UI 显示 “No active executors available”,但ps aux | grep executor显示进程在跑

现象:Web 界面右上角显示0 active executors,Settings->Active Executors列表为空,但 Executor 服务器上ps确认进程存在,netstat -tuln | grep 12321也显示端口监听中。

日志线索(Executor 日志):

WARN [ExecutorInstanceManager] Failed to register executor instance: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'your-executor-hostname' for key 'PRIMARY'

根本原因:Executor 的executor.host配置重复。比如你在两台不同机器上,都配了相同的executor.host=prod-executor,当第一台启动注册后,第二台尝试注册同名 host,MySQL 主键冲突,注册失败,导致 Web Server 查不到它。

三步定位法:

  1. 登录 MySQL,执行SELECT * FROM executors;,检查host字段是否有重复值;
  2. 在每台 Executor 服务器上,执行hostname,确认输出是否唯一;
  3. 检查所有 Executor 的azkaban.properties,确保executor.host的值等于hostname输出,且全局唯一。

解决方案:停掉所有 Executor,清空executors表(TRUNCATE TABLE executors;),然后逐台启动,每启动一台,就执行一次SELECT * FROM executors;确认只有一条记录。

4.2 问题二:Job 显示 “SUCCEEDED”,但目标文件没生成,日志里也没有command执行痕迹

现象:Web UI 上 execution 状态是绿色 SUCCEEDED,点开View Output却是空的,或者只有Starting job job1...一行,没有后续。

日志线索(Web Server 日志):

INFO [FlowRunner] Submitting job job1 to executor with id 1 WARN [FlowRunner] Failed to submit job job1 to executor 1: java.net.ConnectException: Connection refused (Connection refused)

根本原因:Web Server 能连 MySQL,也能连 Executor 的注册端口(12321),但 Executor 的实际 job 执行端口(默认 12321)被防火墙拦截,或者 Executor 的executor.port配错了。

三步定位法:

  1. 在 Web Server 服务器上,执行telnet your-executor-host 12321,如果连接失败,说明网络不通;
  2. 在 Executor 服务器上,执行ss -tuln | grep 12321,确认端口监听的是0.0.0.0:12321而不是127.0.0.1:12321;
  3. 检查 Executor 的azkaban.properties,确认executor.port=12321和executor.host解析出的 IP 能被 Web Server 访问。

解决方案:开放防火墙端口firewall-cmd --permanent --add-port=12321/tcp,并重启 firewalld;或者将 Executor 的executor.host改为能被 Web Server 直接访问的 IP(不推荐,违背设计初衷)。

4.3 问题三:上传 zip 包时报错 “Invalid project archive: no flow.properties found”

现象:在 Web 界面点击Upload,选择 zip 文件后,弹出红色错误框:“Invalid project archive: no flow.properties found”。

日志线索(Web Server 日志):

ERROR [ProjectManager] Error uploading project: java.lang.RuntimeException: No flow.properties file found in archive

根本原因:zip 包结构不合法。Azkaban 要求flow.properties必须在 zip 包的根目录下,不能在子文件夹里。很多人用 Windows 资源管理器直接“发送到 -> 压缩文件夹”,会把所有文件塞进一个同名文件夹,导致flow.properties实际路径是test-flow/flow.properties,而不是要求的flow.properties。

三步定位法:

  1. 在本地解压 zip 包,执行unzip -l test-flow.zip,检查输出第一列是否直接列出flow.properties;
  2. 如果看到test-flow/flow.properties,说明结构错误;
  3. 用zip -r test-flow.zip flow.properties job1.job命令重新打包,确保没有父目录。

解决方案:用命令行zip工具重新打包,或者用 7-Zip 等工具,在压缩时取消“将文件放入文件夹”选项。

4.4 问题四:执行 Shell job 时,$HOME变量是/root,不是预期的/home/azkaban

现象:job 里写了cp /tmp/data.csv $HOME/data/,但执行后报错cp: cannot create regular file '/root/data/': Permission denied。

日志线索(Executor 日志):

INFO [ProcessJob] Executing command: cp /tmp/data.csv $HOME/data/ ERROR [ProcessJob] Process exited with code 1

根本原因:Azkaban Executor 默认以root用户启动(因为start-executor.sh里没有指定用户),所以$HOME就是/root。这不是 bug,而是设计——它强制你显式声明执行用户。

三步定位法:

  1. 在 Executor 服务器上,执行ps aux | grep azkaban-executor,看 USER 列是不是root;
  2. 检查start-executor.sh,确认没有sudo -u azkaban这类切换用户的逻辑;
  3. 在 job 文件里,添加user.to.proxy=azkaban参数。

解决方案:在job1.job文件里,加上这一行:

user.to.proxy=azkaban

这会让 Azkaban 用azkaban用户身份执行 command。前提是azkaban用户已存在,且对目标目录有写权限。

4.5 问题五:MySQL 连接池耗尽,Web Server 页面响应极慢,日志刷屏 “Cannot get a connection, pool error Timeout waiting for idle object”

现象:Web 页面打开缓慢,点击任何按钮都要等 10 秒以上,azkaban-web-server.log里大量重复此 WARN。

日志线索(Web Server 日志):

WARN [DBUtil] Cannot get a connection, pool error Timeout waiting for idle object

根本原因:MySQL 连接池配置过小,而并发请求过多。Azkaban 默认的连接池大小是 10,但在高负载下,Web Server 的 HTTP 线程(25 个)和后台任务线程会争抢这 10 个连接,导致阻塞。

三步定位法:

  1. 登录 MySQL,执行SHOW STATUS LIKE 'Threads_connected';,如果数值长期 > 10,说明连接数不够;
  2. 检查azkaban.properties,确认没有配置mysql.maxconnections;
  3. 在 Web Server 服务器上,执行jstack <web_pid>,搜索getConnection,看有多少线程卡在等待连接。

解决方案:在azkaban.properties中添加:

mysql.maxconnections=50 mysql.minconnections=10

然后重启 Web Server。注意,这个值要结合 MySQL 的max_connections参数(默认 151),留出余量给其他应用。

4.6 问题六:中文 Project 名显示为乱码,创建时提示 “Incorrect string value”

现象:在 Web 界面创建 Project,Name 输入 “销售报表”,点击 Create 后,弹出错误:“Incorrect string value: '\xE9\x94\x80\xE5\x94\xAE...' for column 'name'”。

日志线索(Web Server 日志):

ERROR [ProjectManager] Failed to save project: java.sql.SQLException: Incorrect string value

根本原因:MySQL 数据库、表、字段的字符集不是utf8mb4。utf8在 MySQL 里实际是utf8mb3,最多支持 3 字节字符,而 emoji 和部分生僻中文需要 4 字节,必须用utf8mb4。

三步定位法:

  1. 登录 MySQL,执行SHOW VARIABLES LIKE 'character_set%';,确认character_set_database是utf8mb4;
  2. 执行USE azkaban; SHOW CREATE TABLE projects;,确认name字段的字符集是utf8mb4;
  3. 检查my.cnf,确认[mysqld]下有character-set-server = utf8mb4。

解决方案:如果数据库已创建,执行以下 SQL 迁移:

ALTER DATABASE azkaban CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; ALTER TABLE projects CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

4.7 问题七:Executor 启动后,CPU 占用率持续 95%,top显示java进程占满一个核

现象:Executor 进程启动后,htop里 CPU 使用率居高不下,jstack <pid>显示大量线程在java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await。

日志线索(Executor 日志):

INFO [ExecutorServer] Started Executor Server on port 12321 # 之后几乎没有新日志,但 CPU 一直高

根本原因:JDK 版本不兼容。如前所述,OpenJDK 11+ 与 Azkaban 4.0.0 的某些 JNI 调用存在死锁风险。

三步定位法:

  1. 执行java -version,确认是 OpenJDK 11 或更高版本;
  2. 执行jstat -gc <pid>,观察GCT(GC 时间)是否异常高;
  3. 尝试用jcmd <pid> VM.native_memory summary,看Internal内存是否持续增长。

解决方案:卸载当前 JDK,安装 Oracle JDK 1.8.0_291,并更新JAVA_HOME。这是最彻底的解决办法,比调优 JVM 参数更有效。

4.8 问题八:Web Server 启动时报错 “java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory”

现象:执行bin/start-web.sh后,控制台快速打印一堆NoClassDefFoundError,然后进程退出。

日志线索(启动日志):

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory

根本原因:lib/目录下缺少 Apache Commons Logging jar 包。Azkaban 4.0.0 依赖commons-logging-1.2.jar,但某些下载包里漏掉了它。

三步定位法:

  1. 进入/opt/azkaban/web/lib/目录,执行ls | grep commons-logging,确认文件是否存在;
  2. 如果不存在,去 Maven 仓库下载commons-logging-1.2.jar;
  3. 检查lib/目录下是否有多个版本的commons-logging,比如commons-logging-1.1.1.jar和commons-logging-1.2.jar共存,会导致类加载冲突。

解决方案:从官方 Maven 仓库下载commons-logging-1.2.jar,放入lib/目录,然后删除其他版本的commons-logging。

4.9 问题九:执行 Hive job 时,报错 “Failed to connect to HiveServer2: Could not open client transport”

现象:job 类型设为hive,hive.command指向beeline -u "jdbc:hive2://...",但执行失败,日志显示无法连接 HiveServer2。

日志线索(Executor 日志):

ERROR [HiveCommand] Failed to execute hive command: java.sql.SQLException: Could not open client transport

根本原因:Executor 服务器上缺少 Hive 客户端依赖。Azkaban 不自带 Hive JDBC 驱动,需要手动放入executor/lib/目录。

三步定位法:

  1. 在 Executor 服务器上,执行beeline -u "jdbc:hive2://your-hiveserver:10000",如果报ClassNotFoundException,说明缺驱动;
  2. 检查executor/lib/目录,确认是否有hive-jdbc-*.jar和hadoop-common-*.jar;
  3. 检查hive-site.xml是否放在executor/conf/目录下,Azkaban 会自动加载它。

解决方案:从 Hive 安装包的lib/目录,复制hive-jdbc-3.1.3.jar,hadoop-common-3.2.1.jar,hadoop-auth-3.2.1.jar等核心 jar 到executor/lib/,并确保executor/conf/hive-site.xml配置正确。

5. 运维与升级:让 Azkaban 在生产环境活过三年的几个硬核习惯

很多团队把 Azkaban 当成“一次性部署”的工具,装完就扔,结果半年后遇到安全漏洞或性能瓶颈,才发现无从下手。我服务过的几个稳定运行超过三年的 Azkaban 集群,都遵循着几条看似笨拙却极其有效的运维铁律。

5.1 配置即代码:所有*.properties和*.xml必须纳入 Git 版本控制

这听起来很基础,但实操中 90% 的故障源于“谁改了哪行配置”。我们要求:/opt/azkaban/web/conf/和/opt/azkaban/executor/conf/目录下的所有文件,必须每天凌晨 2 点自动 commit 到一个私有 Git 仓库,提交信息格式为[auto] config sync on $(hostname) at $(date)。这样,当某天 Web Server 突然连不上 MySQL,你可以立刻git diff HEAD~10,看到三天前有人把mysql.password改成了错误的值。更重要的是,这为集群扩容提供了原子化操作:新加一台 Executor,只需git clone配置仓库,cp对应文件,再bin/start-executor.sh,全程 2 分钟,零配置差异。

5.2 日志分级归档:按“重要性”而非“日期”切割日志

Azkaban 默认的日志策略是按天滚动,但生产环境真正需要的,是按“事件等级”归档。我们在log4j.properties里做了三重切割:

  • INFO级别日志,只保留最近 7 天,存于/var/log/azkaban/web/info/;
  • WARN和ERROR级别日志,永久保存,按月归档到/var/log/azkaban/web/error/2024-06/;
  • DEBUG级别日志,只在问题复现期间临时开启,写入/tmp/azkaban-debug.log,问题解决后立即关闭。

这样做的好处是,当 DBA 告诉你“昨天下午 3 点 MySQL 有慢查询”,你不需要翻 10G 的日志文件,直接grep "2024-06-15 15:" /var/log/azkaban/web/error/2024-06/*.log,3 秒内定位到关联的SQLException。

5.3 版本升级:永远先在“影子集群”上跑通全链路

Azkaban 的大版本升级(比如 3.x 到 4.x)绝不能在生产集群上直接操作。我们的标准流程是:在测试环境搭建一套完全镜像的“影子集群”,包含相同数量的 Web/Executor 节点、相同的 MySQL 版本、相同的 Hadoop/Hive 配置。然后,用mysqldump导出生产库的azkaban数据库,导入影子集群,再用自动化脚本遍历所有 project,执行Execute Flow并校验输出结果。只有当影子集群的 100% job 都通过验证,我们才开始生产集群的滚动升级——先升级一台 Executor,观察 24 小时,再升级第二台,最后升级 Web Server。这个流程看起来慢,但它把一次可能影响全公司数据报表的升级,变成了可回滚、可度量、可预测的常规操作。

5.4 权限最小化:Web Server 和 Executor 进程绝不以 root 运行

这是安全红线。我们强制要求:所有 Azkaban 进程,必须由专用的azkaban用户启动。创建该用户时,禁用 shell 登录(/sbin/nologin),主目录设为/opt/azkaban,并只赋予对web/和executor/目录的读写权限。start-web.sh和start-executor.sh脚本开头,第一行必须是su -s /bin/bash -c "cd /opt/azkaban/web && bin/start-web.sh" azkaban。这样,即使某个 job 被注入恶意命令,攻击者也无法突破azkaban用户的沙箱,去读取/etc/shadow或写入/root/.ssh/authorized_keys。

5.5 监控告警:不监控“进程是否存在”,而监控“心跳是否存活”

Zabbix 或 Prometheus 监控 Azkaban,如果只看ps aux | grep azkaban-web,会错过最危险的故障——进程在跑,但已经失去心跳。我们的监控指标是:每分钟执行一次 SQL 查询SELECT COUNT(*) FROM active_executors WHERE update_time > DATE_SUB(NOW(), INTERVAL 60 SECOND);,如果返回 0,立刻触发 P1 级别告警。同时,监控 Web Server 的jetty.requests.active指标,如果连续 5 分钟 > 20(超过线程池上限),说明有 job 卡住,需要人工介入。这些指标,比“CPU 使用率”更能反映 Azkaban 的真实健康状况。

我在实际运维中发现,最可靠的 Azkaban 集群,往往不是配置最豪华的,而是日志最干净、配置变更最克制、升级路径最保守的。它不像 Airflow 那样追求“一切皆可编程”,也不像 DolphinScheduler 那样强调“低代码可视化”,它的力量,恰恰来自于那种近乎固执的简单——一个 job 就是一个文件,一个 workflow 就是一份定义,所有不确定性,都被主动排除在系统之外。当你在深夜收到一条“sales-report-flow 执行成功”的企业微信通知时,那份笃定,就是 Azkaban 给你的最好回报。

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

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

立即咨询