Airflow 1.10.4版本部署及整合企业微信告警

Airflow 1.10.4版本部署及整合企业微信告警

本次部署的Airflow版本为1.10.4,依赖的Python版本为Python3.6

1.Airflow安装

Python的安装就不多做赘述,网上教程太多了。如果是本地部署测试,可以新启一个python的测试环境virtualenv

#1.安装virtualenv
pip3 install virtualenv 

#2.创建目录
mkdir Myproject
cd Myproject

#3.创建独立运行环境-命名,得到独立第三方包的环境,并且指定解释器是python3
virtualenv --no-site-packages --python=python3  venv

#4.进入虚拟环境,此时进入虚拟环境(venv)Myproject
source venv/bin/activate

#5.安装第三方包,我把需要用到的包都放到配置文件中了(可选),如果安装速度慢可以选择国内的源(可选)
pip install --upgrade -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 

包配置文件

airflow
alembic==1.0.11
amqp==2.5.1
apache-airflow==1.10.4
apispec==2.0.2
asn1crypto==0.24.0
attrs==19.1.0
Babel==2.7.0
bcrypt==3.1.7
billiard==3.6.1.0
cached-property==1.5.1
celery==4.3.0
certifi==2019.6.16
cffi==1.12.3
chardet==3.0.4
Click==7.0
colorama==0.4.1
colorlog==4.0.2
configparser==3.5.3
croniter==0.3.30
cryptography==2.7
defusedxml==0.6.0
dill==0.2.9
docutils==0.15.2
dumb-init==1.2.2
Flask==1.1.1
Flask-Admin==1.5.3
Flask-AppBuilder==1.13.1
Flask-Babel==0.12.2
Flask-Bcrypt==0.7.1
Flask-Caching==1.3.3
Flask-JWT-Extended==3.21.0
Flask-Login==0.4.1
Flask-OpenID==1.2.5
Flask-SQLAlchemy==2.4.0
flask-swagger==0.2.13
Flask-WTF==0.14.2
flower==0.9.3
funcsigs==1.0.0
future==0.16.0
gunicorn==19.9.0
idna==2.8
importlib-metadata==0.19
iso8601==0.1.12
itsdangerous==1.1.0
Jinja2==2.10.1
json-merge-patch==0.2
jsonschema==3.0.2
kombu==4.6.4
lazy-object-proxy==1.4.2
lockfile==0.12.2
Mako==1.1.0
Markdown==2.6.11
MarkupSafe==1.1.1
marshmallow==2.19.5
marshmallow-enum==1.5.1
marshmallow-sqlalchemy==0.17.0
more-itertools==7.2.0
mysqlclient==1.3.14
numpy==1.17.0
ordereddict==1.1
pandas==0.25.1
pendulum==1.4.4
prison==0.1.0
protobuf==3.9.1
psutil==5.6.3
pycparser==2.19
Pygments==2.4.2
PyJWT==1.7.1
pyrsistent==0.15.4
python-daemon==2.1.2
python-dateutil==2.8.0
python-editor==1.0.4
python3-openid==3.1.0
pytz==2019.2
pytzdata==2019.2
PyYAML==5.1.2
requests==2.22.0
setproctitle==1.1.10
six==1.12.0
snakebite==2.11.0
SQLAlchemy==1.3.7
tabulate==0.8.3
tenacity==4.12.0
termcolor==1.1.0
text-unidecode==1.2
thrift==0.11.0
tornado==5.1.1
tzlocal==1.5.1
unicodecsv==0.14.1
urllib3==1.25.3
vine==1.3.0
Werkzeug==0.15.5
WTForms==2.2.1
zipp==0.6.0
zope.deprecation==4.4.0

安装完毕之后就可以启动Airflow了

2.Airflow使用

Airflow要贴合生产使用正常是需要更改配置的,内置的SqLite3并不是特别好用,一般会换成别的数据库,推荐Mysql,原因是简单易用;还有一点就是Executor的修改,生产上一般都是使用CeleryExecutor,也就是分布式任务队列。

配置airflow.cfg(仅列出我做过修改的配置)
[core]
# The home folder for airflow, default is ~/airflow
# airflow的home目录指定
airflow_home = /project/workflow

# airflow的dags目录指定
dags_folder = /project/workflow/dags

# airflow的日志文件目录指定
base_log_folder = /project/workflow/logs

# 时区指定(时区指定,需要多处修改)
default_timezone = Asia/Shanghai

# 修改executor的模式为CeleryExecutor
executor = CeleryExecutor

# 存储数据的数据库修改为mysql
sql_alchemy_conn = mysql://root:@localhost/airflow

# 不加载默认的example dag
load_examples = False

# airflow插件位置修改
plugins_folder = /project/workflow/plugins

# 账户安全性配置,只通过账号密码控制(可选)
[webserver]
authenticate = True
auth_backend = airflow.contrib.auth.backends.password_auth

启用密码身份验证后,需要先创建初始用户,然后其他账户才能登陆,进入 python 命令行,执行以下命名, 或者通过运行python脚本,设置 airflow 的用户名和密码,若提示缺失包,直接通过 pip 安装即可,用户信息会存入 users 表中

import airflow
from airflow import models, settings
from airflow.contrib.auth.backends.password_auth import PasswordUser

user = PasswordUser(models.User())
user.username = 'new_user_name'
user.email = 'new_user_email@example.com'
user.password = 'set_the_password'
session = settings.Session()
session.add(user)
session.commit()
session.close()

通过账号密码+角色权限控制来登陆

[webserver]
security = Flask AppBuilder
secure_mode = True
rbac=True
ps: 和第一种方式不共存,必须删除 authenticate 和 auth_backend 的配置

添加配置之后,需要重建数据库表:
airflow resetdb

这种情况下,创建用户必须使用命令行 airflow create_user
例如:
airflow create_user --lastname user --firstname admin --username admin --email admin_user@mail.com --role Admin --password admin123
airflow create_user --lastname user --firstname view --username view --email view_user@mail.com --role Viewer --password view123

此时, admin 角色的用户 UI 界面会出现 Security 的 Tab, 就可以愉快的通过 UI 界面来添加/修改用户了

时区的修改,界面右上角的时间目前没有配置修改,只能改源码实现

需要在 dag 的 arg 中 start_date 指定时区,这样定时任务就会按照上海时间来执行

import pendulum

from datetime import datetime
from airflow.models import DAG
from airflow.operators.bash_operator import BashOperator
from airflow.operators.dummy_operator import DummyOperator

local_tz = pendulum.timezone("Asia/Shanghai")

args = {
    'owner': 'Airflow',
    'start_date': datetime(2019, 1, 1, tzinfo=local_tz),
}

dag = DAG(
    dag_id='bash_test',
    default_args=args,
    schedule_interval='* * * * *',
)

启动Airflow

# 主服务启动,webserver 是一个守护进程,它接受 HTTP 请求,允许你通过 Python Flask Web 应用程序与 airflow 进行交互。
Airflow webserver
# 调度器启动,scheduler 是一个守护进程,它周期性地轮询任务的调度计划,以确定是否触发任务执行。
Airflow scheduler
# worker启动,worker 是一个守护进程,它启动 1 个或多个 Celery 的任务队列,负责执行具体 的 DAG 任务。
Airflow worker

然后皆可以登录web界面操作Airflow了。

3.一个简单的DAG编写

# -*- coding: utf-8 -*-
#用于初始化DAG对象
from airflow import DAG
#众多operators的一种,用于执行bash命令
from airflow.operators.bash_operator import BashOperator
#日期时间相关的包
from datetime import timedelta, datetime

# DAG时区指定
local_tz = pendulum.timezone("Asia/Shanghai")

# ------------------------------------------------------------------------------
# 当创建DAG对象或者task时,可以明确地传递一系列的参数来描述它。可以定义一个字典来实现。default_args作用于该DAG下的所有task
default_args = {'owner': 'admin',
                'depends_on_past': False,#此DAG开始执行的日期
                'start_date': datetime(2019, 8, 25, tzinfo=local_tz),#此DAG开始执行的日期
                'email': 'xxxxxxx@qq.com',#任务失败、重试时用于接受邮件
                'email_on_failure': True,#任务失败是是否接收邮件
                'email_on_retry': True,#任务重试时是否接收邮件
                'retries': 1,#重试次数
                'retry_delay': timedelta(minutes=1),#重试间隔
                # 'priority_weight': 10,
                # 'end_date': datetime(2016, 1, 1),
                }

# -------------------------------------------------------------------------------
# 初始化DAG对象
dag = DAG(
    'test_dag',#dag_id唯一标示
    default_args=default_args,
    description='my first DAG',
		schedule_interval='30 2 * * *'#DAG schedule间隔,支持cron格式(0 7 * * *)
  
# -------------------------------------------------------------------------------
# first operator
t1 = BashOperator(
    task_id='date_task',#task_id唯一标示
    bash_command='date ',#具体的bash命令
    dag=dag)

t2 = BashOperator(
    task_id='sleep',
    bash_command='sleep 5',
    retries=3, #task里传递的参数值优先于dag(此task的重试次数是3而不是1)
    dag=dag)
  
t1 >> t2 #等价于 t1.set_downstream(t2),t2 << t1,t2.set_upstream(t1)
#如果有多个task,task列表也可以设置依赖关系
#t1.set_downstream([t2, t3])
#t1 >> [t2, t3]
#[t2, t3] << t1

这基本是一个DAG对象的骨架,DAG()除了上述列出之外还有很多的参数可以设置,来提高task的运行效率。operators同理,除了最基本的BashOperator还有诸如PythonOperator,mysql_to_hiveOperator等等,每种operator都有相应的功能,可以根据业务场景任意挑选

python ~/airflow/dags/test.py (如果脚本不报错表示没有语法错误且你的airflow运行环境正常)
airflow list_dags (输出显示可用的dags)
airflow list_tasks tutorial (输出显示tutorial dag下所有的task)
airflow list_tasks tutorial --tree (以树形结构输出显示tutorial dag下所有的task)
airflow test tutorial print_date 20159-09-01 (测试该task是否可以正确运行)

4.调度与触发规则(Scheduling & Triggers)

airflow的scheduler监控所有的dag和task,根据其依赖关系触发执行。在后台, scheduler启动了一个linux子进程来监控DAG_FOLDER目录,收集dag的python文件(.py)和检测可用的task是否可以被触发执行。airflow scheduler被设计为持续性的后台服务来运行,可以用airflow scheduler命令来启动,其将读取airflow.cfg配置文件。

airflow的调度模式是在当前的调度周期触发上一个调度周期,举例说明:

对于天调度。一个dag的schedule_interval设置为0 7 * * ,现在是2019-01-02 08:00:00,那么scheduler执行的最后一个task实例是2019-01-01T07:00:00*,而不是2019-01-02T07:00:00
对于周调度。举例说明:start_date为2019-02-01,schedule_interval为0 7 * * 1,now是2019-01-11 07:00:00,那么scheduler执行的最后一个task实例是2019-01-04T07:00:00,因为从start_date到2019-01-11期间,只有2019-02-04到2019-02-10是一个完整的周期。如果start_date和now不变,schedule_interval改为0 7 * * 2,则没有实例可执行

5.外部触发

airflow dag除了scheduler在后天自动执行外,还可以在命令行airflow trigger_dag和webUI手动触发执行。命令行执行时需要传入run_id。

airflow关于并行度在airflow.cfg里有4个配置项:

parallelism: 指整个Airflow在任何一刻能同时运行的Task Instance的数量,这个数量跟DAG无关
max_active_runs_per_dag: 指同一个Dag能被同时激活的Dag Run的数量
dag_concurrency: 指同一个Dag Run中能同时运行的Task Instance的个数
non_pooled_task_slot_count: 指默认的Pool能同时运行的Task Instance的数量,如果你的Task没有指定Pool选项,那么这个Task就是属于这个默认的Pool的

6.插件(Plugin)

这次主要介绍的是Airflow新版本的钉钉机器人插件改企业微信机器人,Airflow刚开始的告警只有内置的邮件,现在很多公司办公都选择企业微信或者是钉钉,能用他们进行告警显得尤为重要,1.10版本之前的Airflow要实现钉钉或者企微告警只能通过改造源码,模仿邮件告警写新的告警系统。

6.1 新版Airflow的钉钉插件

官网链接:https://airflow.apache.org/howto/operator/dingding.html

使用起来很简单,官方文档很详细,有个坑说一下。

这边文档说吧dingding_default的连接改一下,并不是改下边的配置,而是要去改web界面的connection,不然运行的时候会报数据库找不到配置信息。

6.2 钉钉插件改企业微信

直接贴代码

WechatHook.py文件

import json

import requests

from airflow import AirflowException
from airflow.hooks.http_hook import HttpHook


class WechatHook(HttpHook):

    def __init__(self,
                 wechat_conn_id='wechat_default',
                 message_type='text',
                 message=None,
                 at_mobiles=None,
                 at_all=False,
                 *args,
                 **kwargs
                 ):
        super(WechatHook, self).__init__(http_conn_id=wechat_conn_id, *args, **kwargs)
        self.message_type = message_type
        self.message = message
        self.at_mobiles = at_mobiles
        self.at_all = at_all

    def _get_endpoint(self):
        """
        Get WeChat endpoint for sending message.
        """
        conn = self.get_connection(self.http_conn_id)
        token = conn.password
        if not token:
            raise AirflowException('WeChat token is requests but get nothing, '
                                   'check you conn_id configuration.')
        return 'cgi-bin/webhook/send?key={}'.format(token)

    def _build_message(self):
        """
        Build different type of WeChat message
        As most commonly used type, text message just need post message content
        rather than a dict like ``{'content': 'message'}``
        """
        if self.message_type in ['text', 'markdown']:
            data = {
                'msgtype': self.message_type,
                self.message_type: {
                    'content': self.message
                } if self.message_type == 'text' else self.message,
                'at': {
                    'atMobiles': self.at_mobiles,
                    'isAtAll': self.at_all
                }
            }
        else:
            data = {
                'msgtype': self.message_type,
                self.message_type: self.message
            }
        return json.dumps(data)

    def get_conn(self, headers=None):
        """
        Overwrite HttpHook get_conn because just need base_url and headers and
        not don't need generic params

        :param headers: additional headers to be passed through as a dictionary
        :type headers: dict
        """
        conn = self.get_connection(self.http_conn_id)
        self.base_url = conn.host if conn.host else 'https://qyapi.weixin.qq.com'
        session = requests.Session()
        if headers:
            session.headers.update(headers)
        return session

    def send(self):
        """
        Send WeChat message
        """
        support_type = ['text', 'link', 'markdown', 'actionCard', 'feedCard']
        if self.message_type not in support_type:
            raise ValueError('WeChatWebhookHook only support {} '
                             'so far, but receive {}'.format(support_type, self.message_type))

        data = self._build_message()
        self.log.info('Sending WeChat type %s message %s', self.message_type, data)
        resp = self.run(endpoint=self._get_endpoint(),
                        data=data,
                        headers={'Content-Type': 'application/json'})

        # WeChat success send message will with errcode equal to 0
        if int(resp.json().get('errcode')) != 0:
            raise AirflowException('Send WeChat message failed, receive error '
                                   'message %s', resp.text)
        self.log.info('Success Send WeChat message')

WechatOperator.py文件

from airflow.operators.bash_operator import BaseOperator
from airflow.utils.decorators import apply_defaults

from wechat_hook import WechatHook


class WechatOperator(BaseOperator):

    template_fields = ('message',)

    ui_color = '#4ea4d4'  # Wechat icon color

    @apply_defaults
    def __init__(self,
                 wechat_conn_id='wechat_default',
                 message_type='text',
                 message=None,
                 at_mobiles=None,
                 at_all=False,
                 *args,
                 **kwargs):
        super(WechatOperator, self).__init__(*args, **kwargs)
        self.wechat_conn_id = wechat_conn_id
        self.message_type = message_type
        self.message = message
        self.at_mobiles = at_mobiles
        self.at_all = at_all

    def execute(self, context):
        self.log.info('Sending WeChat message.')
        hook = WechatHook(
            self.wechat_conn_id,
            self.message_type,
            self.message,
            self.at_mobiles,
            self.at_all
        )
        hook.send()

两个文件直接放到plugins目录下即可

6.3 使用

operator可以通过编写函数通过回调来进行操作,然后将该函数传递给on_success_callback、on_failure_callback或on_retry_callback。简单来说就是一个task执行之后,可以通过不同的状态执行回调函数发送消息到企业微信,回调的配置可以配置在arg中,也可以配置在task中。

# -*- coding: utf-8 -*-
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import timedelta, datetime

from wechat_operator import WechatOperator


def failure_callback(context):
    message = 'AIRFLOW TASK FAILURE TIPS:\n' \
              'DAG:    {}\n' \
              'TASKS:  {}\n' \
              'Reason: {}\n' \
        .format(context['task_instance'].dag_id,
                context['task_instance'].task_id,
                context['exception'])
    return WechatOperator(
        task_id='failure_callback',
        dingding_conn_id='wechat_default',
        message_type='text',
        message=message,
        at_all=True,
    ).execute(context)


def success_callback(context):
    message = 'AIRFLOW TASK SUCCESS TIPS:\n' \
              'DAG:    {}\n' \
              'TASKS:  {}\n' \
        .format(context['task_instance'].dag_id,
                context['task_instance'].task_id)
    return WechatOperator(
        task_id='success_callback',
        dingding_conn_id='wechat_default',
        message_type='text',
        message=message,
        at_all=True,
    ).execute(context)


def retry_callback(context):
    message = 'AIRFLOW TASK RETRY TIPS:\n' \
              'DAG:    {}\n' \
              'TASKS:  {}\n' \
              'Reason: {}\n' \
        .format(context['task_instance'].dag_id,
                context['task_instance'].task_id,
                context['exception'])
    return WechatOperator(
        task_id='retry_callback',
        dingding_conn_id='wechat_default',
        message_type='text',
        message=message,
        at_all=True,
    ).execute(context)


# -------------------------------------------------------------------------------
# these args will get passed on to each operator
# you can override them on a per-task basis during operator initialization
default_args = {'owner': 'admin',
                'depends_on_past': False,
                'start_date': datetime(2019, 8, 25),
                'email': 'xxxxxx@qq.com',
                'email_on_failure': False,
                'email_on_retry': False,
                'retries': 1,
                'retry_delay': timedelta(minutes=1),
                'on_failure_callback': failure_callback,
                'on_success_callback': success_callback,
                'on_retry_callback': retry_callback
                }

# -------------------------------------------------------------------------------
# dag
dag = DAG(
    'callback_test_dag',
    default_args=default_args,
    description='my first DAG',
    schedule_interval=timedelta(days=1))
# -------------------------------------------------------------------------------
# first operator
date_operator = BashOperator(
    task_id='date_task',
    bash_command='abc ',
    dag=dag)

date_operator
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Airflow部署可以采用多节点(集群)部署的方式,这种方式适用于对稳定性要求较高的场景。在多节点部署中,可以将Airflow的守护进程分布在多台机器上运行,以实现高可用性。[1] 具体的部署步骤如下: 1. 确保已经安装了CentOS 7.2操作系统,并满足以下版本要求:Airflow 1.9、Python 2.7.5、Celery 4.3.0、Redis 4.1.0和MySQL 5.7。 2. 执行以下命令安装Airflow:`pip install apache-airflow`. 3. 执行以下命令初始化Airflow的数据库:`airflow db init`. 4. 启动Web服务:`airflow webserver --port 8080 -D`. 5. 启动调度器:`airflow scheduler -D`. 如果需要重新启动Airflow,可以执行以下步骤: 1. 杀死所有正在运行的Airflow进程:`killall airflow`. 2. 运行以下命令初始化数据库:`airflow db upgrade`. 3. 启动Web服务:`airflow webserver --port 8080 -D`. 4. 启动调度器:`airflow scheduler -D`. 对于MySQL数据库的部署,您可以使用以下步骤: 1. 登录MySQL数据库,执行以下命令创建Airflow数据库:`create database airflow;`. 2. 创建Airflow用户并设置密码:`create user 'airflow'@'%' identified by '123456';`. 3. 授予Airflow用户对所有数据库的所有权限:`grant all privileges on *.* to 'airflow'@'%';`. 4. 刷新权限:`FLUSH PRIVILEGES;`. 请注意,以上步骤仅为Airflow部署和数据库配置的基本步骤,具体的部署配置可能会因环境和需求而有所不同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [airflow初探(一)-- 安装和部署](https://blog.csdn.net/hpugym/article/details/89476480)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [airflow 2.2.3部署(2022年最新版+史上最完整版)](https://blog.csdn.net/small_fash/article/details/123254349)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值