从零搭建Azkaban任务调度平台:Solo与集群模式部署实践

从零搭建Azkaban任务调度平台:Solo与集群模式部署实践 1. 项目概述与核心价值最近在梳理团队的数据处理流程发现很多脚本和任务散落在各个服务器上靠 crontab 硬撑着。时间依赖复杂一点的任务比如B任务必须在A任务成功完成后才能启动或者每天凌晨需要按顺序跑几十个ETL脚本光靠人工协调和查日志就够喝一壶的。更别提任务失败后的告警和重试基本靠人肉盯。这种背景下一个集中、可视化的任务调度平台就成了刚需。在对比了 Airflow、DolphinScheduler 和 Azkaban 之后我们最终选择了 Azkaban。原因很简单Azkaban 由 LinkedIn 开源久经考验设计理念直观——用属性文件.job和压缩包.zip来定义工作流学习成本低对于从脚本时代过渡过来的团队特别友好。它核心就解决一件事在正确的时间以正确的依赖关系运行正确的任务并提供清晰的执行历史和日志。这次搭建的目标很明确先在一台机器上把 Solo Server 模式跑起来快速验证核心功能并让团队先用上然后为了满足生产环境对高可用和性能的要求再平滑过渡到多执行器的集群模式。Solo 模式把所有组件Web Server 和 Executor Server打包在一起适合开发测试而集群模式则将 Web 服务器负责界面和调度与一个或多个 Executor 服务器负责任务执行分离这样可以水平扩展执行能力并且即使某个 Executor 挂了Web Server 也能将任务分发给其他健康的节点。整个搭建过程其实就是理解 Azkaban 的架构、搞定数据库、配置组件、最后打通它们之间通信的过程。下面我就把从零开始搭建 Azkaban Solo 及集群的完整过程、踩过的坑和最佳实践毫无保留地分享出来。2. 环境准备与依赖安装2.1 基础系统环境要求Azkaban 是 Java 生态的产物所以第一步就是准备好它的“土壤”。我们选择的是 Linux 系统这里以 CentOS 7.x 为例其他发行版如 Ubuntu 在包管理命令上略有不同但步骤相通。首先确保系统基础环境干净。Java 环境Azkaban 3.x 版本需要 JDK 8 或 JDK 11。我强烈建议使用 JDK 8因为它在 Azkaban 社区中经过最广泛的测试。通过yum install java-1.8.0-openjdk-devel安装安装后务必检查版本java -version。光有 JRE 不够需要完整的 JDK因为某些任务如 Spark可能会在运行时编译代码。数据库Azkaban 需要关系型数据库来存储项目、工作流、执行历史等元数据。官方支持 MySQL。这里有个大坑Azkaban 对 MySQL 的版本和编码有严格要求。推荐使用 MySQL 5.7 或 8.0。安装 MySQL 后必须做两件事创建专用数据库和用户不要使用 root 用户。创建一个如azkaban的数据库以及一个同名的用户并授予该用户对这个数据库的所有权限。调整数据库配置编辑 MySQL 配置文件通常是/etc/my.cnf或/etc/mysql/my.cnf在[mysqld]部分增加以下关键配置然后重启 MySQL 服务。[mysqld] max_allowed_packet1024M transaction-isolationREAD-COMMITTED character-set-serverutf8mb4 collation-serverutf8mb4_unicode_cimax_allowed_packet是为了防止上传大型项目 ZIP 包时出错transaction-isolation是 Azkaban 官方明确要求的utf8mb4编码则能更好地支持中文等字符。注意如果使用 MySQL 8.0默认的身份验证插件是caching_sha2_password而一些较旧的 MySQL 连接驱动可能不支持。如果 Azkaban 启动时连不上数据库可以尝试将azkaban用户的密码插件改为mysql_native_passwordALTER USER azkaban% IDENTIFIED WITH mysql_native_password BY YourPassword;构建工具我们需要从源码编译 Azkaban。Azkaban 使用Gradle进行构建。直接去 Gradle 官网下载二进制包如 gradle-6.x-all.zip解压并配置环境变量GRADLE_HOME和PATH。验证gradle -v。2.2 Azkaban 源码获取与编译Azkaban 的发布页面上通常只提供 Web Server 和 Executor Server 的编译好包。但为了获得最大的灵活性和一致性我推荐从 GitHub 拉取指定版本的源码自行编译。# 1. 克隆仓库如果网络慢可以找国内的镜像源 git clone https://github.com/azkaban/azkaban.git cd azkaban # 2. 切换到稳定版本分支例如 3.90.0 git checkout 3.90.0 # 3. 使用 Gradle 进行编译。这个过程会下载大量依赖请保持网络通畅。 # 编译所有模块Web Server, Executor Server, Solo Server等 ./gradlew build -x test # 参数 -x test 表示跳过测试可以显著加快编译速度。首次构建可能需要10-20分钟。编译成功后你需要的发布包位于各个子项目的build/distributions/目录下是.tar.gz或.zip格式。主要关注三个azkaban-web-server-*.tar.gzazkaban-exec-server-*.tar.gzazkaban-solo-server-*.tar.gz把它们解压到你规划的安装目录例如/opt/azkaban。我习惯的目录结构是/opt/azkaban/ ├── solo/ # Solo 服务器目录 ├── web/ # Web 服务器目录集群模式 ├── exec1/ # 执行器1目录集群模式 ├── exec2/ # 执行器2目录集群模式 └── dependencies/ # 公共依赖如MySQL驱动jar包2.3 数据库初始化在启动任何 Azkaban 服务之前必须初始化数据库表结构。Azkaban 源码的sql目录下提供了创建表的脚本。# 进入解压后的源码目录下的sql文件夹 cd /path/to/azkaban-source/azkaban-db/src/main/sql # 查看sql文件通常有 create-all-sql-*.sql 和 upgrade-*.sql # 对于全新安装使用 create-all-sql-*.sql ls -la # 使用mysql命令导入建表脚本 mysql -h your_mysql_host -u azkaban -p azkaban create-all-sql-0.1.0-SNAPSHOT.sql # 请根据实际文件名调整。执行后不报错即表示成功。这个步骤至关重要且容易忽略。如果跳过Azkaban 启动时会因为找不到表而报出一连串数据库异常。3. Solo Server 模式搭建详解Solo 模式是 Azkaban 的“一体机”版本将 Web 界面和任务执行器整合在同一个 JVM 进程中。它是学习和功能验证的绝佳起点。3.1 目录结构与关键配置将azkaban-solo-server-*.tar.gz解压到/opt/azkaban/solo。核心目录如下bin/启动脚本start-solo.sh和shutdown-solo.sh。conf/配置文件所在是重中之重。lib/存放所有依赖的 Jar 包。plugins/可以放置各种扩展插件如 HDFS、Spark、Hive 任务类型。web/存放 Web 静态资源。首先处理数据库驱动。将 MySQL 的 JDBC 驱动 Jar 包如mysql-connector-java-8.0.xx.jar复制到lib/目录下。否则 Azkaban 无法连接数据库。接下来配置conf/azkaban.properties。这是 Solo 模式的主配置文件。你需要修改以下关键项# Azkaban 运行时临时文件目录确保有写权限 azkaban.temp.dir/opt/azkaban/solo/temp # 数据库配置 database.typemysql mysql.port3306 mysql.hostlocalhost # 你的MySQL服务器地址 mysql.databaseazkaban # 你创建的数据库名 mysql.userazkaban mysql.passwordYourSecurePassword mysql.numconnections100 # 连接池大小 # Web服务器配置 azkaban.webserver.port8081 # Web UI 访问端口 azkaban.webserver.ssl.port8443 # HTTPS端口可按需配置 azkaban.webserver.website.urlhttp://your-server-ip:8081 # 重要用于生成任务日志等链接 # 执行器配置在Solo模式中执行器与Web服务器同进程 azkaban.executor.port12321 # 执行器端口Solo模式内部使用 # 用户管理默认使用基于文件的简单用户管理 user.manager.classazkaban.user.XmlUserManager user.manager.xml.fileconf/azkaban-users.xml # 邮件通知配置可选但生产环境建议配置 mail.senderyour-emailgmail.com mail.hostsmtp.gmail.com mail.useryour-emailgmail.com mail.passwordyour-app-specific-password # 注意可能需用应用专用密码 job.failure.emailteam-alertyourcompany.com job.success.emailteam-alertyourcompany.com另一个重要文件是conf/azkaban-users.xml用于定义登录用户和权限。一个简单的例子azkaban-users user usernameadmin passwordadmin rolesadmin groupsazkaban / user usernameanalyst passwordanalyst123 rolesread / role nameadmin permissionsADMIN / role nameread permissionsREAD / /azkaban-users实操心得在测试环境可以用简单密码但在生产环境务必使用强密码并考虑集成 LDAP 或 SSO。azkaban-users.xml的密码是明文存储的这也是为什么生产环境推荐更安全的用户管理方式。3.2 启动、验证与第一个工作流配置完成后就可以启动了。cd /opt/azkaban/solo # 前台启动方便看日志 bin/start-solo.sh # 或者后台启动 nohup bin/start-solo.sh /dev/null 21 启动后查看日志文件logs/azkaban-webserver.log确认没有ERROR级别的错误并看到类似“Started AzkabanWebServer”的信息。然后在浏览器访问http://your-server-ip:8081。如果看到登录界面恭喜你Solo 模式搭建成功现在创建第一个工作流来验证功能。准备任务文件创建一个项目文件夹my_first_project。command.job: 这是一个最简单的 Shell 任务。typecommand commandecho Hello Azkaban at $(date)workflow.job: 这是一个流程定义文件依赖上面的任务。typeflow nodestask1 task1.typecommand task1.commandecho This is a workflow task创建一个dep.zip文件将上述两个.job文件打包进去。注意压缩时必须直接选择文件进行压缩不能包含上层目录。即用zip dep.zip *.job而不是zip dep.zip my_first_project/*.job。在 Azkaban Web UI 中操作登录后点击 “Create Project”。输入项目名和描述创建项目。在项目页面点击 “Upload”选择刚才的dep.zip文件上传。上传成功后你会看到定义的工作流。点击 “Execute Flow” 可以立即运行也可以点击 “Schedule” 设置定时调度Cron 表达式。运行后在 “Executing” 或 “History” 页面可以查看实时日志和最终状态。这个简单的流程验证了 Azkaban 最基本的任务定义、打包上传、调度执行和日志查看功能。Solo 模式到此就完全可以用于个人或小团队的任务管理了。4. 集群模式架构与部署当任务量增长或者对可用性有要求时Solo 模式的单点瓶颈就显现出来了。集群模式通过分离关注点来解决这个问题Web Server 专司调度和界面展示一个或多个 Executor Server 负责实际执行任务。这样Web 界面挂了不影响已提交任务的执行Executor 独立运行Executor 挂了可以自动转移到其他节点并且可以通过增加 Executor 来水平扩展任务执行能力。4.1 集群架构深度解析在集群模式下你需要部署至少两个独立的服务Azkaban Web Server职责提供 Web 用户界面管理项目、权限、定时调度。它接收用户的操作指令但并不自己运行任务而是将任务派发给可用的 Executor。核心组件内置一个ExecutorManager它维护着一个可用 Executor 的列表从数据库读取并负责负载均衡和故障转移。存储所有元数据项目文件、工作流定义、执行记录、调度计划都存储在 MySQL 中。Azkaban Executor Server职责任务的“苦力”。它从 Web Server 领取任务指令在本地启动一个独立的 JVM 进程来执行具体的任务如 Shell、Python、Spark Job并负责收集日志、上报状态。特点每个 Executor 可以配置同时运行的任务数maxThreads。可以部署多个 Executor 在多台机器上以实现分布式执行和负载均衡。活性Executor 启动后会主动向数据库“心跳表”注册自己。Web Server 通过轮询这张表来感知哪些 Executor 是活跃的。通信机制Web Server 和 Executor 之间不直接通信。它们通过共享的 MySQL 数据库进行状态同步。Web Server 将任务执行请求写入数据库处于空闲状态的 Executor 会定期扫描数据库领取属于自己的任务更新状态执行最后将结果和日志写回数据库。Web Server 则从数据库读取状态更新并展示给用户。这是一种基于数据库队列的松耦合设计优点是架构简单避免了复杂的 RPC 调用但数据库成为了性能和单点的关键。4.2 Web Server 独立部署将azkaban-web-server-*.tar.gz解压到/opt/azkaban/web。其配置与 Solo 类似但更专注于调度。编辑conf/azkaban.properties除了基础的数据库、邮件配置外需要特别关注以下集群相关配置# 指定Executor的选取策略常用的是随机Random或轮询RoundRobin azkaban.executorselector.filtersStaticRemainingFlowSize,MinimumFreeMemory,CpuStatus azkaban.executorselector.comparator.NumberOfAssignedFlowComparator1 azkaban.executorselector.comparator.Memory1 azkaban.executorselector.comparator.LastDispatched1 azkaban.executorselector.comparator.CpuUsage1 # 设置Web Server的模式为“executor”表示它自己不执行任务而是分发任务。 executor.port12321 # 注意这个端口在Web Server配置中仅用于标识实际不由Web Server监听 azkaban.executor.connector.port12322 # Executor的RPC端口用于日志推送新版可能变化 # 关键指定Executor的发现方式为数据库 azkaban.executor.dispatcherDatabaseExecutorDispatcher # Web Server 检查 Executor 活跃状态的频率毫秒 executor.healthcheck.interval5000 # 认为 Executor 失效的阈值毫秒 executor.max.failure.count6编辑conf/azkaban-users.xml配置用户。然后启动 Web Servercd /opt/azkaban/web bin/start-web.sh查看logs/azkaban-webserver.log确保启动成功。此时访问 Web UI在 “Executor” 管理页面应该看不到任何活跃的 Executor因为还没有启动。4.3 Executor Server 独立部署与配置将azkaban-exec-server-*.tar.gz解压到/opt/azkaban/exec1如果有多台可以叫 exec2, exec3 等。每台执行器机器都需要有任务运行所需的环境如 Hadoop、Spark、Python 环境。编辑conf/azkaban.properties其配置相对简单# 数据库配置必须与Web Server使用同一个数据库 database.typemysql mysql.port3306 mysql.hostyour_mysql_host mysql.databaseazkaban mysql.userazkaban mysql.passwordYourSecurePassword # Executor 自身标识 azkaban.webserver.urlhttp://your-web-server-ip:8081 # 指向Web Server的地址用于回调 executor.port12321 # **重要**此Executor的服务端口需唯一。Web Server通过此端口识别不同Executor。 executor.hostyour-executor-host-ip # 此Executor服务器的主机名或IP # 执行能力配置 executor.maxThreads50 # 此Executor同时可运行的最大任务数。根据机器CPU和内存调整。 executor.flow.threads30 # 同时可运行的最大工作流数。 # 任务运行相关 azkaban.temp.dir/opt/azkaban/exec1/temp # 本地临时目录 execute.as.userfalse # 是否以提交任务的用户身份运行。生产环境若需多用户隔离可设为true并配置sudo。关键点executor.port和executor.host是 Executor 在集群中的唯一标识。Web Server 通过这个host:port组合来区分不同的执行器。启动 Executorcd /opt/azkaban/exec1 bin/start-exec.sh启动后检查日志logs/azkaban-execserver.log应该看到 Executor 成功连接到数据库并定期更新心跳。此时再刷新 Web UI 的 “Executor” 页面你应该能看到一个状态为 “Active” 的 Executor其 IP 和端口就是刚才配置的。4.4 激活 Executor 与集群验证新部署的 Executor 默认是“非活跃”状态。这是一个安全设计防止未经配置的 Executor 意外接收任务。你需要手动激活它。在 Executor 服务器的lib目录下找到azkaban-exec-server-*.jar文件用以下命令查找其内置的激活密钥该密钥在每次启动时随机生成cd /opt/azkaban/exec1 grep -r executor.activate logs/azkaban-execserver.log你会在日志中找到一行类似“Generated active key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”的信息。使用这个密钥通过 Azkaban 提供的 API 或脚本激活 Executor。最简单的方法是使用curl命令curl -G http://your-executor-host-ip:$(cat executor.port)/executor?actionactivate \ --data-urlencode ajaxactivate \ --data-urlencode keyxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx如果返回{status:success}则表示激活成功。executor.port文件通常在 Executor 安装目录下里面记录了端口号。再次查看 Web UI 的 “Executor” 页面该 Executor 的状态应变为绿色 “Active”。现在集群模式已经搭建完成。你可以像在 Solo 模式下一样创建一个项目并上传工作流。当执行工作流时Web Server 会从数据库的活跃 Executor 列表中选取一个根据配置的选取策略将任务分配给它。你可以在任务执行详情页看到具体是哪个 Executor (host:port) 执行了该任务。5. 高级配置、优化与故障排查5.1 关键插件配置与使用Azkaban 的核心是任务调度其任务执行能力通过插件扩展。默认支持command类型执行 Shell 命令。要执行 Hadoop、Spark、Hive 等任务需要配置对应插件。安装插件从 Azkaban 官网或编译好的包中找到azkaban-jobtype-*.tar.gz和azkaban-hdfs-security-plugin-*.tar.gz如果涉及 Hadoop 安全认证。将其解压到 Web Server 和每个Executor Server 的plugins/jobtypes/目录下。确保所有节点插件目录结构一致。配置插件重点是修改plugins/jobtypes/commonprivate.properties或common.properties。# 指定任务运行用户的代理如果 execute.as.usertrue jobtype.global.classpath${azkaban.home}/plugins/jobtypes/lib/* # 对于Hadoop任务指定Hadoop原生库路径和配置文件路径 hadoop.home/usr/lib/hadoop hadoop.conf.dir/etc/hadoop/conf # 对于Spark任务指定Spark home spark.home/usr/lib/spark # 对于Java任务指定自定义类路径等 # java.classpath...使用插件在.job文件中将type改为对应的插件名如typespark并设置插件所需的参数如spark.app.name,spark.master,spark.class等。注意事项插件配置是 Azkaban 踩坑高发区。最常见的问题是环境变量和类路径。务必确保 Executor 服务器上的系统环境变量如HADOOP_HOME,SPARK_HOME与插件配置一致并且执行任务的用户有权限访问相关目录和命令。5.2 性能调优与安全加固数据库优化Azkaban 重度依赖 MySQL。除了之前提到的配置还应考虑为executors和execution_flows等核心表建立合适的索引。根据任务量调整mysql.numconnections连接池大小。定期清理历史执行记录Azkaban 有内置的清理任务可在 Web UI 配置。Executor 资源控制executor.maxThreads不宜设置过高否则会拖垮服务器。建议根据 CPU 核心数和任务类型IO密集型或CPU密集型来设定。监控 Executor 服务器的 CPU、内存和磁盘 I/O。日志管理Azkaban 任务日志默认存储在数据库的BLOB字段中对于大量长日志会影响数据库性能。可以配置将日志存储到 HDFS 或本地文件系统。在azkaban.properties中配置azkaban.logstorage.typehdfs或local。安全加固网络隔离将 Web Server 部署在内网通过反向代理如 Nginx对外提供 HTTPS 访问。Executor 服务器应与 Web Server 和数据库网络互通但不应直接暴露给外网。用户认证尽快将azkaban-users.xml文件认证方式迁移到更安全的 LDAP、OAuth 或 SAML。权限细分利用 Azkaban 的 Project 权限和 User Role遵循最小权限原则分配访问和操作权限。5.3 常见问题与排查实录即使按照步骤操作也难免会遇到问题。这里记录几个我踩过的坑和排查思路。问题一Web Server 启动失败报数据库连接错误。现象日志中出现Communications link failure或Access denied for user。排查检查azkaban.properties中的数据库连接信息主机、端口、库名、用户、密码是否正确。确认 MySQL 服务是否正常运行且允许从 Azkaban 服务器 IP 连接。检查 MySQL 用户权限GRANT ALL PRIVILEGES ON azkaban.* TO azkaban% IDENTIFIED BY password; FLUSH PRIVILEGES;生产环境建议限制IP。对于 MySQL 8.0检查用户密码插件问题如前文所述。问题二任务一直处于“准备中”PREPARING状态不执行。现象在 Web UI 点击执行后任务状态卡在 “PREPARING”日志也无输出。排查检查 Executor 状态这是最常见的原因。去 “Executor” 页面确认有状态为 “Active” 的 Executor。如果没有按 4.4 节步骤激活。检查队列Azkaban 有排队机制。如果executor.maxThreads已满新任务会排队。检查 Executor 的当前负载。查看 Web Server 日志在azkaban-webserver.log中搜索任务 ID看是否有 “Cannot dispatch execution” 之类的错误可能原因是 Web Server 无法与数据库中的 Executor 记录同步。问题三任务执行失败报 “Cannot run program” 或 “Permission denied”。现象任务状态为 “FAILED”日志显示命令找不到或权限错误。排查路径问题Azkaban Executor 执行命令时使用的是 Executor 服务器上的环境。确保你在.job文件中写的命令如python,hadoop,spark-submit在该服务器的PATH环境变量中或者使用绝对路径。用户权限如果execute.as.userfalse任务将以启动 Executor 服务的系统用户如azkaban运行。确保该用户有权限执行命令、读写相关目录。例如如果任务要写 HDFS需要该用户的 Kerberos keytab 或相应的代理用户配置。插件配置对于 Hadoop/Spark 任务检查插件配置文件中的hadoop.home,spark.home等路径是否正确以及相关配置文件如core-site.xml,hdfs-site.xml是否存在且可读。问题四上传项目 ZIP 包失败报 “Max allowed packet” 相关错误。现象上传较大的 ZIP 包时失败。解决这就是为什么要在 MySQL 配置中设置max_allowed_packet1024M。修改后需重启 MySQL 服务。问题五任务日志在 Web UI 中看不到或加载慢。排查检查 Executor 的executor.port是否正确且该端口在防火墙上对 Web Server 开放因为日志是通过 HTTP 从 Executor 拉取的。考虑配置外部日志存储HDFS/Local减轻数据库压力。搭建和运维 Azkaban 集群是一个细致活尤其是在与 Hadoop/Spark 等大数据生态集成时。最好的办法是每做一项配置变更都先用一个最简单的echo任务进行验证确保基础通路是顺畅的然后再逐步增加复杂度。它的稳定性一旦建立对于管理成百上千个定时任务来说效率提升是巨大的。