Apache Airflow 1.10+引入了对支持DST的timezones的本机支持。
这使我认为(也许是错误的)应该可以在相同的Airflow调度程序上创建2个DAG,如下所示:
Pacific/Auckland
的06:00开始America/New_York
的21:00开始无需引入在需要的开始时间之前“睡眠”的任务。该文档明确排除了cron计划程序以进行DST感知的计划,但仅说明了如何设置DAG在该时区(默认为午夜)每天运行。
关于此主题的先前问题仅考虑使用cron scheduler或基于pre-1.10 airflow的问题,而implementation
configuration works并未引入对DST感知时区的本机支持。
在“ airflow.cfg”中,我将default_timezone
更新为系统时区。然后,我尝试像这样安排DAG:
DAG('NZ_SOD',
description='New Zealand Start of Day',
start_date=datetime(2018, 12, 11, 06, 00, tzinfo=pendulum.timezone('Pacific/Auckland')),
catchup=False)
并且:
DAG('NAM_EOD',
description='North Americas End of Day',
start_date=datetime(2018, 12, 11, 21, 00, tzinfo=pendulum.timezone('America/New_York')),
catchup=False)
但是似乎在Apache Airflow中没有明确考虑传递给start_date
的datetime对象的“ Time”部分,并且会产生意外的行为。
Airflow是否有任何内置选项可以产生所需的行为,或者我正在尝试使用错误的工具来完成工作?
答案 0 :(得分:3)
答案是肯定的,cron计划支持在DST感知时区中运行DAG。
但是有很多警告,所以我不得不假设Airflow的维护者没有将此作为支持的用例。首先,在撰写本文时,documentation在以下情况下明确错误:
日程安排
如果您设置了cron时间表,Airflow会假设您始终希望完全在同一时间运行。然后它将忽略夏令时。因此,如果您有一个时间表说每天在间隔08:00 GMT + 1时运行,则无论是否设置了夏时制,它总是在间隔08:00 GMT + 1时运行。
我已经编写了这个有点怪异的代码,可以让您了解时间表如何运行,而无需运行Airflow实例(请注意,您已经安装了Penulum 1.x,如果运行则使用正确的documentation或编辑此代码):
import pendulum
from airflow import DAG
from datetime import timedelta
# Set-up DAG
test_dag = DAG(
dag_id='foo',
start_date=pendulum.datetime(year=2019, month=4, day=4, tz='Pacific/Auckland'),
schedule_interval='00 03 * * *',
catchup=False
)
# Check initial schedule
execution_date = test_dag.start_date
for _ in range(7):
next_execution_date = test_dag.following_schedule(execution_date)
if next_execution_date <= execution_date:
execution_date = test_dag.following_schedule(execution_date + timedelta(hours=2))
else:
execution_date = next_execution_date
print('Execution Date:', execution_date)
这为我们提供了7天的新西兰DST体验期:
Execution Date: 2019-04-03 14:00:00+00:00
Execution Date: 2019-04-04 14:00:00+00:00
Execution Date: 2019-04-05 14:00:00+00:00
Execution Date: 2019-04-06 14:00:00+00:00
Execution Date: 2019-04-07 15:00:00+00:00
Execution Date: 2019-04-08 15:00:00+00:00
Execution Date: 2019-04-09 15:00:00+00:00
正如我们看到的那样,使用cron计划会观察到DST,此外,如果您编辑我的代码以删除cron计划,则会看到未观察到DST。
但是要注意,即使cron时间表遵守夏令时,您仍然可能会有1天的错误,并且在夏令时更改的那天,因为Airflow提供的是上一个日期而不是当前日期(例如,日历中的星期日)但在Airflow中,执行日期为星期六)。在我看来,这不是follow_schedule
逻辑所解释的。
最后,如@dlamblin指出的那样,如果DAG的本地执行日期与UTC不同,则通过模板字符串或provide_context=True
为Python可调用对象提供Airflow提供给作业的变量将是错误的执行日期。可以在使用self.execution_date
的{{3}}中观察到这一点,而无需将其修改为本地时间。而且我们可以在TaskInstance.get_template_context中看到self.execution_date
已转换为UTC。
我处理此问题的方法是通过执行@dlamblin建议并使用Pendulum的local_cal_date
方法来派生一个我称为convert
的变量。编辑此代码以满足您的特定需求(我实际上在所有Python可调用对象的包装中使用了它,以便它们都收到local_cal_date
):
import datetime
def foo(*args, dag, execution_date, **kwargs):
# Derive local execution datetime from dag and execution_date that
# airflow passes to python callables where provide_context is set to True
airflow_timezone = dag.timezone
local_execution_datetime = airflow_timezone.convert(execution_date)
# I then add 1 day to make it the calendar day
# and not the execution date which Airflow provides
local_cal_datetime = local_execution_datetime + datetime.timedelta(days=1)
更新:对于模板字符串,我发现最好的方法是创建自定义运算符,该自定义运算符在呈现模板之前将自定义变量注入到上下文中。我在使用自定义宏时发现的问题是它们TaskInstance.__init__,这意味着您必须做很多额外的工作才能以有用的方式呈现它们。因此,在自定义运算符模块中,我类似于以下代码:
# Standard Library
import datetime
# Third Party Libraries
import airflow.operators.email_operator
import airflow.operators.python_operator
import airflow.operators.bash_operator
class CustomTemplateVarsMixin:
def render_template(self, attr, content, context):
# Do Calculations
airflow_execution_datetime = context['execution_date']
airflow_timezone = context['dag'].timezone
local_execution_datetime = airflow_timezone.convert(airflow_execution_datetime)
local_cal_datetime = local_execution_datetime + datetime.timedelta(days=1)
# Add to contexts
context['local_cal_datetime'] = local_cal_datetime
# Run normal Method
return super().render_template(self, attr, content, context)
class BashOperator(CustomTemplateVarsMixin, airflow.operators.bash_operator.BashOperator):
pass
class EmailOperator(CustomTemplateVarsMixin, airflow.operators.email_operator.EmailOperator):
pass
class PythonOperator(CustomTemplateVarsMixin, airflow.operators.python_operator.PythonOperator):
pass
class BranchPythonOperator(CustomTemplateVarsMixin, airflow.operators.python_operator.BranchPythonOperator):
pass
答案 1 :(得分:2)
前几尼特:
start_date=pendulum.datetime(2018, 12, 11, 6, 0, tz='Pacific/Auckland')
,是的,Airflow中的时区有些混乱。 The docs说,计划时间表总是在该时区的偏移量中。这并不是应该的,因为偏移量是变化的。假设您像这样设置默认配置时区:
[core]
default_timezone = America/New_York
使用start_date
,例如:
start_date = datetime(2018, 12, 11, 6, 0),
您获得的offset
的UTC为-18000
或-5h。
start_date = datetime(2018, 4, 11, 6, 0),
您获得的offset
的UTC为-14400
或-4h。
第二个要点的偏移量为46800
或13h,而奥克兰的四月份为43200
或12h。如果我没记错的话,这些将应用于DAG的schedule_interval
。
文档似乎在说您的schedule_interval
crontab字符串将以相同的偏移量进行永久解释。因此,如果0 5 * * *
将在12月份在纽约开始运行,则将在凌晨5或6时运行;如果在4月在纽约开始,则将运行在5或4凌晨。嗯我认为是对的。我对此也感到困惑。
通过将默认设置保留为utc不能避免这种情况。不,如果您使用start_date
来显示和选择具有utc偏移的区域,则不会。
现在……第二期,一天中的时间。开始日期曾经是最早的有效开始时间间隔。一天中的某个时间很棒,但是时间表默认为timedelta(days=1)
。我认为它是@daily
,也意味着0 0 * * *
,它为您带来有趣的结果,例如从12月11日上午6点开始,这是您第一个完整的午夜至午夜时间间隔将在12月13日午夜关闭,因此,第一轮运行会在12月12日午夜日期execution_date
之前通过。但是我希望将timedelta
应用于start_date
,它将改为于12月12日上午6点开始,而昨天的时间与execution_date
相同。但是,我还没有看到它能以这种方式解决问题,这确实使我认为它可能仅在某处date
中使用了datetime
的{{1}}部分。
如记录所示,传入start_date
(以及所有宏日期)的时间将采用UTC(因此,将exeucution_date
时区偏移量中的午夜或凌晨6点转换为UTC)。至少它们附有tz,因此您可以根据需要在它们上使用start_date
。