Airflow 是什麼
Airflow 是 Airbnb 開發的用於工作流管理的開源項目,自帶 web UI 和調度。現在 Apache 下做孵化,地址是 https://github.com/apache/airflow
airflow
Airflow 解決什麼問題
Airflow 主要解決的問題可以參考 Airbnb 官方的博客: airflow-a-workflow-management-platform,簡單來說就是管理和調度各種離線定時 Job ,可以替代 crontab。
當 cron job 規模達到數百上千時,其對人的要求將會非常高的,如果你的團隊經歷過這樣的事情,應該能體會其中痛苦,所以使用類似 airflow 這樣的工具代替 cron 來做定時任務將會極大提高工作效率。
開始使用 airflow 之前需要知道和準備的
Airflow 在 pip 上已經更名爲 apache-airflow
,下載最新版請使用後者 pip install apache-airflow
。
Airflow 1.8 版本依賴的是 MySQL 5.6 以上,5.7 以下報 1071, u'Specified key was too long; max key length is 767 bytes
,如果你使用 MySQL 作爲你的 airflow backend 請升級你的 MySQL 到最新版。
MySQL 5.6 升級到 5.7 在使用 airflow 時會報 1146, u"Table 'performance_schema.session_variables' doesn't exist"
,執行 mysql_upgrade -u root -p --force
解決。
Airflow 的 mysql driver 使用的是 mysqlclient mysql://root:@127.0.0.1/sqlalchemy_lab?charset=utf8
,如果使用其他 driver 將報 syntax error。
基礎概念
Airflow 中最基本的兩個概念是:DAG 和 task。DAG 的全稱是 Directed Acyclic Graph 是所有你想執行的任務的集合,在這個集合中你定義了他們的依賴關係,一個 DAG 是指一個 DAG object,一個 DAG object 可以在 Python 腳本中配置完成。
比如一個簡單的的 DAG 包含三個 task:A、B、C,A 執行成功之後 B 才能執行,C 不依賴 A 和 B 即可執行。在這個簡單的 DAG 中 A B C 可以是任何你想要執行的任務。
DAG 的定義使用 Python 完成的,其實就是一個 Python 文件,存放在 DAG 目錄,Airflow 會動態的從這個目錄構建 DAG object,每個 DAG object 代表了一個 workflow,每個 workflow 都可以包含任意個 task。
安裝和使用
Airflow 是基於 Python 構建的,可以很容易用 pip 安裝使用,pip install apache-airflow
,默認情況下 airflow 會在 ~/airflow
目錄存放相關配置。
安裝過程中可能會出現報錯的情況,此時我們查看錯誤log,發現是讓我們設置環境變量,接下來我們需要做的就是
vim ~/.bash_profile
export SLUGIFY_USES_TEXT_UNIDECODE=yes
source ~/.bash_profile
Airflow 提供了一些列命令來完成 airflow 的初始化工作來和它的正確使用。
# 在 airflow 目錄初始化數據庫和 airflow 配置
airflow initdb
# 啓動 airflow web
airflow webserver
# 開始調度
airflow scheduler複製代碼
更詳細的信息請參考文檔 airflow.incubator.apache.org/
示例管道定義
以下是基本管道定義的示例。如果這看起來很複雜,請不要擔心,下面將逐行說明。
"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/airflow/blob/master/airflow/example_dags/tutorial.py
"""
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2015, 6, 1),
'email': ['[email protected]'],
'email_on_failure': False,
'email_on_retry': False,
'retries': 1,
'retry_delay': timedelta(minutes=5),
# 'queue': 'bash_queue',
# 'pool': 'backfill',
# 'priority_weight': 10,
# 'end_date': datetime(2016, 1, 1),
}
dag = DAG('tutorial', default_args=default_args, schedule_interval=timedelta(days=1))
# t1, t2 and t3 are examples of tasks created by instantiating operators
t1 = BashOperator(
task_id='print_date',
bash_command='date',
dag=dag)
t2 = BashOperator(
task_id='sleep',
bash_command='sleep 5',
retries=3,
dag=dag)
templated_command = """
{% for i in range(5) %}
echo "{{ ds }}"
echo "{{ macros.ds_add(ds, 7)}}"
echo "{{ params.my_param }}"
{% endfor %}
"""
t3 = BashOperator(
task_id='templated',
bash_command=templated_command,
params={'my_param': 'Parameter I passed in'},
dag=dag)
t2.set_upstream(t1)
t3.set_upstream(t1)
這是一個DAG定義文件
包圍你的一件事(對於每個人來說可能不是很直觀)是這個Airflow Python腳本實際上只是一個配置文件,將DAG的結構指定爲代碼。此處定義的實際任務將在與此腳本的上下文不同的上下文中運行。不同的任務在不同的時間點對不同的工作程序運行,這意味着此腳本不能用於在任務之間交叉通信。請注意,爲此,我們有一個更高級的功能XCom
。
人們有時會將DAG定義文件視爲可以進行實際數據處理的地方 - 事實並非如此!該腳本的目的是定義DAG對象。它需要快速評估(秒,而不是分鐘),因爲調度程序將定期執行它以反映更改(如果有的話)。
導入模塊
Airflow管道只是一個Python腳本,恰好定義了Airflow DAG對象。讓我們首先導入我們需要的庫。
# The DAG object; we'll need this to instantiate a DAG
from airflow import DAG
# Operators; we need this to operate!
from airflow.operators.bash_operator import BashOperator
默認參數
我們即將創建一個DAG和一些任務,我們可以選擇顯式地將一組參數傳遞給每個任務的構造函數(這將變得多餘),或者(更好!)我們可以定義一個默認參數的字典,我們可以可以在創建任務時使用。
from datetime import datetime, timedelta
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2015, 6, 1),
'email': ['[email protected]'],
'email_on_failure': False,
'email_on_retry': False,
'retries': 1,
'retry_delay': timedelta(minutes=5),
# 'queue': 'bash_queue',
# 'pool': 'backfill',
# 'priority_weight': 10,
# 'end_date': datetime(2016, 1, 1),
}
有關BaseOperator參數及其功能的更多信息,請參閱airflow.models.BaseOperator
文檔。
另外,請注意,您可以輕鬆定義可用於不同目的的不同參數集。一個例子是在生產和開發環境之間進行不同的設置。
實例化
我們需要一個DAG對象來嵌入我們的任務。這裏我們傳遞一個定義的字符串dag_id
,它作爲DAG的唯一標識符。我們還傳遞我們剛剛定義的默認參數字典,並schedule_interval
爲DAG 定義1天。
dag = DAG(
'tutorial', default_args=default_args, schedule_interval=timedelta(days=1)
)
任務
在實例化操作員對象時生成任務。從運算符實例化的對象稱爲構造函數。第一個參數 task_id
充當任務的唯一標識符。
t1 = BashOperator(
task_id='print_date',
bash_command='date',
dag=dag)
t2 = BashOperator(
task_id='sleep',
bash_command='sleep 5',
retries=3,
dag=dag)
請注意我們如何將從BaseOperator繼承的bash_command所有運算符(retries)共同的運算符特定參數()和通用參數傳遞給運算符的構造函數。這比爲每個構造函數調用傳遞每個參數更簡單。另外,請注意在第二個任務中我們用retries參數覆蓋參數3
任務的優先規則如下:
- 明確傳遞參數
default_args
字典中存在的值- 運算符的默認值(如果存在)
任務必須包括或繼承的參數task_id
和owner
,否則氣流將引發異常。
使用Jinja模板
Airflow利用Jinja Templating的強大功能, 爲管道作者提供一組內置參數和宏。Airflow還爲管道作者提供了定義自己的參數,宏和模板的鉤子。
本教程幾乎沒有涉及在Airflow中使用模板進行操作的表面,但本節的目的是讓您瞭解此功能的存在,讓您熟悉雙花括號,並指向最常見的模板變量:( 今天的“日期戳”)。{{ ds }}
templated_command = """
{% for i in range(5) %}
echo "{{ ds }}"
echo "{{ macros.ds_add(ds, 7) }}"
echo "{{ params.my_param }}"
{% endfor %}
"""
t3 = BashOperator(
task_id='templated',
bash_command=templated_command,
params={'my_param': 'Parameter I passed in'},
dag=dag)
請注意,templated_command
包含塊中的代碼邏輯,引用參數,如調用函數 ,並引用用戶定義的參數。{% %}
{{ ds }}
{{ macros.ds_add(ds, 7)}}
{{ params.my_param }}
該params
鉤BaseOperator
允許你傳遞的參數和/或對象字典來你的模板。請花點時間瞭解參數如何my_param
通過模板。
文件也可以傳遞給bash_command
參數,例如 bash_command='templated_command.sh'
,文件位置相對於包含管道文件的目錄(tutorial.py
在本例中)。這可能是出於許多原因,例如分離腳本的邏輯和管道代碼,允許在用不同語言編寫的文件中正確的代碼突出顯示,以及構造管道的一般靈活性。也可以將您定義template_searchpath
爲指向DAG構造函數調用中的任何文件夾位置。
使用相同的DAG構造函數調用,可以定義 user_defined_macros
哪些允許您指定自己的變量。例如,傳遞dict(foo='bar')
給此參數允許您在模板中使用。此外,指定 允許您註冊自己的過濾器。例如,傳遞給此參數允許您在模板中使用。有關自定義過濾器的更多信息,請查看 Jinja文檔{{ foo }}
user_defined_filters
dict(hello=lambda name: 'Hello %s' % name)
{{ 'world' | hello }}
有關可以在模板中引用的變量和宏的更多信息,請務必閱讀宏部分
設置依賴關係
我們有任務t1,t2和t3不相互依賴。以下是一些可以定義它們之間依賴關係的方法:
t1.set_downstream(t2)
# This means that t2 will depend on t1
# running successfully to run.
# It is equivalent to:
t2.set_upstream(t1)
# The bit shift operator can also be
# used to chain operations:
t1 >> t2
# And the upstream dependency with the
# bit shift operator:
t2 << t1
# Chaining multiple dependencies becomes
# concise with the bit shift operator:
t1 >> t2 >> t3
# A list of tasks can also be set as
# dependencies. These operations
# all have the same effect:
t1.set_downstream([t2, t3])
t1 >> [t2, t3]
[t2, t3] << t1
請注意,在執行腳本時,Airflow會在DAG中找到循環或多次引用依賴項時引發異常。
回顧
好吧,所以我們有一個非常基本的DAG。此時,您的代碼應如下所示:
"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/airflow/blob/master/airflow/example_dags/tutorial.py
"""
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2015, 6, 1),
'email': ['[email protected]'],
'email_on_failure': False,
'email_on_retry': False,
'retries': 1,
'retry_delay': timedelta(minutes=5),
# 'queue': 'bash_queue',
# 'pool': 'backfill',
# 'priority_weight': 10,
# 'end_date': datetime(2016, 1, 1),
}
dag = DAG(
'tutorial', default_args=default_args, schedule_interval=timedelta(days=1))
# t1, t2 and t3 are examples of tasks created by instantiating operators
t1 = BashOperator(
task_id='print_date',
bash_command='date',
dag=dag)
t2 = BashOperator(
task_id='sleep',
bash_command='sleep 5',
retries=3,
dag=dag)
templated_command = """
{% for i in range(5) %}
echo "{{ ds }}"
echo "{{ macros.ds_add(ds, 7)}}"
echo "{{ params.my_param }}"
{% endfor %}
"""
t3 = BashOperator(
task_id='templated',
bash_command=templated_command,
params={'my_param': 'Parameter I passed in'},
dag=dag)
t2.set_upstream(t1)
t3.set_upstream(t1)
測試
運行腳本
是時候進行一些測試了。首先讓我們確保管道解析。假設我們正在保存上一步中 tutorial.py
引用的DAGs文件夾中的代碼airflow.cfg
。DAG的默認位置是~/airflow/dags
。
python ~/airflow/dags/tutorial.py
如果腳本沒有引發異常,則意味着您沒有做任何可怕的錯誤,並且您的Airflow環境有點健全。
命令行元數據驗證
讓我們運行一些命令來進一步驗證這個腳本。
# print the list of active DAGs
airflow list_dags
# prints the list of tasks the "tutorial" dag_id
airflow list_tasks tutorial
# prints the hierarchy of tasks in the tutorial DAG
airflow list_tasks tutorial --tree
測試
讓我們通過在特定日期運行實際任務實例來進行測試。在此上下文中指定的日期是a execution_date
,它模擬在特定日期+時間運行任務或dag的調度程序:
# command layout: command subcommand dag_id task_id date
# testing print_date
airflow test tutorial print_date 2015-06-01
# testing sleep
airflow test tutorial sleep 2015-06-01
現在還記得我們之前用模板做過的事嗎?通過運行此命令,瞭解如何呈現和執行此模板:
# testing templated
airflow test tutorial templated 2015-06-01
這應該導致顯示詳細的事件日誌並最終運行bash命令並打印結果。
請注意,該命令在本地運行任務實例,將其日誌輸出到stdout(在屏幕上),不依賴於依賴項,並且不向數據庫傳達狀態(運行,成功,失敗,...)。它只允許測試單個任務實例。airflow test
回填
一切看起來都運行良好所以讓我們運行回填。 backfill
將尊重您的依賴項,將日誌發送到文件並與數據庫通信以記錄狀態。如果您有網絡服務器,您將能夠跟蹤進度。如果您有興趣在回填過程中直觀地跟蹤進度,則將啓動Web服務器。airflow webserver
請注意,如果使用depends_on_past=True
,則單個任務實例將取決於前面任務實例的成功,除了指定了自身的start_date,此依賴關係將被忽略。
此上下文中的日期範圍是a start_date
和可選的a end_date
,用於使用此dag中的任務實例填充運行計劃。
# optional, start a web server in debug mode in the background
# airflow webserver --debug &
# start your backfill on a date range
airflow backfill tutorial -s 2015-06-01 -e 2015-06-07