시간대 다루기 — UTC 중심의 DAG 스케줄링

시간대 다루기 — UTC 중심의 DAG 스케줄링

배치 워크플로를 만들다 보면 "이 DAG은 뉴욕 기준 매일 아침 9시에 돌아야 해" 같은 요구를 만나요. Airflow는 시간대 지원을 기본으로 켜 두고, 내부와 데이터베이스에는 datetime을 모두 UTC로 저장해요. 그래서 시간대 의존 스케줄로 DAG을 돌릴 수 있으면서도 내부 정합성은 UTC로 유지하는 거죠. 다만 UI에는 항상 UTC로 표시되고, 사용자 시간대로 자동 변환되지 않으니 이 점은 기억해 두셔야 해요.

출처: Airflow 공식 문서 — Time Zones

본문

시간대를 잘못 다루면 단순한 DAG에는 문제가 안 보여도, 금융처럼 장 마감 시한을 지켜야 하는 곳에서는 골치 아파져요. Airflow는 시간대 설정을 airflow.cfg에서 정하고, 기본값은 UTC예요. 시스템 설정이나 IANA 시간대(예: Europe/Amsterdam)로 바꿀 수도 있는데, pytz보다 정확한 pendulum에 의존하고 설치 때 함께 딸려 들어와요.

주의할 점 하나가 있어요. pendulum은 기본적으로 자체 시간대 데이터베이스를 쓰는데, 이게 IANA 데이터베이스만큼 자주 갱신되지는 않아요. 시스템 데이터베이스를 쓰게 하려면 PYTZDATA_TZDATADIR 환경 변수에 시스템 DB 경로(예: /usr/share/zoneinfo)를 지정하면 돼요.

airflow.sdktimezone 모듈로 현재 시각이나 날짜를 만드는 기본 예시부터 볼게요.

from airflow.sdk import timezone

now = timezone.utcnow()
a_date = timezone.datetime(2017, 1, 1)

웹 UI의 시간대

기본적으로 웹 UI는 시간을 UTC로 보여줘요. 우측 상단 메뉴(시계 아이콘 클릭)에서 표시 시간대를 바꿀 수 있어요. "Local"은 브라우저의 시간대에서 감지되고, "Server" 값은 [core] 섹션의 default_timezone 설정에서 와요. 사용자가 고른 시간대는 LocalStorage에 저장되므로 브라우저별로 유지돼요.

기본 시간대 (Default timezone)

기본 시간대는 [core] 아래의 default_timezone 설정으로 정해져요. Airflow를 막 설치했다면 utc로 되어 있고 이게 권장 사항이에요. system이나 IANA 시간대(예: Europe/Amsterdam)로 바꿀 수도 있어요.

dag = DAG(
    "my_dag",
    start_date=pendulum.datetime(2017, 1, 1, tz="UTC"),
    default_args={"retries": 3},
)
op = BashOperator(task_id="hello_world", bash_command="Hello World!", dag=dag)
print(op.retries)  # 3

설정 파일에서는 이렇게 생겼어요.

[core]
default_timezone = utc

DAG도 워커에서 평가되므로 워커의 시간대 설정도 함께 맞춰야 해요.

시간대 인지 DAG (Time zone aware Dags)

시간대 인지 DAG을 만드는 건 아주 간단해요. pendulum으로 시간대를 아는 start_date를 주면 되는데, 표준 라이브러리의 timezone은 한계가 알려져 있고 DAG에 쓰는 걸 의도적으로 금지하고 있으니 pendulum을 쓰세요.

import pendulum

dag = DAG("my_tz_dag", start_date=pendulum.datetime(2016, 1, 1, tz="Europe/Amsterdam"))
op = EmptyOperator(task_id="empty", dag=dag)
print(dag.timezone)  # <Timezone [Europe/Amsterdam]>

템플릿에서의 시간대

Airflow는 템플릿에서 시간대를 아는 datetime을 돌려주지만, 로컬 시간으로 변환하지 않으므로 그대로 UTC 상태예요. 이후 변환은 DAG 쪽에서 처리해야 해요.

import pendulum

local_tz = pendulum.timezone("Europe/Amsterdam")
local_tz.convert(logical_date)

크론 스케줄과 시간대

시간대 인지 DAG이 크론 스케줄을 쓰면 일광 절약 시간(DST)을 존중해요. 예를 들어 US/Eastern 시간대에 시작일이 있고 스케줄이 0 0 * * *인 DAG은, 일광 절약 시간 동안 매일 04:00 UTC에, 그 외에는 05:00 UTC에 실행돼요.

시간 간격과 시간대

timedeltarelativedelta 스케줄을 쓰는 시간대 인지 DAG은 시작일에서는 일광 절약 시간을 존중하지만, 이후 실행을 예약할 때는 일광 절약 시간을 조정하지 않아요. 예를 들어 pendulum.datetime(2020, 1, 1, tz="UTC")에 시작일을 두고 스케줄 간격을 timedelta(days=1)로 잡으면, 일광 절약 시간과 무관하게 매일 05:00 UTC에 실행돼요.

더 알아보기