工作流调度工具--Oozie

一、背景

一个完整的数据分析系统通常是由大量的任务单元组成,Shell脚本、Java程序、MapReduce程序、Hive脚本等等,各个任务单元之间存在时间先后及前后依赖关系。

      为了很好的组织这样的复杂执行计划,需要一个工作流调度系统来调用执行。

      简单的工作流调度:Liunx的crontab来定义

      复杂的工作流调度:Oozie、Azakaban等。

在没有工作流调度系统之前,公司里面的任务都是通过 crontab 来定义的,时间长了后会发现很多问题:

1.大量的crontab任务需要管理
2.任务没有按时执行,各种原因失败,需要重试
3.多服务器环境下,crontab分散在很多集群上,光是查看log就很花时间

而在大数据领域,现在市面上常用的工作流调度工具有Oozie, Azkaban,Cascading等,

Oozie和Azkaban来做对比:

两者在功能方面大致相同,只是Oozie底层在提交Hadoop Spark作业是通过org.apache.hadoop的封装好的接口进行提交,而Azkaban可以直接操作shell语句。在安全性上可能Oozie会比较好。

  • 工作流定义: Oozie是通过xml定义的,而Azkaban为properties来定义。
  • 部署过程: Oozie的部署相对困难些,同时它是从Yarn上拉任务日志。
  • 任务检测: Azkaban中如果有任务出现失败,只要进程有效执行,那么任务就算执行成功,这是BUG,但是Oozie能有效的检测任务的成功与失败。
  • 操作工作流: Azkaban使用Web操作。Oozie支持Web,RestApi,Java API操作。
  • 权限控制: Oozie基本无权限控制,Azkaban有较完善的权限控制,供用户对工作流读写执行操作。
  • 运行环境: Oozie的action主要运行在hadoop中,而Azkaban的actions运行在Azkaban的服务器中。
  • 记录workflow的状态: Azkaban将正在执行的workflow状态保存在内存中,Oozie将其保存在Mysql中。
  • 出现失败的情况: Azkaban会丢失所有的工作流,但是Oozie可以在继续失败的工作流运行

二、什么是Oozie?

      Oozie(驯象人)是一个基于工作流引擎的开源框架,用在一个工作流内以一个特定顺序运行一组工作或流程。

      Oozie在集群中扮演的是定时调度任务,多任务,并开源按照业务逻辑顺序调度。

组成

Workflow顺序执行流程节点,执行分支多节点或合并多分点为一个分支。

Coordinator定时触发Workflow

BundleJob绑定多个Coordinator

Oozie节点

      控制流节点(Control Flow Nodes):一般都是定义在工作流开始或结束,比如start、end、kill等,以及提供工作流的执行路径。

      动作节点(Action Nodes):执行具体动作的节点。

三、Oozie的基本操作:

# 运行一个应用:
bin/oozie job -oozie http://nameNode:11000/oozie -config examples/apps/map-reduce/job.properties -run

# 杀掉一个job
bin/oozie job -oozie http://nameNode:11000/oozie  -kill 0000001-160702224410648-oozie-beif-W

# 查看job的日志信息
bin/oozie job -oozie http://nameNode:11000/oozie -log 0000001-160702224410648-oozie-beif-W

# 查看job的信息
bin/oozie job -oozie http://nameNode:11000/oozie -info 0000001-160702224410648-oozie-beif-W

操作准备;

job.properties

任务的一些配置文件,绑定多个 Coordinator。例如运行的HDFS以及JobTracker(ResourceManager)等

Workflow.xml

工作流的依赖,配置顺序执行流程节点,支持 fork(分支多个节点),join(合并多个节点为一个)

coordinator.xml定时触发 workflow,在这里可以设定触发oozie任务的时间和间隔

****.jar

执行任务的jar包(自定义的文件)

oozie工作流调度的设置总结

1 解压
cd /export/servers/oozie-4.1.0-cdh5.14.0
tar -zxf oozie-examples.tar.gz

2 拷贝该任务的模板

3 修改job.properties 文件

4 修改workfolw.xml的文件

5 上传已配置好的文件上传的hdfs上

6 执行任务 

案例

job.properties

#NameNode地址
nameNode=hdfs://node01:8020       #端口8082

#ResourceManager地址,默认端口8032
jobTracker=node01:8032

#内部的Event队列名称
queueName=default
examplesRoot=oozie-apps #这是一个参数变量 在workflow.xml中会用到

#是否使用系统依赖包 一般都是填true
oozie.use.system.libpath=true

#程序位置hdfs // workflow.xml的地址
oozie.wf.application.path=${nameNode}/user/${user.name}/${examplesRoot}/shell

#coordinator.xml在hdfs的位置
#oozie.coord.application.path=${nameNode}/home/ssh_jobs/test_init_and_load

#执行这个脚本
EXEC1=test1.sh
EXEC2=test2.sh
 
#任务开始时间与结束时间 以UTC时区为准(start:必须设置为未来时间,否则任务失败)
start=2020-12-25T10:10+0800
end=2026-12-21T23:59+0800

workflow.xml

<workflow-app xmlns="uri:oozie:workflow:0.3" name="shell-wf">
    <start to="get_var"/>
    <action name="get_var">
        <shell xmlns="uri:oozie:shell-action:0.1">
            <job-tracker>${jobTracker}</job-tracker>
            <name-node>${nameNode}</name-node>
            <configuration>
                <property>
                    <name>mapred.job.queue.name</name>
                    <value>${queueName}</value>
                </property>
            </configuration>
            <exec>${EXEC1}</exec>
            <file>/user/admin/oozie-apps/shell/${EXEC1}#${EXEC1}</file>
            <capture-output/> 
            <argument>hi shell in oozie</argument>
        </shell>
        <ok to="end"/>
        <error to="fail"/>
    </action>

    <kill name="fail">
        <message>Map/Reduce failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>
    </kill>
    <end name="end"/>
</workflow-app>
   
<workflow-app xmlns="uri:oozie:workflow:0.4" name="cip_poi_etl_workflow">  
    <start to="get_var"/>  

    <action name="get_var">  
        <shell xmlns="uri:oozie:shell-action:0.1">  
            <job-tracker>${jobTracker}</job-tracker>  
            <name-node>${nameNode}</name-node>  
            <configuration>  
                <property>  
                    <name>mapred.job.queue.name</name>  
                    <value>${queueName}</value>  
                </property>  
            </configuration>  
            <exec>${EXEC1}</exec>
            <file>/user/admin/oozie-apps/shell/${EXEC1}#${EXEC1}</file>
            <!--
            <exec>input.sh</exec>  
            <file>input.sh</file> 
            -->
            <capture-output/>    <!--它的作用是是告诉oozie,这个action的输出要被oozie运行存储下来。 -->
        </shell>  
        <ok to="use_var" />  
        <error to="send_email" />  
    </action>  

    <action name="use_var">  
        <shell xmlns="uri:oozie:shell-action:0.1">  
            <job-tracker>${jobTracker}</job-tracker>  
            <name-node>${nameNode}</name-node>  
            <configuration>  
                <property>  
                    <name>mapred.job.queue.name</name>  
                    <value>${queueName}</value>  
                </property>  
            </configuration>  
            <exec>output.sh</exec> 
<!--它的作用是说我要调用“get_var”这个action捕捉到的那个输出的其中一个参数。 --> 
            <argument>${wf:actionData('get_var')['var']}</argument>  
            <file>output.sh</file>  
        </shell>  
        <ok to="end" />  
        <error to="send_email" />  
    </action>  

    <action name="send_email">  
        <email xmlns="uri:oozie:email-action:0.1">  
            <to>${emailTo}</to>  
            <subject>Status of workflow ${wf:id()}</subject>  
            <body>The workflow ${wf:id()} had issues and was killed. The error message is: ${wf:errorMessage(wf:lastErrorNode())}</body>  
        </email>  
        <ok to="fail"/>  
        <error to="fail"/>  
    </action>  

    <kill name="fail">  
        <message>Failed, Error Message[${wf:errorMessage(wf:lastErrorNode())}]</message>  
    </kill>  
    <end name="end"/>  
</workflow-app>
input.sh
echo var=var_test_result

output.sh
hadoop fs -mkdir /user/hue/$1

test1.sh

#!/bin/bash
/usr/bin/date > /tmp/test.log
/usr/bin/date > /tmp/test.log

执行:

上传到HDFS:

hdfs dfs -put oozie-apps/*  /user/admin/oozie-apps/shell/

执行任务:

bin/oozie job -oozie http://node01:11000/oozie -config oozie-apps/shell/job.properties -run

使用注意事项

[if !supportLists]1.    [endif]启动不了,则到oozie-server/temp查看是否有*.pid文件,有就删除后在启动。

[if !supportLists]2.    [endif]如果无法关闭oozie则kill掉。

[if !supportLists]3.    [endif]Mysql配置如果没有生效的话,默认使用derby数据库

[if !supportLists]4.    [endif]在本地修改完成的job配置,必须重新上传到HDFS。

[if !supportLists]5.    [endif]Linux用户名和Hadoop的用户名不一致。

[if !supportLists]6.    [endif]时区

四、在hue当中操作ooize的操作,如何使用Hue上创建一个完整Oozie工作流

在hue当中操作ooize的操作:

Hue是一个可快速开发和调试Hadoop生态系统各种应用的一个基于浏览器的图形化用户接口。

Hue可实现对oozie任务的开发,监控,和工作流协调调度 。使的oozie的操作变得更加的简单快捷。

(1)创建工作流

(1)添加到定制执行任务。指定定时任务。

一、在hue当中操作ooize的操作

进入hue首页:

  

Workflow是工作流,Schedule是调度工作流的,如设置工作流何时跑,周期是多久,等等,下面会详细介绍,Bundle是绑定多个调度,暂时我没有用上

等使用后再更新

上面有一栏,有hive,hive2,spark,java,shell等等,直接拖入到Drop your action here这个阴影框中即可

下面的都以hive2为例,

选择文件添加即可,同时下面还有很多选项

 友情提示,虽然已经选择了文件,但是我建议还是在选择一次,右边的是添加文件依赖,

左边是参数设置,比如工作流按时间跑,设置是时间等等,根据需求设置即可

  

工作流建立完成之后,单个脚本或者代码可以单独执行,进行测试,再右上角

或者整个工作流进行运行,也是在右上角

这是工作流的配置,下面讲调度

进入调度

选择一个workflow工作流

注意hue,oozie是有时区的设置的,默认是零时区时间,换成东八区时间要+8小时,设置配置时间同步

由于公司没有设置,所以是在建立调度上自己注意的,这里的时间都是零时区的时间

可以按小时,天,周,月,年进行定时调度

 因为是今天跑昨天的数据,所以数据是昨天的,时间要减,因为是零时区的时间和东八区的时间差了8个小时,所以减了16个小时

下面的是输入文件,起到标识作用,有了这个文件任务才能启动,也是一种依赖文件

还有一种情况

如果是周计划,或者月计划,需要依赖多个,如7个,30个文件,不可能一一配置

选择上面的range,-6代表从任务执行的是时间-6,因为时区原因,如果是星期一启动任务,则到了上周星期一,0到了上周日

这是跑周计划中依赖一周的文件,其他情况可以参考上面的,可能会一些时区或者平台的问题有一些出入,配置根据具体情况而定

任务运行后可以查看执行情况:在右上角,

自己选择查看类型,里面也有日志,执行task,执行参数,时间等等

二、本篇文章主要讲述如何使用Hue创建一个以特定顺序运行的Oozie工作流。

本文工作流程如下:

2.创建一个Parquet格式的Hive表


创建一个Hive表,该表用于Spark作业保存数据,注意这里创建的Parquet格式的表

create table testaaa (
'_age' bigint COMMENT   '',
'_id' bigint COMMENT  '',
'_name'  string COMMENT ''
) STORED AS parquet

3.Sqoop抽数作业


这里的Sqooop抽数以MySQL为例。

1.创建一个MySQL的测试账号及准备测试数据

CREATE USER 'testuser'@'%' IDENTIFIED BY 'password';

GRANT ALL PRIVILEGES ON *.* TO 'testuser'@'%';

FLUSH PRIVILEGES;

向表中插入数据

insert into test_user (name,age) values('user1', 12);
insert into test_user (name,age) values('user2', 13);
insert into test_user (name,age) values('user3', 14);
insert into test_user (name,age) values('user4', 15);
insert into test_user (name,age) values('user5', 16);
insert into test_user (name,age) values('user6', 17);
insert into test_user (name,age) values('user7', 18);
insert into test_user (name,age) values('user8', 19);

2.Sqoop抽数脚本

sqoop import \
--connect jdbc:mysql://ip-172-31-22-86.ap/test_db 
--username username \
--password password \
--table test_user \
--target-dir /tmp/sqoop \
-m 1

4.Spark ETL作业


将Sqoop抽取的数据通过Python的Spark作业进行ETL操作写入Hive表中

1.编写Spark脚本

#!/usr/local/anaconda3/bin/python
#coding:utf-8

# 初始化sqlContext
from pyspark import SparkConf,SparkContext
from pyspark.sql import HiveContext,Row
conf=(SparkConf().setAppName('PySparkETL'))
sc=SparkContext(conf=conf)
sqlContext = HiveContext(sc)

# 加载文本文件并转换成Row.
lines = sc.textFile("/tmp/sqoop/part-*")
parts = lines.map(lambda l: l.split(","))
people = parts.map(lambda p: Row(id=int(p[0]),name=p[1], age=int(p[2])))

# 将DataFrame注册为table.
schemaPeople = sqlContext.createDataFrame(people)
schemaPeople.registerTempTable("people")

sqlContext.cacheTable("people")

# 执行sql查询,查下条件年龄在13岁到16岁之间
teenagers = sqlContext.sql("SELECT * FROM people WHERE age >= 13 AND age <= 16")

teenagers.write.saveAsTable("testaaa", mode="append")

5.Hive查询作业


将Spark作业处理后的数据写入hive表中,使用Hive对表进行查询操作

编写hive-query.sql文件,内容如下:

select * from testaaa where age>=10 and age<=15

6.创建工作流


1、进入Hue界面,选择”Workflows” => “Editors”=> “Workflows”

2、在以下界面中点击“Create”按钮创建工作流

3.然后进入WrokSpace

4.在工作流中添加Sqoop抽数作业

5.添加PySpark ETL工作流

5.添加Hive工作流

如下是一个完成的工作流

7.工作流运行


1.工作流保存成功后,点击运行

2.Oozie调度任务执行成功

8.作业运行结果查看


Sqoop抽数结果查看

Spark ETL执行成功查看Hive表testaaa数据

Hive作业执行结果查看

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四月天03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值