Airflow 3로 업그레이드하기

Airflow 3로 업그레이드하기 (Upgrading to Airflow 3)

Apache Airflow 3는 주요(major) 릴리스로 breaking changes를 포함해요. 이 가이드는 Airflow 2.x에서 Airflow 3.0으로 업그레이드하는 데 필요한 단계를 안내해 드려요.

출처: 문서

본문

Airflow 3.x 아키텍처 변경 이해하기

Airflow 3.x는 보안, 확장성, 유지보수성을 개선하는 중요한 아키텍처 변경을 도입해요. 이러한 변경을 이해하면 업그레이드를 준비하고 워크플로우를 그에 맞게 적응시키는 데 도움이 돼요.

Airflow 2.x 아키텍처

Airflow 2.x architecture diagram showing scheduler, metadata database, and worker

  • 모든 컴포넌트가 Airflow 메타데이터 데이터베이스와 직접 통신해요.
  • Airflow 2는 모든 컴포넌트를 같은 네트워크 공간에서 실행하도록 설계됐어요: task 코드와 task 실행 코드(사용자 코드를 실행하는 airflow 패키지 코드)가 같은 프로세스에서 실행돼요.
  • 워커가 Airflow 데이터베이스와 직접 통신하며 모든 사용자 코드를 실행해요.
  • 사용자 코드가 세션을 import하고 Airflow 메타데이터 데이터베이스에서 악의적인 작업을 수행할 수 있었어요.
  • 데이터베이스에 대한 연결 수가 과도해져 확장에 어려움이 있었어요.

Airflow 3.x 아키텍처

Airflow 3.x architecture diagram showing the decoupled Execution API Server and worker subprocesses

  • API server가 현재 task와 워커를 위한 메타데이터 DB의 유일한 접근 지점이에요.
  • 여러 애플리케이션을 지원해요: Airflow REST API, 정적 JS를 호스팅하는 Airflow UI용 내부 API, 그리고 task 실행 인터페이스를 통해 TIs를 실행할 때 워커가 상호작용하는 API.
  • 워커가 데이터베이스 대신 API server와 통신해요.
  • Dag processor와 Triggerer가 그들의 task에 대해 task 실행 메커니즘을 활용하며, 특히 variables나 connections가 필요할 때 그래요.

데이터베이스 접근 제한 (Database Access Restrictions)

Airflow 3에서는 task 코드에서 메타데이터 데이터베이스에 직접 접근하는 것이 이제 제한돼요. 이는 Dag 작성자가 Airflow 리소스와 상호작용하는 방식에 영향을 주는 핵심 보안 및 아키텍처 개선이에요:

  • 직접 데이터베이스 접근 없음 (No Direct Database Access): Task 코드는 더 이상 Airflow 데이터베이스 세션이나 모델을 직접 import해 사용할 수 없어요.
  • API 기반 리소스 접근 (API-Based Resource Access): 모든 런타임 상호작용(상태 전이, heartbeat, XComs, 리소스 가져오기)은 전용 Task Execution API를 통해 처리돼요.
  • 향상된 보안 (Enhanced Security): 워커 task 코드가 Airflow 메타데이터 데이터베이스에 직접 접근하거나 수정하는 것을 방지해 격리와 보안을 개선해요. Dag 작성자 코드가 Dag File Processor와 Triggerer에서 잠재적으로 여전히 직접 데이터베이스 접근으로 실행될 수 있다는 점을 유의하세요 — 자세한 내용은 Airflow 보안 모델 참고.
  • 안정적 인터페이스 (Stable Interface): Task SDK는 직접 데이터베이스 의존성 없이 Airflow 리소스에 접근하기 위한 안정적이고 전방 호환 가능한 인터페이스를 제공해요.

1단계: 사전 요구사항 처리

  • Airflow 2.7 이상에 있는지 확인하세요. 최신 2.x로 업그레이드한 다음 Airflow 3으로 업그레이드하는 것을 권장해요.
  • Python 버전이 지원 목록에 있는지 확인하세요.
  • Airflow 3에서 제거된 기능이나 기능성을 사용하지 않는지 확인하세요.

2단계: 기존 Airflow 인스턴스 정리 및 백업

  • 마이그레이션 과정을 시작하기 전에 Airflow 인스턴스, 특히 Airflow 메타데이터 데이터베이스를 백업하는 것을 강력히 권장해요.

    • 데이터베이스에 대한 "핫 백업" 기능이 없다면, 백업이 일관되도록 Airflow 인스턴스를 종료한 후에 해야 해요. 예를 들어 Airflow 인스턴스를 끄지 않으면 데이터베이스 백업에 모든 TaskInstances나 DagRuns가 포함되지 않아요.
    • 백업을 하지 않고 마이그레이션이 실패하면 절반만 마이그레이션된 상태에 빠질 수 있어요. 이는 예를 들어 마이그레이션 중에 Airflow CLI와 데이터베이스 사이의 네트워크 연결이 끊어져 발생할 수 있어요. 백업을 하는 것은 이런 문제를 피하기 위한 중요한 예방책이에요.
  • 오래 실행된 Airflow 인스턴스는 더 이상 필요 없는 상당한 양의 데이터(예: 오래된 XCom 데이터)를 축적할 수 있어요. 스키마 변경은 Airflow 3 업그레이드 과정의 일부가 될 거예요. 데이터베이스가 크면 이러한 스키마 변경에 오랜 시간이 걸릴 수 있어요. 더 빠르고 안전한 마이그레이션을 위해 업그레이드 전에 Airflow 메타데이터베이스를 정리하는 것을 권장해요. airflow db clean Airflow CLI 명령을 사용해 Airflow 데이터베이스를 정리할 수 있어요.

  • AirflowDagDuplicatedIdException 같은 Dag 처리 관련 오류가 없는지 확인하세요. airflow dags reserialize를 오류 없이 실행할 수 있어야 해요. Dag 처리에서 오류를 해결해야 한다면, 업그레이드 전에 기존 인스턴스에 변경사항을 배포하고, 모든 Dags가 재처리될 때까지(모든 오류가 사라질 때까지) 기다린 후 업그레이드를 진행하세요.

3단계: Dag 작성자 - Airflow Dags 호환성 확인

이전 버전의 Airflow에서 업그레이드하는 사용자의 마찰을 최소화하기 위해 RuffAIR 규칙을 결합한 Dag 업그레이드 확인 유틸리티를 만들었어요. AIR301과 AIR302 규칙은 Airflow 3에서의 breaking changes를 나타내며, AIR311과 AIR312는 현재는 breaking하지 않지만 업데이트를 강력히 권장하는 변경을 강조해요.

최신 ruff 버전에 가장 최신 규칙이 있지만, 최소한 버전 0.13.1 사용을 확인하세요. 아래 예시는 Airflow 3에서 기대대로 동작하기 전에 수정해야 할 Dag 비호환성을 확인하는 방법을 보여줘요.

ruff check dags/ --select AIR301

권장 수정 사항을 미리 보려면 다음 명령을 실행해요:

ruff check dags/ --select AIR301 --show-fixes

일부 변경은 자동으로 수정할 수 있어요. 이렇게 하려면 다음 명령을 실행해요:

ruff check dags/ --select AIR301 --fix

일부 수정은 unsafe로 표시돼요. Unsafe 수정은 보통 Dag 코드를 깨뜨리지 않아요. 일부 런타임 동작을 변경할 수 있기 때문에 unsafe로 표시돼요. 자세한 내용은 Fix Safety를 참고하세요. 이러한 수정을 트리거하려면 다음 명령을 실행해요:

ruff check dags/ --select AIR301 --fix --unsafe-fixes

Note

AIR 규칙에서 unsafe 수정은 import된 멤버의 이름을 유지하면서 import 경로를 변경하는 것을 포함해요. 예를 들어 from airflow.sensors.base_sensor_operator import BaseSensorOperatorfrom airflow.sdk.bases.sensor import BaseSensorOperator로 변경하려면 ruff가 새 것을 추가하기 전에 원래 import를 제거해야 해요. 반대로 safe 수정은 from airflow.datasets import Datasetfrom airflow.sdk import Asset으로 변경하는 것처럼 멤버 이름과 import 경로 모두의 변경을 포함해요. 이러한 조정은 ruff가 이전 import를 제거할 필요가 없어요. 사용하지 않는 레거시 import를 제거하려면 unused-import 규칙(F401)을 활성화해야 해요.

구성 파일을 통해 이러한 플래그를 구성할 수도 있어요. 자세한 내용은 Configuring Ruff를 참고하세요.

주요 Import 업데이트 (Key Import Updates)

ruff가 많은 import 문제를 자동으로 고칠 수 있지만, Dags와 다른 코드가 Airflow 3에서 Airflow 컴포넌트를 올바르게 import하게 하기 위해 필요한 주요 import 변경이 여기 있어요. 더 오래된 경로는 폐지되며 향후 Airflow 버전에서 제거될 거예요.

Old Import Path (Deprecated) New Import Path (airflow.sdk)
airflow.decorators.dag airflow.sdk.dag
airflow.decorators.task airflow.sdk.task
airflow.decorators.task_group airflow.sdk.task_group
airflow.decorators.setup airflow.sdk.setup
airflow.decorators.teardown airflow.sdk.teardown
airflow.models.dag.DAG airflow.sdk.DAG
airflow.models.baseoperator.BaseOperator airflow.sdk.BaseOperator
airflow.models.param.Param airflow.sdk.Param
airflow.models.param.ParamsDict airflow.sdk.ParamsDict
airflow.models.baseoperatorlink.BaseOperatorLink airflow.sdk.BaseOperatorLink
airflow.sensors.base.BaseSensorOperator airflow.sdk.BaseSensorOperator
airflow.hooks.base.BaseHook airflow.sdk.BaseHook
airflow.notifications.basenotifier.BaseNotifier airflow.sdk.BaseNotifier
airflow.utils.task_group.TaskGroup airflow.sdk.TaskGroup
airflow.utils.context.Context airflow.sdk.Context
airflow.datasets.Dataset airflow.sdk.Asset
airflow.datasets.DatasetAlias airflow.sdk.AssetAlias
airflow.datasets.DatasetAll airflow.sdk.AssetAll
airflow.datasets.DatasetAny airflow.sdk.AssetAny
airflow.models.connection.Connection airflow.sdk.Connection
airflow.models.variable.Variable airflow.sdk.Variable
airflow.io.* airflow.sdk.io.*

마이그레이션 타임라인 (Migration Timeline)

  • Airflow 3.1: 레거시 imports가 폐지 경고를 보여주지만 계속 동작
  • 향후 Airflow 버전: 레거시 imports가 제거될 예정

4단계: Standard Provider 설치

  • airflow-core 패키지에 포함되어 있던 흔히 사용되는 Operators, Sensors, Triggers(예: BashOperator, PythonOperator, ExternalTaskSensor, FileSensor 등)가 이제 별도의 패키지 apache-airflow-providers-standard로 분리되었어요.
  • 편의상 이 패키지는 Airflow 2.x 버전에도 설치할 수 있어서, Dags가 Airflow Core 대신 standard provider 패키지에서 이러한 Operators를 참조하도록 수정할 수 있어요.

5단계: 직접 DB 접근하는 커스텀 작성 task 검토

Airflow 3에서 operators는 데이터베이스 세션을 사용해 Airflow 메타데이터 데이터베이스에 직접 접근할 수 없어요. 커스텀 operators가 있다면 직접 데이터베이스 접근 호출이 없는지 코드를 검토하세요. 필요한 경우 코드를 수정하는 방법은 https://github.com/apache/airflow/issues/49187의 예시를 따라할 수 있어요.

이전에 메타데이터 데이터베이스에 직접 접근하던 커스텀 operators나 task 코드가 있다면 다음 접근 방식 중 하나로 마이그레이션해야 해요:

권장 접근 방식: Airflow Python Client 사용

공식 Airflow Python Client를 사용해 REST API를 통해 Airflow 메타데이터 데이터베이스와 상호작용해요. Python Client는 DagRuns, TaskInstances, Variables, Connections, XComs 등을 포함해 대부분의 사용 사례에 대한 API를 정의하고 있어요.

장점 (Pros):

  • 워커에서 직접 데이터베이스 네트워크 접근 불필요
  • Airflow 3의 API-first 아키텍처와 가장 일치
  • 워커 환경에 데이터베이스 자격 증명 불필요 (API 토큰 사용)
  • 워커에 데이터베이스 드라이버 설치 불필요
  • API server를 통한 중앙 집중식 접근 제어와 인증

단점 (Cons):

  • apache-airflow-client 패키지 설치 필요
  • /auth/token에 API 호출을 수행해 접근 토큰을 획득하고 필요에 따라 회전 필요
  • API server 가용성과 API server로의 네트워크 접근 필요
  • 모든 데이터베이스 작업이 API 엔드포인트로 노출되지 않을 수 있음

Note

Airflow Python Client에서 사용할 수 없는 기능이 필요하다면 새 API 엔드포인트나 Task SDK 기능을 요청하는 것을 고려하세요. Airflow 커뮤니티는 직접 데이터베이스 접근을 활성화하는 것보다 누락된 API 기능을 추가하는 것을 우선시해요.

알려진 해결 방법: DbApiHook 사용 (PostgresHook 또는 MySqlHook)

Warning

이 접근 방식은 권장되지 않으며, Airflow Python Client를 사용할 수 없는 사용자를 위한 알려진 해결 방법으로만 문서화돼요. 이 접근 방식은 상당한 제한이 있으며 향후 Airflow 버전에서 깨질 거예요.

중요 고려사항:

  • 향후 버전에서 깨질 것 (Will break in future versions): 이 접근 방식은 Airflow 3.2+ 및 이후에서 깨질 거예요. 스키마 변경이 발생할 때 코드를 적응시키는 것은 사용자의 책임이에요.
  • 데이터베이스 스키마는 공개 API가 아님 (Database schema is NOT a public API): Airflow 메타데이터 데이터베이스 스키마는 통지 없이 언제든 변경될 수 있어요. 스키마 변경은 경고 없이 쿼리를 깨뜨릴 거예요.
  • task 격리 깨뜨림 (Breaks task isolation): 이것은 Airflow 3의 핵심 기능 중 하나인 task 격리와 모순돼요. Tasks는 메타데이터 데이터베이스에 직접 접근해서는 안 돼요.
  • 성능 영향 (Performance implications): 각 task가 별도의 데이터베이스 연결을 여는 Airflow 2 동작을 다시 도입해 성능 특성과 확장성을 극적으로 바꿔요.

사용 사례가 Python Client로 해결될 수 없고 위험을 이해한다면, 데이터베이스 hooks를 사용해 메타데이터 데이터베이스를 직접 쿼리할 수 있어요. 메타데이터 데이터베이스 유형과 일치하는 데이터베이스 connection(PostgreSQL 또는 MySQL)을 만들고 Airflow의 Database Hooks를 사용하세요.

참고: 이 hooks는 psycopg2 또는 mysqlclient 같은 데이터베이스 드라이버를 사용해 (API server를 통하지 않고) 데이터베이스에 직접 연결해요.

PostgresHook 사용 예시 (MySql도 비슷한 인터페이스가 있어요)

from airflow.sdk import task
from airflow.providers.postgres.hooks.postgres import PostgresHook


@task
def get_connections_from_db():
    hook = PostgresHook(postgres_conn_id="metadata_postgres")
    records = hook.get_records(sql="""
        SELECT conn_id, conn_type, host, schema, login
        FROM connection
        WHERE conn_type = 'postgres'
        LIMIT 10;
        """)

    return records

SQLExecuteQueryOperator 사용 예시

hooks 대신 operators를 사용하는 것을 선호한다면 SQLExecuteQueryOperator를 사용할 수도 있어요:

from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator

query_task = SQLExecuteQueryOperator(
    task_id="query_metadata",
    conn_id="metadata_postgres",
    sql="SELECT conn_id, conn_type FROM connection WHERE conn_type = 'postgres'",
    do_xcom_push=True,
)

Note

메타데이터 데이터베이스 connection에는 항상 읽기 전용 데이터베이스 자격 증명을 사용하고, 임시 자격 증명을 사용하는 것이 권장돼요.

6단계: 배포 관리자 - Airflow 인스턴스 업그레이드

더 쉽고 안전한 업그레이드 과정을 위해 Airflow 인스턴스 구성을 업그레이드하는 유틸리티도 만들었어요.

첫 단계는 아래처럼 이 구성 확인 유틸리티를 실행하는 거예요:

airflow config update

이 구성 유틸리티는 구성을 자동으로 업데이트해 Airflow 3과 호환되게 만들 수도 있어요. 이는 아래처럼 할 수 있어요:

airflow config update --fix

Airflow 업그레이드의 가장 큰 부분은 데이터베이스 업그레이드예요. Airflow 3의 데이터베이스 업그레이드 과정은 Airflow 2.7 이상과 동일해요:

airflow db migrate

Flask-AppBuilder 뷰(appbuilder_views), Flask-AppBuilder 메뉴 항목(appbuilder_menu_items), 또는 Flask blueprints(flask_blueprints)를 사용하는 플러그인이 있다면, 그것들을 FastAPI 앱으로 변환하거나 Airflow 3용 역호환 레이어를 제공하는 FAB provider를 설치해야 해요. 이상적으로는 플러그인을 Airflow 3 플러그인 인터페이스, 즉 External Views(external_views), Fast API 앱(fastapi_apps), FastAPI 미들웨어(fastapi_root_middlewares)로 변환해야 해요.

Airflow Helm Chart를 사용해 Airflow를 배포한다면, 정의한 values를 Airflow 3에서 사용 가능한 구성 옵션과 대조해 확인하세요. webserver 아래의 모든 구성 옵션을 apiServer로 변경해야 해요. 많은 파라미터가 이름이 바뀌거나 제거되었음을 고려하세요. 전체 차트별 업그레이드 체크리스트(values.yaml 변경, standalone Dag processor, JWT secret, FAB 기본값, 최소 Kubernetes 버전, 차트 1.16.0..1.18.0에서 이름이 바뀐 키)는 Helm Chart를 Airflow 3으로 업그레이드를 참고하세요.

7단계: 시작 스크립트 변경

Airflow 3에서 Webserver는 일반 API server가 됐어요. API server는 다음 명령으로 시작할 수 있어요:

airflow api-server

Dag processor는 이제 로컬이나 개발 설정에서도 독립적으로 시작해야 해요:

airflow dag-processor

이제 Airflow 3 인스턴스를 시작할 수 있을 거예요.

8단계: 확인할 사항들

Airflow 인스턴스를 업그레이드한 후 다음을 확인하는 것을 고려해 보세요:

  • OAuth, OIDC 또는 LDAP로 Single-Sign-On(SSO)을 구성했다면 인증이 기대대로 동작하는지 확인하세요. 커스텀 webserver_config.py를 사용한다면 from airflow.www.security import AirflowSecurityManagerfrom airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride로 바꿔야 해요.

Breaking Changes

Airflow 2.x에서 폐지된 일부 기능은 Airflow 3에서 사용할 수 없어요. 여기에는 다음이 포함돼요:

  • SubDAGs: TaskGroups, Assets, Data Aware Scheduling으로 대체.

  • Sequential Executor: SQLite와 함께 로컬 개발용으로 사용할 수 있는 LocalExecutor로 대체.

  • CeleryKubernetesExecutor와 LocalKubernetesExecutor: 여러 Executor 구성으로 대체.

  • SLAs: 폐지되고 제거됨; Deadline Alerts로 대체.

  • Subdir: 많은 CLI 명령의 인자로 사용되던 --subdir 또는 -SDag bundles로 대체.

  • REST API (/api/v1) 대체됨: 현대 FastAPI 기반 안정 /api/v2를 사용하세요; 자세한 내용은 Airflow API v2 참고.

  • 일부 Airflow 컨텍스트 변수: task instance의 컨텍스트에서 다음 키를 더 이상 사용할 수 없어요. 대체하지 않으면 Dag 오류를 일으킬 거예요:

    • tomorrow_ds
    • tomorrow_ds_nodash
    • yesterday_ds
    • yesterday_ds_nodash
    • prev_ds
    • prev_ds_nodash
    • prev_execution_date
    • prev_execution_date_success
    • next_execution_date
    • next_ds_nodash
    • next_ds
    • execution_date
  • catchup_by_default Dag 파라미터가 기본적으로 이제 False예요.

  • create_cron_data_intervals 구성이 기본적으로 이제 False예요. 즉 기본적으로 CronDataIntervalTimetable 대신 CronTriggerTimetable이 사용될 거예요.

    이것은 schedule=순수 cron 문자열을 전달하는 Dags에만 영향이 있어요(예: schedule="0 0 * * *"); 명시적 timetable 인스턴스를 전달하는 Dags는 영향받지 않아요. data_interval_start / data_interval_end(및 task에서 logical_date에서 파생되고 두 timetable 사이에서 이동하는 ds / ts 같은 관련 템플릿 값)에 의존하는지 결정하세요. 의존한다면 CronDataIntervalTimetable을 유지하기 위해 create_cron_data_intervals=True를 명시적으로 설정하세요. 그렇지 않다면 새로운 False 기본값이 괜찮아요.

    이 설정을 업그레이드 전에 해두세요. 일부 Airflow 3 dagruns가 이미 존재한 후 플래그를 변경하면(즉 CronTriggerTimetableCronDataIntervalTimetable), 이전 run의 logical_date와 충돌하지 않도록 예약된 run 하나가 건너뛰어져요.

  • 수동 Dag runs와 데이터 구간: Airflow 3에서는 수동으로 트리거된 Dag run의 data_interval이 제공된 logical_date에서 파생되거나 같다고 가정하지 마세요. Dag 로직에 사용자가 지정한 트리거 날짜가 필요하면 logical_date를 명시적으로 사용하세요. 이는 특히 수동 트리거 중이거나 TriggerDagRunOperator를 사용할 때 data_interval_start 또는 data_interval_end를 읽는 워크플로우에 영향을 줘요. 자세한 마이그레이션 지침은 수동 Dag runs와 logical_date를 참고하세요.

  • Simple Auth가 이제 기본 auth_manager예요. FAB를 Auth Manager로 계속 사용하려면 FAB provider를 설치하고 auth_managerFabAuthManager로 설정하세요:

    airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager
    
  • AUTH API auth manager에 정의된 API 경로는 /auth 라우트 접두어가 붙어요. oauth redirect url처럼 애플리케이션 외부에서 소비되는 URL은 그에 맞게 업데이트해야 해요. 예를 들어 Airflow 2.x에서 https://<your-airflow-url.com>/oauth-authorized/google이었던 oauth redirect url은 Airflow 3.x에서 https://<your-airflow-url.com>/auth/oauth-authorized/google이 돼요.

  • XCom Pull 기본 동작: task_ids 인자 없이 xcom_pull()을 호출하면 이제 현재 task에서만 가져와요. Airflow 2에서는 task_ids를 생략하면 Dag run의 모든 task를 검색해 주어진 키에 대해 가장 최근에 푸시된 값을 반환했어요. 이제 다른 task에서 XComs를 가져오려면 task_ids를 명시적으로 전달해야 해요:

    # Airflow 2 - pulls most recent value from any task
    value = ti.xcom_pull(key="shared_state")
    
    # Airflow 3 - same call only checks the current task
    value = ti.xcom_pull(key="shared_state")
    
    # Airflow 3 - specify task_ids to pull from other tasks
    value = ti.xcom_pull(task_ids="upstream_task", key="shared_state")
    

수동 Dag Runs와 logical_date

스케줄된 run의 경우 logical_datedata_interval은 둘 다 Dag의 timetable에서 파생돼요.

Airflow 3에서 수동으로 트리거된 run의 경우, data_interval_startdata_interval_end가 제공된 logical_date에서 파생되거나 같다고 가정하지 마세요. 결과 data_interval은 timetable과 트리거 경로에 의존하며, 일부 API는 데이터 구간을 명시적으로 제공할 수도 있어요.

이것은 다음 Dags에 가장 중요해요:

  • 수동 run 동안 data_interval_start 또는 data_interval_end를 사용하는 경우
  • TriggerDagRunOperator로 다운스트림 Dags를 트리거하는 경우
  • Airflow 2에서 마이그레이션했고 data_interval_start를 요청된 수동 run 날짜로 취급한 경우

마이그레이션 지침

Dag 로직이 수동 run에 대해 사용자가 지정한 날짜가 필요하다면 logical_date를 명시적으로 사용하세요.

from airflow.decorators import get_current_context, task


@task
def process_data():
    context = get_current_context()
    processing_date = context["logical_date"]
    return f"Processing data for {processing_date}"

사용자가 제공한 트리거 날짜 대신 run의 해석된 구간 의미론이 필요할 때는 data_interval_startdata_interval_end를 계속 사용하세요.

Airflow 2에서 업그레이드할 때 data_interval_startdata_interval_end를 읽는 수동 트리거 워크플로우를 검토하고, 정말로 구간 의미론을 원했는지 아니면 요청된 logical date를 원했는지 확인하세요.

더 알아보기 (Learn more)