Airflow 스케줄링과 Execution Date 뽀개기

Hyemi Noh
12 min readJun 11, 2023

--

https://cwiki.apache.org/confluence/download/attachments/145723561/wordmark_1.png?api=v2

이번 회사에서 Airflow를 처음 썼다. Airflow를 쓰면서 가장 이해하기 힘들었던 부분은 스케줄링과 Execution Date였다. 하지만 DE로서 여러 데이터 잡을 Airflow에서 운영해야하니 이해하지 않을 수 없었다. 중요한 daily DAG가 새벽에 돌다보니 Execution Date 때문에 실패해 잠에 깨거나 아침에 대참사를 맞이한 적도 종종 있었다. 😇 하지만 이렇게 이런저런 고생을 하다보니 이제는 어느정도 알게 됐다. 이해한 걸 정리도 하고 Airflow를 사용하시는 분들께 도움이 될 것 같아 공유해보려고 한다.

물론 2.2 이후부터는 Execution Date라는 표현을 사용하지 않고 Logical Date와 Data Interval이라는 표현을 쓴다. 하지만 아직 많은 곳에서 사용하기도 하고 UI에 표시도 되기 때문에 Execution Date로 지칭하고 표현을 혼용하겠다.

용어 정리

Schedule Interval

DAG가 어떤 주기로 수행돼야 하는지를 나타낸다. 0 4 * * * 처럼 cron 방식으로 표현된다.

Data Interval

execution_date를 이해하기 위해서 매우 중요한 개념이다. 공식 문서에 따르면 “작동 시간 범위”를 나타낸다. DAG가 오늘 스케줄에서 처리해야할 데이터의 시간 범위이다. 예를 들어, 매일 자정에 스케줄되는 DAG의 data interval은 매일 자정에 시작해서 다음 날짜 자정에 끝난다. 예를 들어 2020–01–01의 데이터를 처리한다면 2020–01–02 00:00:00에 수행된다. 그림을 통해 좀 더 이해하고 싶다면 이 포스트를 추천한다.

Execution Date (Logical Date)

Data Interval의 시작 시간이다. 위의 2020–01–02 00:00:00에 수행되는 DAG에서는 2020–01–01 00:00:00이 Execution Date이다.

Start Date

실제로 시작하는 날짜가 아니다. DAG의 첫번째 Data Interval 시작 시간이다. 공식 문서에 따르면 start_date에서 한 번의 Data Interval이 끝나고 DAG가 수행된다고 나와있다. DAG의 맨처음 수행과 Execution Date의 계산에 영향을 주는 값이다.

DAG Run

스케줄된 DAG의 인스턴스이다. DAG의 schedule_interval이 정의되면 주기적으로 수행되거나 외부 트리거에 의해 수행되는데, 수행된 각각의 DAG가 DAG Run이 된다. 클래스랑 객체 관계랑 비슷한 것 같다.

Execution Date 계산 방식

DAG를 맨 처음 수행할 때와 이후로 방식이 나뉜다. 하지만 start_date의 날짜와 시간은 모든 경우에서execution_date의 계산에 영향을 주기 때문에 now와 같은 위험한 값은 사용하지 않는 걸 권장한다. 공식 문서에서도 동적인 값보다 고정된 값을 쓰는 걸 권장하고 있다. 또한 특별한 이유가 없다면 처음 설정한 이후 변경하지 않는 걸 권장한다.

이후 ‘이전schedule_interval’ 이라는 표현을 굉장히 많이 사용한다. 정확히 정의하지 않으면 혼란을 줄 것 같아 미리 정의하고 가려고한다. 현재 시간을 기준으로 보면 이전 스케줄이 직전에 수행된 스케줄이고, 직전에 수행된 스케줄을 기준으로 보면 이전 스케줄이 그 이전의 스케줄이다. 즉, ‘이전 schedule_interval’의 대상이 달라진다.

아래에서는 ‘이전 schedule_interval’이라고 하면 현재 시간이 아닌 수행된 스케줄을 기준으로 한다. 공식적으로는 정확히 어떻게 지칭하는지 모르겠지만 schedule_interval이라는 개념을 생각해보면 이렇게 표현하는 게 맞는 것 같아서 이렇게 표현한다.

DAG를 맨 처음 수행할 때

  • DAG가 처음으로 언제 수행되는지와 원하는 스케줄 시간에 어떤 execution_date가 설정되는지를 신경써야한다.

DAG는 언제 처음 수행되는가?

  • DAG는 현재 시간이start_date에 지정된 날짜와 시간에서 schedule_interval까지 Data Interval을 설정할 수 있어야 수행된다. 그렇지 않다면 설정이 가능한 다음 schedule_interval에 수행된다. 따라서 start_date는 맨 처음 수행하길 원하는 날짜와 시간보다 무조건 이르게 설정해야한다.
  • 만약 배포후 DAG가 바로 수행되는 걸 원하지 않는다면 start_date를 수행을 원하는 날짜와 시간에서 정확히 이전 schedule_interval로 하면된다. 바로 수행 돼도 괜찮다면 그 이전으로 하고 수동으로 성공 처리를 하면 된다. 이럴 경우 첫 수행을 버리고 다음 수행부터 원하는 대로 수행되길 바라는 것이다.
  • 예시를 보면 훨씬 이해하기가 쉬울 것 같다. DAG를 2023–06–11 04:00:00에 처음 제대로 수행하고 싶고 2023–06–10 17:00:00에 배포한다고 가정해보자. 아래와 같이 케이스를 분류할 수 있다.

execution_date는 어떻게 계산되는가?

  • execution_date = max(start_date의 날짜와 시간, start_date의 날짜와 현재 시간 및 schedule_interval을 이용해 계산한 시간)
  • 자세한 계산 방식은 이 포스트에 나와있다. 하지만 start_date 혹은 이전 schedule_intervalexecution_date로 하면 된다. 계산 방식에도 나오듯이 DAG 인자로 넘겨준 start_date가 다른 값보다 크다면 그대로 쓰이니 주의해야한다.
  • 배포 후 DAG가 바로 수행됐다면 높은 확률로 원하는 시간에 원하는 execution_date로 수행될 확률이 크다.
  • 실제 예시로 값을 계산해보면 다음과 같다.

DAG를 맨 처음 수행한 이후

  • execution_date = max(start_date의 날짜와 시간, 마지막 DAG Run Data Interval End, 이전 schedule_interval)
  • 역시 예시로 값을 계산해보자. 마지막 DAG Run이 오래전인 경우는 보통 스케줄을 꺼놓고 있다가 다시 키거나 이전 DAG Run들을 직접 삭제한 것이므로 스케줄 시간에 맞추지 않았다.

schedule_interval 변경시 주의 사항

운영을 하다보면 분명 schedule_interval을 바꿔야하는 순간이 온다. 만약 해당 DAG에 의존하는 DAG가 존재해ExternalTaskSensor를 사용한다면 execution_date가 기대한 대로 설정되는게 중요하다. 센싱하는 DAG의 execution_date가 틀리면 센싱이 실패하기 때문이다.

그래서 ExternalTaskSensor를 사용해야한다면 아래 내용이 도움이 될 것 같다. 만약 DAG끼리 의존관계가 없어 센서를 사용하지 않거나 자동으로 execution_date를 계산하게 만들었다면 이 부분은 스킵해도 괜찮다.

schedule_interval을 변경하는 건 두 가지 경우가 있다. 시간을 미루거나 시간을 앞 당기는 것이다. 시간을 미룰 때는 배포하자마자 DAG가 수행된다. 그래서 다음 DAG Run에서는 execution_date가 변경된 schedule_interval에 맞춰서 설정된다.

시간을 앞당길 때는 마지막 DAG Run을 삭제하지 않으면, 다음 DAG Run에서 변경된 schedule_interval에 맞게 execution_date가 설정되지 않는다. 그래서 마지막 DAG Run을 삭제해야한다. 삭제하는 경우 시간을 미룰 때와 동일하게 삭제하자마자 DAG가 바로 수행되니 주의해야한다.

그림으로 예시를 들어보면 다음과 같다.

schedule_interval을 04:00에서 04:30으로 미루는 경우

2023–06–10 17:00:00에 schedule_interval30 4 * * * 으로 변경해 배포하면, 배포하자마자 노란색 Data Interval에 해당하는 DAG가 수행된다. 그리고 다음날인 2023–06–11부터는 Data Interval이 바뀐 schedule_interval에 맞춰 2023–06–10 04:30:00 ~ 2023–06–11 04:30:00으로 설정된다.

schedule_interval을 04:30에서 04:00으로 앞당기는 경우

2023–06–10 17:00:00에 schedule_interval0 4 * * * 으로 변경해 배포하면, 배포해도 DAG가 수행되지 않는다. 이미 해당 Data Interval은 이전 스케줄로 커버됐기 때문이다. 마지막 DAG Run을 삭제하지 않으면, 다음 DAG Run인 2023–06–11에 수행될 DAG는 여기에 맞춰서 Data Interval이 2023–06–10 04:30:00 ~ 2023–06–11 04:00:00으로 설정된다. 삭제하면 노란색 Data Interval에 해당하는 DAG가 수행된다. 그러면 2023–06–11에 수행될 DAG는 Data Interval이 바뀐 schedule_interval에 맞춰 2023–06–10 04:00:00 ~ 2023–06–10 04:00:00으로 설정된다.

UI로 다음 DAG Run 수행 시간 및 execution_date 확인하기

DAG 목록에서 혹은 DAG를 클릭해 들어갔을 때 우측 상단에서 다음 DAG Run의 수행 시간과 execution_date를 확인할 수 있다. 각각 Run AfterData IntervalStart이다. 특이한 예외가 있지 않은 이상 해당 값을 기반으로 수행된다. 그래서 배포후 아래 값들이 원하는 값으로 설정돼있는지 확인하는 걸 매우 권장한다.

DAG 목록
DAG 세부사항

정리

  • DAG를 맨 처음 수행할 때 start_date는 수행을 원하는 날짜와 시간에서 이전 schedule_interval에 정확히 맞추는 걸 권장한다. 처음 설정한 이후 변경하지 않는 게 좋다.
  • DAG의 execution_date계산 방법은 맨처음과 이후로 나뉜다. 맨처음에는 start_dateschedule_interval을 이용한다. 이후에는 start_date, 마지막 DAG Run의 Data Interval End, schedule_interval을 이용한다.
  • schedule_interval을 변경할 때는 시간을 미루는 경우와 앞 당기는 경우로 나뉜다. 미루는 경우 배포하자마자 수행되고 다음 DAG Run부터 execution_date가 변경된 값을 기준으로 설정된다. 앞 당기는 경우 마지막 DAG Run을 삭제해야 execution_date가 변경된 값을 기준으로 설정된다. 마지막 DAG Run을 삭제하면 삭제시 바로 수행된다.
  • UI를 통해 다음 DAG Run의 수행 시간과 execution_date를 확인할 수 있다.

Reference

관련해서 이해하는데도 글을 작성하는데도 시간이 꽤 걸렸다. 분명 이해한 것 같다가도 경험하지 못한 케이스를 보면 너무 헷갈렸다. 아니면 잘못된 방식으로 오해하기도 했다. 사실 이 글을 작성하면서도 헷갈려 직접 테스트를 해보거나 여러 번 글을 수정했다. 그리고 이 개념을 잘 모르는 사람들에게 어떻게 설명해야 이해가 쉬울지 고민하다보니 더 고민이 많았던 것 같다. 이제 한 90%는 안 것 같은데, 또 다른 케이스가 나오면 다시 헷갈릴 것 같다. 😇 하지만 1년 전 처음 사용하던 때처럼 미지의 영역은 아니다. 이제라도 어느정도 파악해서 다행이라고 생각한다.

--

--