1. 从手动到自动:为什么我们需要工作流调度
如果你在数据团队或者运维团队待过,大概率经历过这样的场景:凌晨两点,手机突然响起,告警提示昨晚的数据报表没有按时生成。你睡眼惺忪地爬起来,登录服务器,检查日志,发现是上游的数据清洗任务失败了,导致下游的报表任务一直在等待。你手动执行了失败的任务,然后祈祷下游的依赖任务能自动续上。折腾了一个小时,终于跑通,天也快亮了。
这种“救火”经历,正是工作流调度系统要解决的核心痛点。在数据仓库、ETL(数据抽取、转换、加载)、机器学习模型训练、甚至是日常的服务器日志备份等场景中,任务之间往往存在复杂的依赖关系。A任务必须在B任务成功完成后才能启动,C和D任务可以并行执行以提升效率,而E任务则需要等待所有前置任务都完成。手动管理这些依赖,不仅效率低下,而且极易出错,一旦某个环节失败,整个流程就可能停滞,需要人工介入排查和恢复。
Azkaban,正是为解决这类问题而生的开源工作流调度系统之一。它由LinkedIn开发并开源,其设计哲学非常直接:用简单、清晰的描述语言定义任务依赖,用一个可靠的中心化服务来调度和执行这些任务。你可以把它想象成一个智能的、永不疲倦的“流程指挥官”,它严格按照你设定的剧本(工作流),在正确的时间、按正确的顺序,指挥各个“演员”(计算任务)登台表演。
在开始动手安装之前,我们需要理解Azkaban的几个关键特性,这能帮助我们在后续的选型和配置中做出更合理的决策:
- 依赖驱动的调度:这是核心。任务是否执行,不单纯看时间,更看它的前置任务状态。这完美契合了数据处理管道“流水线”式的特点。
- Web UI操作界面:提供了直观的界面来上传工作流、查看执行日志、监控任务状态、设置定时调度等,降低了使用门槛。
- 插件化架构:Azkaban本身不关心你具体执行的是Shell脚本、Python程序、Hive SQL还是Spark作业。它通过执行器插件来支持各种类型的任务,这种解耦设计使其具备了良好的扩展性。
- 轻量级与易部署:相比一些“重量级”的调度系统,Azkaban的架构相对简单,核心组件不多,对于中小型团队或个人项目来说,部署和维护成本更低。
接下来,我们将从零开始,完成一个Azkaban单机版的部署。这个版本包含了Azkaban最核心的Web Server和Executor Server,适合学习、测试和小规模生产环境使用。
2. 部署前准备:环境与资源盘点
在下载任何安装包之前,充分的准备工作能避免后续80%的坑。Azkaban的运行依赖于Java和数据库,我们需要确保这些基础环境就绪。
2.1 基础环境要求
首先,确认你的服务器环境。我以一台干净的CentOS 7.x虚拟机为例进行说明,其他Linux发行版在命令上可能略有差异,但思路一致。
- 操作系统:主流Linux发行版均可(如CentOS, Ubuntu)。确保你有sudo或root权限。
- Java:Azkaban 3.x版本需要JDK 8或更高版本。这是硬性要求,不兼容的JDK版本会导致启动失败。
- 数据库:Azkaban需要数据库来存储项目、工作流、执行历史等元数据。官方支持MySQL。在生产环境强烈建议使用独立的MySQL实例,在测试环境我们可以在本地安装。
提示:虽然Azkaban也支持H2内存数据库(用于快速启动测试),但任何重启都会导致数据丢失,因此仅适用于初次体验,不适用于任何需要持久化的场景。
2.2 安装与验证JDK
如果你的系统还没有安装JDK 8,可以通过以下步骤安装OpenJDK:
# 1. 检查是否已安装Java java -version # 如果未安装或版本不对,使用yum安装OpenJDK 8(CentOS 7) sudo yum install -y java-1.8.0-openjdk-devel # 对于Ubuntu/Debian系统,可以使用 # sudo apt-get update # sudo apt-get install -y openjdk-8-jdk # 2. 安装后再次验证版本 java -version # 应输出类似:openjdk version "1.8.0_392"确保JAVA_HOME环境变量已正确设置。你可以通过echo $JAVA_HOME查看。如果未设置,可以将其添加到~/.bashrc或/etc/profile中:
# 查找JDK安装路径,通常类似于 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.392.b08-2.el7_9.x86_64 sudo alternatives --config java # 会显示Java路径,例如 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.392.b08-2.el7_9.x86_64/jre/bin/java # JAVA_HOME是上述路径去掉最后的 `/jre/bin/java` # 编辑配置文件 echo "export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.392.b08-2.el7_9.x86_64" >> ~/.bashrc echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> ~/.bashrc # 使配置生效 source ~/.bashrc2.3 安装与配置MySQL数据库
接下来安装MySQL,并创建Azkaban所需的数据库和用户。
# 1. 安装MySQL服务器(CentOS 7) sudo yum install -y mariadb-server mariadb # 启动MySQL服务并设置开机自启 sudo systemctl start mariadb sudo systemctl enable mariadb # 2. 运行安全初始化脚本,设置root密码(建议设置一个强密码) sudo mysql_secure_installation # 过程中会提示设置root密码、移除匿名用户、禁止root远程登录等,根据提示选择即可。 # 3. 登录MySQL,为Azkaban创建专属数据库和用户 mysql -u root -p # 输入你刚才设置的root密码 # 在MySQL命令行中执行以下SQL语句 CREATE DATABASE azkaban DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 创建一个新用户,例如‘azkaban’,并设置密码(请替换‘YourStrongPassword123!’为你的密码) CREATE USER 'azkaban'@'localhost' IDENTIFIED BY 'YourStrongPassword123!'; # 授予该用户对azkaban数据库的所有权限 GRANT ALL PRIVILEGES ON azkaban.* TO 'azkaban'@'localhost' WITH GRANT OPTION; # 如果Azkaban Web/Executor Server和MySQL不在同一台机器,需要授权远程连接(生产环境常见) # CREATE USER 'azkaban'@'%' IDENTIFIED BY 'YourStrongPassword123!'; # GRANT ALL PRIVILEGES ON azkaban.* TO 'azkaban'@'%' WITH GRANT OPTION; # 刷新权限 FLUSH PRIVILEGES; # 退出MySQL EXIT;完成以上步骤后,你的基础运行环境就已经准备好了。我们有了Java运行环境和用于存储元数据的MySQL数据库。接下来,就是获取和配置Azkaban本身。
3. 获取与解压:Azkaban安装包的选择
Azkaban的发布包可以在其GitHub仓库的Release页面找到。这里有一个关键选择:是使用预编译的发行版,还是从源码编译?
对于绝大多数用户,我强烈建议直接下载预编译的发行版(Release Package)。源码编译需要配置Gradle、处理依赖,过程繁琐且容易出错,除非你需要修改Azkaban的源代码,否则完全没有必要。
访问 Azkaban GitHub Releases 页面,找到最新的稳定版本。在写作本文时,3.91.0是一个广泛使用的稳定版本。你会看到多个文件:
azkaban-web-server-3.91.0.tar.gz: Web服务器包,提供用户界面和API。azkaban-exec-server-3.91.0.tar.gz: 执行服务器包,负责任务的实际执行。azkaban-sql-3.91.0.tar.gz: 包含创建数据库表的SQL脚本。azkaban-db-3.91.0.tar.gz: 另一个包含数据库脚本的包(通常与上一个相同)。
我们主要需要前三个。在服务器上,选择一个合适的目录进行安装,例如/opt/azkaban。
# 创建安装目录 sudo mkdir -p /opt/azkaban cd /opt/azkaban # 下载安装包(请将版本号替换为最新的稳定版) sudo wget https://github.com/azkaban/azkaban/releases/download/3.91.0/azkaban-web-server-3.91.0.tar.gz sudo wget https://github.com/azkaban/azkaban/releases/download/3.91.0/azkaban-exec-server-3.91.0.tar.gz sudo wget https://github.com/azkaban/azkaban/releases/download/3.91.0/azkaban-sql-3.91.0.tar.gz # 解压 sudo tar -zxvf azkaban-web-server-3.91.0.tar.gz sudo tar -zxvf azkaban-exec-server-3.91.0.tar.gz sudo tar -zxvf azkaban-sql-3.91.0.tar.gz # 为了方便管理,可以创建软链接或重命名目录 sudo ln -s azkaban-web-server-3.91.0 web sudo ln -s azkaban-exec-server-3.91.0 exec现在,你的/opt/azkaban目录结构应该类似于:
/opt/azkaban/ ├── azkaban-web-server-3.91.0.tar.gz ├── azkaban-exec-server-3.91.0.tar.gz ├── azkaban-sql-3.91.0.tar.gz ├── web -> azkaban-web-server-3.91.0 │ ├── bin/ │ ├── conf/ │ ├── lib/ │ ├── plugins/ │ ├── web/ │ └── ... └── exec -> azkaban-exec-server-3.91.0 ├── bin/ ├── conf/ ├── lib/ ├── plugins/ └── ...解压后,最重要的一步就是初始化数据库。进入解压出的azkaban-sql-3.91.0目录,你会看到针对不同数据库的SQL脚本。我们使用MySQL。
cd /opt/azkaban/azkaban-sql-3.91.0 # 查看目录内容,通常有 create-all-sql-3.91.0.sql 或单独的脚本 ls -la # 使用之前创建的azkaban用户和数据库,执行建表脚本 mysql -u azkaban -p azkaban < create-all-sql-3.91.0.sql # 系统会提示输入密码:YourStrongPassword123!执行成功后,登录MySQL验证表是否创建成功:
mysql -u azkaban -p azkaban SHOW TABLES;你应该能看到一系列以azkaban_开头的表,例如azkaban_projects,azkaban_flows,azkaban_executions等。这标志着数据库层已就绪。
4. 核心配置详解:让Azkaban跑起来的关键
Azkaban的配置主要集中在其conf目录下的几个属性文件中。配置错误是启动失败的最常见原因,我们需要仔细对待。
4.1 配置Azkaban Web Server
Web Server是用户交互的入口,它需要知道如何连接数据库以及一些服务器参数。
进入Web Server的配置目录:
cd /opt/azkaban/web/conf这里有两个关键文件:azkaban.properties和azkaban-users.xml。
首先,备份并编辑主配置文件azkaban.properties:
sudo cp azkaban.properties azkaban.properties.bak sudo vi azkaban.properties你需要修改以下关键参数(找到对应行并修改):
# 数据库配置 - 这是最重要的部分! database.type=mysql mysql.port=3306 mysql.host=localhost # 如果MySQL在别的机器,改为其IP mysql.database=azkaban mysql.user=azkaban mysql.password=YourStrongPassword123! # 填写你之前设置的密码 mysql.numconnections=100 # Web Server的Jetty容器配置 jetty.maxThreads=25 jetty.port=8081 # Web UI的访问端口,默认8081,可按需修改 # 时区设置,避免任务调度时间错乱 default.timezone.id=Asia/Shanghai # 根据你的实际时区修改 # Executor配置(单机模式下,Web Server也充当Executor) azkaban.executorselector.filters=StaticRemainingFlowSize,MinimumFreeMemory,CpuStatus azkaban.executorselector.comparator.NumberOfAssignedFlowComparator=1 azkaban.executorselector.comparator.Memory=1 azkaban.executorselector.comparator.LastDispatched=1 azkaban.executorselector.comparator.CpuUsage=1 # 指定Executor的端口和主机(单机模式,指向自己) executor.port=12321 # Executor服务端口 azkaban.webserver.external_hostname=localhost # 或你的服务器IP azkaban.webserver.external_port=8081注意:
mysql.password务必填写正确,并且确保MySQL允许azkaban用户从localhost连接。如果启动时出现数据库连接错误,首先检查这里。
接下来,配置用户权限文件azkaban-users.xml。Azkaban使用这个文件来管理用户和角色(生产环境建议集成LDAP等外部认证)。
sudo vi azkaban-users.xml默认文件里有一个admin用户的示例。我们修改它,添加一个自己的用户:
<azkaban-users> <user username="admin" password="admin" roles="admin" groups="azkaban" /> <user username="your_username" password="your_strong_password" roles="admin" /> <role name="admin" permissions="ADMIN" /> <role name="metrics" permissions="METRICS" /> </azkaban-users>这里创建了两个具有admin角色的用户。在生产环境中,务必使用强密码,并考虑更安全的认证方式。
4.2 配置Azkaban Executor Server
在单机部署中,Executor Server通常与Web Server部署在同一台机器上。进入Executor的配置目录:
cd /opt/azkaban/exec/conf sudo cp azkaban.properties azkaban.properties.bak sudo vi azkaban.propertiesExecutor的配置相对简单,主要需要指定数据库(用于报告执行状态)和它自己的服务端口:
# 数据库配置(和Web Server一样) database.type=mysql mysql.port=3306 mysql.host=localhost mysql.database=azkaban mysql.user=azkaban mysql.password=YourStrongPassword123! # Executor Server配置 executor.port=12321 # 必须和Web Server配置中的 executor.port 一致 maxThreads=50 # 执行线程数,根据机器资源调整 # 时区 default.timezone.id=Asia/Shanghai4.3 关于时区与编码的坑
这是我踩过的一个典型坑:任务调度时间对不上。Azkaban的调度时间依赖于JVM和数据库的时区设置。如果default.timezone.id配置不正确,或者服务器系统时区、MySQL时区不一致,可能会导致定时任务在非预期的时间触发。
排查与解决步骤:
- 统一服务器时区:使用
date命令查看系统时间,并用timedatectl set-timezone Asia/Shanghai(针对systemd系统)进行设置。 - 检查MySQL时区:登录MySQL,执行
SELECT @@global.time_zone, @@session.time_zone;。如果返回SYSTEM,则取决于操作系统时区。也可以设置为具体时区,如SET GLOBAL time_zone = ‘+8:00’;。 - 确保azkaban.properties中的时区配置与上述时区一致。
- 重启Azkaban服务以使配置生效。
另一个常见问题是中文乱码,尤其是在任务日志中。确保你的MySQL数据库创建时使用了utf8mb4字符集(如前文SQL所示),并且执行任务的脚本或环境本身也使用UTF-8编码。
5. 启动、验证与第一个工作流
配置完成后,我们就可以启动服务了。Azkaban提供了启动和停止脚本。
5.1 启动服务
先启动Executor Server,因为它需要向Web Server注册自己。
cd /opt/azkaban/exec # 使用启动脚本 sudo ./bin/start-exec.sh # 检查启动日志,查看是否有错误 tail -f logs/azkaban-execserver.log看到日志中出现类似“ExecutorServer started on port 12321”和“Started ExecutorServer”的信息,说明启动成功。
接着启动Web Server:
cd /opt/azkaban/web sudo ./bin/start-web.sh tail -f logs/azkaban-webserver.log等待日志中出现“Server running on port 8081”或“Started SelectChannelConnector@0.0.0.0:8081”的信息。
注意:首次启动Executor后,还需要激活它。Azkaban设计上需要手动激活Executor实例。 执行以下命令(假设端口是12321):
curl -G "localhost:$(<./executor.port)/executor?action=activate" && echo或者在
exec/conf目录下找到executor.port文件,查看端口号,然后访问http://你的服务器IP:执行器端口/executor?action=activate。看到返回{"status":"success"}即表示激活成功。
5.2 登录Web UI并验证
打开浏览器,访问http://你的服务器IP:8081。你应该能看到Azkaban的登录界面。使用在azkaban-users.xml中配置的用户名和密码(如admin/admin)登录。
登录成功后,进入主界面。点击右上角的 “Projects” 菜单,可以创建和管理项目。但更重要的一个验证步骤是检查Executor是否被正确识别。
点击顶部导航栏的 “Executor” 标签页。你应该能看到一个状态为 “Active” 的Executor,其IP和端口与你配置的一致。这证明Web Server和Executor Server通信正常,整个系统已就绪。
5.3 创建并运行你的第一个工作流
理论说了这么多,是时候动手跑一个最简单的例子了。Azkaban工作流通过一个或多个job文件(.job后缀)和一个project文件(.project)来定义,然后打包成ZIP文件上传。
我们来创建一个经典的 “Hello World” 多步骤工作流:任务A打印“Hello”,任务B在A成功后打印“World”。
创建工作流定义文件: 在本地创建一个临时目录,例如
my_first_flow。project.job:项目级配置文件(可选,但建议有)。# project.job azkaban-flow-version: 2.0flow.job:定义工作流,使用YAML格式(Azkaban 3.0+支持)。# flow.job config: failure.emails: your-email@example.com # 可选,失败通知邮箱 nodes: - name: jobA type: command config: command: echo "Hello from Job A" dependsOn: [] - name: jobB type: command config: command: echo "World from Job B, after Job A" dependsOn: - jobA
也可以使用传统的
.job文件方式:jobA.job:type=command command=echo "Hello from Job A"jobB.job:type=command command=echo "World from Job B, after Job A" dependencies=jobA
打包项目: 将上述所有
.job和.project文件(以及flow.job,如果用YAML)直接放在目录根下,然后打包成ZIP文件,注意不要包含顶层目录。cd my_first_flow zip -r my_first_project.zip ./*在Azkaban UI中操作:
- 登录Azkaban Web UI。
- 点击 “Create Project”,输入项目名称(如 “TestProject”)和描述,点击 “Create”。
- 进入项目后,点击 “Upload” 标签页,选择你刚打包的
my_first_project.zip文件,点击 “Upload”。 - 上传成功后,你会在 “Flows” 标签页看到定义好的工作流(例如一个名为 “my_first_flow” 的流程)。
- 点击流程名称,进入详情页。点击右侧的 “Execute Flow” 按钮。
- 在弹出窗口中,你可以配置执行参数(这里直接使用默认值),点击 “Execute”。
- 页面会自动跳转到本次执行的详情页。你可以实时看到任务
jobA和jobB的状态变化(准备中 -> 运行中 -> 成功/失败)。 - 点击每个任务,可以查看其详细的 “Log” 输出,确认是否打印出了我们预设的 “Hello” 和 “World”。
当看到两个任务都变成绿色 “Success” 状态,并且日志输出符合预期时,恭喜你!你已经成功部署了Azkaban,并运行了第一个依赖驱动的工作流。这标志着从手动运维到自动化调度的关键一步已经迈出。
6. 单机部署的局限与生产环境考量
我们目前搭建的是Azkaban的Solo Server模式,即Web Server和Executor Server在同一进程内,或者像我们这样分两个进程但在同一台机器。这种模式简单快捷,非常适合学习、测试和小型项目。
然而,对于生产环境,尤其是任务量大、可靠性要求高的场景,单机部署存在明显瓶颈:
- 单点故障:无论是Web Server还是Executor Server宕机,整个调度系统都会瘫痪。
- 资源隔离与扩展性差:所有任务都在同一个Executor上运行,资源竞争可能导致重要任务被阻塞。无法通过增加机器来水平扩展执行能力。
- 性能瓶颈:数据库、Web服务、任务执行都集中在一台机器,随着任务数量增长,性能会成为问题。
因此,生产环境通常采用多Executor模式甚至集群模式:
- 多Executor模式:部署一个Web Server和多个独立的Executor Server在不同的机器上。Web Server作为大脑,负责调度和分发任务;多个Executor作为四肢,并行执行任务。这解决了执行能力的水平扩展问题。
- 高可用模式:可以部署多个Web Server实例,通过负载均衡器(如Nginx)对外提供服务,并使用共享数据库和会话存储,避免Web层单点故障。对于Executor,由于其状态由Web Server管理,本身是无状态的(任务状态持久化在数据库),增加或减少节点相对灵活。
向生产环境演进的关键配置点:
- 数据库高可用:将MySQL配置为主从复制或集群,确保元数据存储的可靠性。
- Executor配置:在
azkaban.properties中,Web Server需要配置azkaban.executorselector.comparator相关参数,以在多Executor间进行智能的任务分发(基于负载、内存等)。 - 网络与安全:确保Web Server与各个Executor之间的网络互通,并考虑设置防火墙规则。对于Web UI,考虑配置HTTPS。
- 监控与告警:集成监控系统(如Prometheus+Grafana)来监控Azkaban各组件的健康状态、任务队列长度、执行成功率等关键指标,并设置告警。
从单机版到生产集群的搭建,是一个系统工程,涉及网络规划、资源分配和更细致的调优。但无论如何,其核心原理和我们今天完成的单机部署是一脉相承的。理解了这个基础,再去面对更复杂的架构,你就会心中有数,知道每个组件扮演的角色以及它们之间如何协作。
7. 初期使用中的常见问题与排查心法
即便按照步骤安装,初期也难免会遇到一些问题。这里分享几个我遇到过的典型问题及其排查思路,希望能帮你快速定位。
问题一:Web Server或Executor Server启动失败,日志报数据库连接错误。
- 排查思路:
- 核对配置:反复检查
conf/azkaban.properties中的mysql.host,mysql.port,mysql.database,mysql.user,mysql.password每一项。密码中的特殊字符是否需要转义? - 测试连接:在服务器上,使用
mysql -u azkaban -p -h [host]命令,用配置文件中的参数手动连接MySQL,看是否能成功。 - 检查MySQL权限:确认
azkaban用户是否确实拥有对azkaban数据库的权限,并且允许从Azkaban服务器IP连接(如果不在同一台机器)。 - 检查MySQL服务状态:
systemctl status mariadb确保MySQL服务正在运行。 - 检查防火墙:如果MySQL在远程,确保服务器防火墙开放了3306端口。
- 核对配置:反复检查
问题二:任务一直处于“准备中”(PREPARING)状态,不执行。
- 排查思路:
- 检查Executor状态:这是最常见的原因。去Web UI的 “Executor” 页面,确认是否有状态为 “Active” 的Executor。如果没有,说明Executor未启动或未激活。
- 检查Executor日志:查看
exec/logs/azkaban-execserver.log,看是否有错误信息,特别是与Web Server通信或任务执行相关的错误。 - 检查队列:在 “Executor” 页面,查看活动Executor的 “Queued Flows” 数量。如果队列堆积,可能需要等待或增加Executor资源。
- 检查任务类型:确认你定义的任务类型(如
type=command)是Azkaban支持的。检查exec/plugins目录下是否有对应的执行器插件。
问题三:任务执行失败,日志显示“命令未找到”或权限错误。
- 排查思路:
- 命令路径:在
command中,尽量使用绝对路径,例如/bin/echo而不是echo。因为Azkaban执行任务时的环境变量PATH可能与你的shell环境不同。 - 脚本权限:如果你执行的是Shell脚本,确保脚本文件有可执行权限(
chmod +x script.sh),并且脚本首行有正确的shebang(如#!/bin/bash)。 - 用户权限:Azkaban Executor默认以启动它的系统用户(如
azkaban或root)身份执行命令。确保该用户有权限执行你指定的命令或访问相关文件。出于安全考虑,建议创建一个专用的、权限受限的系统用户来运行Azkaban服务。
- 命令路径:在
问题四:定时调度(Schedule)不生效。
- 排查思路:
- 时区!时区!时区!:这是定时任务最大的坑。确保Web Server、Executor Server的
azkaban.properties中的default.timezone.id、服务器系统时区、MySQL全局时区全部一致,并且是你期望的时区(如Asia/Shanghai)。 - Cron表达式:在设置Schedule时,Azkaban使用的是标准的Cron表达式。确认你的表达式语法正确,并且时间符合预期。可以使用在线的Cron表达式验证工具辅助检查。
- 检查调度线程:查看Web Server日志,是否有调度相关的错误。
- 时区!时区!时区!:这是定时任务最大的坑。确保Web Server、Executor Server的
掌握“看日志”这项基本技能至关重要。Azkaban的日志文件(位于web/logs/和exec/logs/下)通常包含了非常详细的错误信息。遇到问题,第一反应就应该是tail -f查看最新的日志输出,结合错误关键词(如ERROR,Exception)进行搜索,大部分问题都能在这里找到线索。
安装和初步配置只是使用Azkaban的第一步。真正发挥其威力的,在于如何设计健壮、高效、可维护的工作流,如何处理任务失败的重试与告警,如何与Hadoop、Spark、Hive等大数据组件集成。这些内容,我们将在后续的文章中继续深入探讨。