airflow探索篇

R统计师
• 阅读 22228
airflow是一个 Airbnb 的 Workflow 开源项目,在Github 上已经有超过两千星。data pipeline调度和监控工作流的平台,用于用来创建、监控和调整data pipeline。类似的产品有:Azkaban、oozie 

pip方式安装

默认已经安装python >= 2.7 以及 pip
安装可以参考这篇,比较详细。airflow安装以及celery方式启动

重要说明

使用mysql需要安装

python 2 : pip install MySQL-python
python 3 : pip install PyMySQL

AIRFLOW_HOME配置说明

上篇在.bashrc中配置的export AIRFLOW_HOME=/home/airflow/airflow01。AIRFLOW_HOME设置目录在airflow initdb的时候初始化,存放airflow的配置文件airflow.cfg及相关文件。

DAG说明-管理建议

默认$AIRFLOW_HOME/dags存放定义的dag,可以分目录管理dag。常用管理dag做法,dag存放另一个目录通过git管理,并设置软连接映射到$AIRFLOW_HOME/dag。好处方便dag编辑变更,同时dag变更不会出现编辑到一半的时候就加载到airflow中。

plugins说明-算子定义

默认$AIRFLOW_HOME/plugins存放定义的plugins,自定义组件。可以自定义operator,hook等等。我们希望可以直接使用这种模式定义机器学习的一个算子。下面定义了一个简单的加法算子。

# -*- coding: UTF-8 -*-
# !/usr/bin/env python

from airflow.plugins_manager import AirflowPlugin
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults

# Will show up under airflow.operators.plus_plugin.PluginOperator
class PlusOperator(BaseOperator):

    @apply_defaults
    def __init__(self, op_args=None, params=None, provide_context=False, set_context=False, *args, **kwargs):
        super(PlusOperator, self).__init__(*args, **kwargs)
        self.params = params or {}
        self.set_context = set_context

    def execute(self, context):
        if self.provide_context:
            context.update(self.op_kwargs)
            self.op_kwargs = context

        puls = self.op_kwargs['a'] + self.op_kwargs['b']
        print "a =", self.op_kwargs['a'], ". b=", self.op_kwargs['a']
        return_value = self.main()
        context[self.task_id].xcom_push(key='return_value', value=return_value)
        return puls


# Defining the plugin class
class PlusPlugin(AirflowPlugin):
    name = "plus_plugin"
    operators = [PlusOperator]

在dag中使用案例如下

from airflow.operators.plus_plugin import PlusOperator
plus_task = PlusOperator(task_id='plus_task', provide_context=True, params={'a': 1,'b':2},dag=dag)

一些命令说明

命令 说明
airflow webserver -p 8091 8091启动webserver,通过页面查询不需要可以不启动
airflow scheduler 调度器,必须启动,不然dag没法run起来(使用CeleryExecutor、LocalExecutor时)
airflow run dagid [time] run task instance
airflow backfill [dagid] -s[startTime] -e [endTime] run a backfill over 2 days
run的demo
# run your first task instance
airflow run example_bash_operator runme_0 2018-01-11

# run a backfill over 2 days
airflow backfill example_bash_operator -s 2018-01-10 -e 2018-01-11

基于CeleryExecutor方式的系统架构

使用celery方式的系统架构图(官方推荐使用这种方式,同时支持mesos方式部署)。turing为外部系统,GDags服务帮助拼接成dag,可以忽略。

  • 1.master节点webui管理dags、日志等信息。scheduler负责调度,只支持单节点,多节点启动scheduler可能会挂掉
  • 2.worker负责执行具体dag中的task。这样不同的task可以在不同的环境中执行。

airflow探索篇

基于LocalExecutor方式的系统架构图

另一种启动方式的思考,一个dag分配到1台机器上执行。如果task不复杂同时task环境相同,可以采用这种方式,方便扩容、管理,同时没有master单点问题。

airflow探索篇

基于源码的启动以及二次开发

很多情况airflow是不满足我们需求,就需要自己二次开发,这时候就需要基于源码方式启动。比如日志我们期望通过http的方式提供出来,同其他系统查看。airflow自动的webserver只提供页面查询的方式。

下载源码

github源码地址 : [https://github.com/apache/inc...]
git clone git@github.com:apache/incubator-airflow.git

切换分支

master分支的表初始化有坑,mysql设置的sql校验安全级别过高一直建表不成功。这个坑被整的有点惨。v1-8-stable或者v1-9-stable分支都可以。
git checkout v1-8-stable

安装必要Python包

进入incubator-airflow,python setup.py install (没啥文档说明,又是一个坑。找了半天)

初始化

直接输入airflow initdb(python setup.py install这个命令会将airflow安装进去)

修改配置

进入$AIRFLOE_HOME (默认在~/airflow),修改airflow.cfg,修改mysql配置。可以查看上面推荐的文章以及上面的[使用mysql需要安装]

启动

airflow webserver -p 8085
airflow scheduler

获取日志信息的改造

1.进入incubator-airflow/airflow/www/
2.修改views.py
在 class Airflow(BaseView)中添加下面代码

@expose('/logs')
    @login_required
    @wwwutils.action_logging
    def logs(self):
        BASE_LOG_FOLDER = os.path.expanduser(
            conf.get('core', 'BASE_LOG_FOLDER'))
        dag_id = request.args.get('dag_id')
        task_id = request.args.get('task_id')
        execution_date = request.args.get('execution_date')
        dag = dagbag.get_dag(dag_id)
        log_relative = "{dag_id}/{task_id}/{execution_date}".format(
            **locals())
        loc = os.path.join(BASE_LOG_FOLDER, log_relative)
        loc = loc.format(**locals())
        log = ""
        TI = models.TaskInstance
        session = Session()
        dttm = dateutil.parser.parse(execution_date)
        ti = session.query(TI).filter(
            TI.dag_id == dag_id, TI.task_id == task_id,
            TI.execution_date == dttm).first()
        dttm = dateutil.parser.parse(execution_date)
        form = DateTimeForm(data={'execution_date': dttm})

        if ti:
            host = ti.hostname
            log_loaded = False

            if os.path.exists(loc):
                try:
                    f = open(loc)
                    log += "".join(f.readlines())
                    f.close()
                    log_loaded = True
                except:
                    log = "*** Failed to load local log file: {0}.\n".format(loc)
            else:
                WORKER_LOG_SERVER_PORT = \
                    conf.get('celery', 'WORKER_LOG_SERVER_PORT')
                url = os.path.join(
                    "http://{host}:{WORKER_LOG_SERVER_PORT}/log", log_relative
                ).format(**locals())
                log += "*** Log file isn't local.\n"
                log += "*** Fetching here: {url}\n".format(**locals())
                try:
                    import requests
                    timeout = None  # No timeout
                    try:
                        timeout = conf.getint('webserver', 'log_fetch_timeout_sec')
                    except (AirflowConfigException, ValueError):
                        pass

                    response = requests.get(url, timeout=timeout)
                    response.raise_for_status()
                    log += '\n' + response.text
                    log_loaded = True
                except:
                    log += "*** Failed to fetch log file from worker.\n".format(
                        **locals())

            if not log_loaded:
                # load remote logs
                remote_log_base = conf.get('core', 'REMOTE_BASE_LOG_FOLDER')
                remote_log = os.path.join(remote_log_base, log_relative)
                log += '\n*** Reading remote logs...\n'

                # S3
                if remote_log.startswith('s3:/'):
                    log += log_utils.S3Log().read(remote_log, return_error=True)

                # GCS
                elif remote_log.startswith('gs:/'):
                    log += log_utils.GCSLog().read(remote_log, return_error=True)

                # unsupported
                elif remote_log:
                    log += '*** Unsupported remote log location.'

            session.commit()
            session.close()

        if PY2 and not isinstance(log, unicode):
            log = log.decode('utf-8')

        title = "Log"

        return wwwutils.json_response(log)

3.重启服务,访问url如:

http://localhost:8085/admin/airflow/logs?task_id=run_after_loop&dag_id=example_bash_operator&execution_date=2018-01-11

就可以拿到这个任务在execution_date=2018-01-11的日志

异步任务思考

案例:task通过http请求大数据操作,拆分一些数据,存入一些临时表。
方案:
1.新建一张task实例的状态表如:task_instance_state。
2.扩展一个plugins,如:AsyncHttpOperator。AsyncHttpOperator实现逻辑:

  • 在task_instance_state插入一条running状态记录running。
  • 发送http请求给大数据平台,操作数据。
  • 轮询查询task_instance_state状态是成功、失败、running。如是running则继续轮询,成功、失败操作相应后续操作。

3.提供一个restful api update task_instance_state,供大数据平台回调,修改任务实例状态。

不错的文章推荐

瓜子云的任务调度系统
Get started developing workflows with Apache Airflow
官网地址
生产环境使用可能遇到的坑
初探airflow
焦油坑
系统研究Airbnb开源项目airflow

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Souleigh ✨ Souleigh ✨
4年前
每天学点 JS 编码规范(2):Objects
(给前端大全加星标,提升前端技能)英文:AirBnB,翻译:KakakaHou为什么要在前端大全推送这个系列?前段时间有读者通过我的个人微信,提议能不能推送一些编程规范的文章。我们以前关注过 Airbnb 在GitHub上开源的JS编程规范(现在有10万 star,可见受欢迎程度),但因为篇幅太长,一直没有推送过。现在化整为零,设立这
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Stella981 Stella981
3年前
Apache Flink漏洞复现
简介ApacheFlink是高效和分布式的通用数据处理平台,由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎(简单来说,就是跟spark类似)。Flink具有监控API,可用于查询"正在运行的jobs"和"最近完成的jobs"的状态和统计信息。该监控API被用于Flink自
Easter79 Easter79
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Easter79 Easter79
3年前
StatsD!次世代系统监控的核心
在互联网业务蒸蒸日上的今时今日,系统架构日渐复杂,随着软件产品和工程团队的变革,许多开源的监控工具应运而生,其中有一些相当出名,比如Zabbix、Nagios还有StatsD。也有一些问题被大家不断讨论,例如,监控领域的开源工具Zabbix和Nagios哪个更好?StatsD是否有可能取代Zabbix或Nagios(https://
Stella981 Stella981
3年前
Pre
PAT甲级1119,我先在CSDN上面发布的这篇文章:https://blog.csdn.net/weixin\_44385565/article/details/89737224(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.csdn.net%2Fweixin_443855
Stella981 Stella981
3年前
Android MVP模式
1、MVP模式简介相信大家对MVC都是比较熟悉了:MModel模型、VView视图、CController控制器,MVP作为MVC的演化版本,那么类似的MVP所对应的意义:MModel模型、VView视图、PPresenter表示器。从MVC和MVP两者结合来看,Controlller/Pre
Stella981 Stella981
3年前
Schedulis 0.6.0 版本发布
Schedulis0.6.0版本发布Schedulis0.6.0_Schedulis是一个基于LinkedIn的开源项目Azkaban开发的工作流任务调度系统。该调度系统具备高性能,高可用(去中心化多调度中心和多执行器)和多租户资源隔离等金融级特性;现已被集成到数据应用开发