- はじめに — 静かに過ぎたEOL
- アップグレードではなくアーキテクチャ転換である
- DAGコード — ruffが半分、セマンティクスの変化が残り半分
- 一番痛い部分 — メタデータDBを直接読んでいたカスタムコード
- デプロイ担当者チェックリスト — 順序のある作業
- SLAの5か月の空白 — 削除が代替より先に来る
- 2.11は橋として設計された — まだ2.xならここから
- 3.x系列の現在地 — 2026年7月、3.3.0
- では今何をすべきか
- おわりに
- 参考資料
はじめに — 静かに過ぎたEOL
2026年4月22日、Apache Airflow 2がEOL(サポート終了)を迎えました。プロジェクトREADMEのバージョンライフサイクル表が示す公式の事実はこうです — Airflow 2は2025年10月22日から限定メンテナンス(セキュリティ・重大バグ修正のみ)に入り、2026年4月22日付でステータスがEOLになりました。READMEの文をそのまま移せば、EOLバージョンはいかなる修正もサポートも受けられません。endoflife.dateのデータも同じ日付を示しています。
本稿執筆時点の今日(2026-07-17)で3か月が経ちました。日付を一本のタイムラインに並べるとこうです(いずれもGitHubリリースとPyPIリリース履歴で確認した日付です)。
2020-12-17 Airflow 2.0.0 初回リリース
2025-04-22 Airflow 3.0.0 (SLA、SubDAG、pickling、REST v1を削除)
2025-05-20 Airflow 2.11.0 (ブリッジリリース — 移行ツールをバックポート)
2025-09-25 Airflow 3.1.0 (Deadline Alertsがexperimentalとして到着)
2025-10-22 Airflow 2.x が限定メンテナンスへ (セキュリティ・重大バグ修正のみ)
2026-03-14 Airflow 2.11.2 (現時点で最後の2.xパッチ)
2026-04-07 Airflow 3.2.0 (asset partitioning、multi-team)
2026-04-22 Airflow 2.x EOL (以降いかなる修正もなし)
2026-07-06 Airflow 3.3.0 (state store、プラガブルなリトライ、Java/Go SDK)
5年4か月生きたメジャーバージョンにしては静かな退場でした。そして今2.xに残っているチームにとって、これらの日付が意味するところは単純です — 次のCVEが出ても、原則として2.x向けのパッチはありません。問題は2から3への道が短くないことです。本稿はこの道で実際に手がかかる作業を、誇張なく公式アップグレードガイドとリリースノートの原文に基づいて整理します。
アップグレードではなくアーキテクチャ転換である
2と3の違いを一文に絞るとこうなります — 2ではすべてのコンポーネントがメタデータDBに直接つながっていましたが、3ではタスクとワーカーにとってAPIサーバーが唯一の接点になります。
アップグレードガイド自身が挙げる2.xの問題点は率直です。タスクコードとタスク実行コードが同じプロセスで動いていたこと、ワーカーがDBに直接つながってすべてのユーザーコードを実行していたこと、ユーザーコードがDBセッションをインポートしてメタデータDBに悪意ある操作ができたこと、DBコネクション数が過剰でスケーリングが難しかったこと。Airflow 3はこれをTask Execution APIで断ち切ります — 状態遷移、ハートビート、XCom、リソース照会がすべて専用APIを経由し、タスクコードはDBセッションに触れられません。
この転換がマイグレーション工数の根っこです。DAGファイルのインポートパス修正はツールが半分ほどやってくれますが、「ワーカーでメタデータDBを直接読んでいたコード」は設計を変える必要があります。後段で別に扱います。
一つニュアンスもドキュメントのまま移しておきます — DAG作成者コードは、Dagファイルプロセッサとトリガラーの中ではまだDB直接アクセスの状態で実行され得ます。分離はタスク実行経路に対するものであり、まだすべての経路に対するものではありません。
DAGコード — ruffが半分、セマンティクスの変化が残り半分
機械が拾ってくれる部分
公式ガイドが最初に案内するツールはRuffのAIRルールです。AIR301とAIR302がAirflow 3で壊れるコードを、AIR311とAIR312が今は動くが乗り換えるべきコードを示します。ドキュメントはruff 0.13.1以上を要求します。
ruff check dags/ --select AIR301 --show-fixes # 何がどう直るかプレビュー
ruff check dags/ --select AIR301 --fix # 安全な自動修正
ruff check dags/ --select AIR301 --fix --unsafe-fixes # インポートパスの置き換えまで
ここでunsafeという語に怖じ気づく必要はありません — AIRルールでいうunsafeなフィックスとは、たいてい「インポートされるメンバー名はそのままにパスだけ変える」修正を指すとドキュメントが説明しています。
インポート移動の方向は一つです。DAG作成に使うものすべてがairflow.sdkネームスペースへ移ります。
airflow.models.dag.DAG -> airflow.sdk.DAG
airflow.models.baseoperator.BaseOperator -> airflow.sdk.BaseOperator
airflow.decorators.task -> airflow.sdk.task
airflow.datasets.Dataset -> airflow.sdk.Asset (名前まで変わる)
airflow.models.variable.Variable -> airflow.sdk.Variable
airflow.models.connection.Connection -> airflow.sdk.Connection
airflow.hooks.base.BaseHook -> airflow.sdk.BaseHook
公式のタイムラインは明示的です — 3.1ではレガシーなインポートは警告を出しつつ動作し、将来のバージョンで削除されます。今直しておくのが正解です。
もう一つ、BashOperator、PythonOperator、ExternalTaskSensor、FileSensorのようにコアにバンドルされていたよくあるオペレーターが、apache-airflow-providers-standardという別パッケージに切り出されました。このパッケージは2.xにもインストールできるので、アップグレード前にあらかじめインポートを変えておく先行作業が可能です。
機械が拾ってくれない部分 — セマンティクスが変わる
インポートより危ないのは、動くけれど違う動きをする変化です。アップグレードガイドのBreaking Changes節から、実務で大きく引っかかるものだけ抜き出します。
execution_dateとその派生キーがコンテキストから消えました。 execution_date、prev_execution_date、next_execution_date、prev_ds、next_ds、tomorrow_ds、yesterday_ds系がすべて削除リストにあります。テンプレートやタスクコードでこのキーを参照するとDAGエラーになります。代替はlogical_dateとdata interval系です。
クロン文字列のデフォルトのセマンティクスが変わりました。 create_cron_data_intervals設定のデフォルト値がFalseになったことで、schedule="0 0 * * *"のようにクロン文字列をそのまま渡したDAGは、今やCronDataIntervalTimetableではなくCronTriggerTimetableとして解釈されます。明示的にtimetableインスタンスを渡したDAGは影響を受けません。問題はdata_interval_startやds、tsのようなテンプレート値がlogical_dateから派生している点で、二つのtimetableの間でこれらの値がずれます。これらの値に依存しているなら、アップグレード前にcreate_cron_data_intervals=Trueを明示せよというのがドキュメントの指示であり、3.xのランがすでに生成された後で遅れてフラグを戻すと、以前のランのlogical_dateとの衝突を避けるためにスケジュールランを一つ飛ばします。
手動トリガーランのdata intervalを仮定してはいけません。 3では、手動でトリガーされたランのdata_intervalがユーザーが渡したlogical_dateから派生していると仮定するな、とドキュメントが明示しています。ユーザーが指定した日付が必要ならlogical_dateを直接読む必要があり、これはTriggerDagRunOperatorで下位DAGをトリガーするワークフローに特に引っかかります。
xcom_pull()のデフォルトの動作が変わりました。 2ではtask_idsなしで呼ぶとDAGラン全体からそのキーの最新値を探してくれましたが、3では現在のタスクの中でしか探しません。他のタスクのXComが必要な場合は、今後task_idsを必ず渡す必要があります。こうしたコードは静かにNoneを受け取り始めるので、grepで洗い出しておくのが安全です。
catchup_by_defaultがFalseになりました。 スケジュールの空白を追いつくcatchupに依存していたなら、DAGに明示する必要があります。
そして機能削除リスト — SubDAG(TaskGroupとassetスケジューリングに置換)、SequentialExecutor、CeleryKubernetesExecutorとLocalKubernetesExecutor(マルチexecutor構成に置換)、DAG・XComのpickling、CLIの--subdir引数(Dagバンドルに置換)、そしてREST API v1がすべて消えました。APIクライアントはFastAPIベースのv2へ移行する必要があります。
一番痛い部分 — メタデータDBを直接読んでいたカスタムコード
カスタムオペレーターやタスクコードがAirflowのDBセッションでメタデータを読み書きしていたチームにとっては、ここがマイグレーション工数の最大項目です。3ではこのコードは動作せず、公式ガイドは二つの経路を示します。
推奨経路はAirflow Python ClientでREST APIを使うことです。 DagRun、TaskInstance、Variable、Connection、XComなど、ほとんどのユースケースがAPIでカバーされます。ただしドキュメントは欠点もそのまま記しています — トークンを/auth/token呼び出しで取得しローテーションする必要があり、APIサーバーの可用性とネットワーク経路に依存するようになり、すべてのDB操作がAPIエンドポイントとして公開されているわけではありません。欠けている機能については直接DBに戻らず、API追加をリクエストせよというのがコミュニティの方針です。
回避策はPostgresHookのようなDbApiHookでメタデータDBに一般的なDBコネクションを張ることですが、ドキュメント自身が推奨しないと明言しています。 理由も明示的です — この方式はAirflow 3.2以降のバージョンで壊れる予定であり、メタデータDBのスキーマは公開APIではないため予告なく変わり、タスク分離という3の中核設計と衝突し、タスクごとにDBコネクションを開く2時代のパフォーマンス特性に逆戻りします。一時的に使うとしても読み取り専用アカウントで、乗り換え計画とセットで使う必要があります。実際の移行例はissue #49187に集まっています。
工数見積もりの観点から助言すると、マイグレーションの見積もりを出す前に二つの数を数えておくとよいでしょう — ruff AIR301違反の件数、そしてDBセッションを直接触るカスタムオペレーターの件数。前者はほとんどが機械的に解決し、後者は一つひとつが設計変更です。
デプロイ担当者チェックリスト — 順序のある作業
運用側の作業はガイドの手順をなぞればよいのですが、いくつかは前もって知っておかないとスケジュールが狂います。
airflow config update # 何が変わるべきかの点検 (2.11にもバックポート)
airflow config update --fix # 自動修正
airflow db migrate # スキーママイグレーション — 最も時間のかかる段階
airflow api-server # webserverコマンドはもうない
airflow dag-processor # 今や単独起動が必須 (ローカル開発も含む)
- 前提条件。 最低Airflow 2.7、推奨は最新の2.xを経由して3へ行く経路です。
airflow dags reserializeがエラーなく動く必要があり、DAG処理エラーはアップグレード前に旧バージョンで解消しておく必要があります。 - DBの掃除とバックアップ。 スキーマ変更にかかる時間はDBサイズに比例します。ドキュメントは
airflow db cleanで古いXComなどを整理してから始めることを推奨し、バックアップなしで進めてマイグレーションが途中で切れると、半分だけマイグレーションされた状態に陥りうると警告しています。 - コンポーネント構成が変わります。 ウェブサーバーは汎用APIサーバーになり、Dagプロセッサは常に独立プロセスとして起動する必要があります。Helmチャートを使うなら、webserver配下の値をすべてapiServerへ移す必要があり、チャート1.16.0から1.18.0の間にキー名がいくつも変わりました。
- 認証が変わります。 デフォルトのauth managerがSimple Authに変わり、FABベースの認証を維持するにはFAB providerをインストールしてauth_managerを明示する必要があります。OAuthリダイレクトURLには
/authプレフィックスが付きます — IdP側に登録されたredirect URLも合わせて直す必要があるということです。 - プラグイン。 Flask-AppBuilderのビューとブループリントに依存していたプラグインは、FastAPIアプリへ移すか、FAB providerの互換レイヤーに乗せる必要があります。
SLAの5か月の空白 — 削除が代替より先に来る
今回のメジャー転換で一番教訓的な出来事はSLAです。3.0.0(2025-04-22)はSLAのコールバックとメトリクスを削除しつつ、リリースノートに「より柔軟な代替メカニズムであるDeadlineAlertsが将来のバージョンで計画されている」と記しました。そのDeadline Alerts(AIP-86)は3.1.0(2025-09-25)でようやく到着し、それすらexperimental表記で非同期コールバックしかサポートしていませんでした。同期コールバック(SyncCallback)は3.2.0(2026-04-07)で追加されましたが、3.2.0のリリースノート時点でもまだexperimental表記が付いています。3.3.0はBrowseメニューにDeadlinesページを追加しました。
まとめると — 3.0へ早く移行したSLAユーザーには本家機能ベースで5か月の空白があり、代替は到着から10か月経った今もexperimentalの札を付けたまま成熟中です。3.0のリリースノートが示した当座の対処は、タスクレベルの成功・失敗フックや外部モニタリングでした。特定の機能に大きく依存しているチームにとっては、メジャーアップグレードの計画で「その機能の代替がどのバージョンでどんな状態にあるか」をリリースノートで自分で確認する習慣が必要だという話でもあります。
2.11は橋として設計された — まだ2.xならここから
2.11.0(2025-05-20)は新機能リリースというより移行装置です。リリースノートの「Ease migration to Airflow 3」節が示す内容はこうです。
airflow config lintとairflow config updateが2.11にバックポートされ、3へ行く前に設定をあらかじめ点検・修正できます。execution_dateを使っていたすべてのモデルにlogical_dateフィールドが並行して追加されました。3はexecution_dateを完全に捨てます。create_delta_data_intervalsフラグ(2.11のデフォルトはTrue、3.0のデフォルトはFalse)を先に反転させておくと、timedeltaスケジュールの新しい解釈(DeltaTriggerTimetable)を2.xのうちにあらかじめ体験できます。クロン側のcreate_cron_data_intervalsと同じ趣旨のリハーサル装置です。- タイミングメトリクスの単位を統一する
timer_unit_consistencyも同じパターンです — 2.11で試しにオンにでき、3.0では常にオンの状態になります。
つまり公式の経路は明確です。最新の2.11.xへ上げ、フラグを3.0のデフォルト方向へあらかじめ反転させ、ruffとconfig lintで違反を潰してから3へ移る。2.11がPython 3.9から3.12までをサポートするので、Pythonバージョンの整合もこの段階で済ませておけます。
3.x系列の現在地 — 2026年7月、3.3.0
渡った先の世界も正直に見る必要があります。二つ重要な点があります。
第一に、3.x系列の中でも最新マイナーの追跡が事実上要求されます。 リリース履歴を見ると、3.0.xの最後のパッチは3.0.6(2025-08-29)で3.1.0の直前が最後、3.1.xは3.1.8(2026-03-11)で3.2.0の直前、3.2.xは3.2.2(2026-05-29)で3.3.0の直前が最後です。前のマイナー向けのパッチは次のマイナーが出ると事実上止まるパターンで、READMEも使用中のメジャーの最新マイナーを使うよう推奨しています。LTSのようなものはありません。おおむね半年周期のマイナーアップグレードを運用カレンダーに組み込む必要があるということです。
第二に、コードの移動は3.x系列の中でも続いています。 3.2.0はタスクが使う例外をairflow.sdk.exceptionsへ移し(既存のairflow.exceptionsインポートは警告を出すプロキシとして残存)、直列化(serde)ロジックもTask SDKへ移しました — 両方の互換レイヤーともAirflow 4で削除予定だとドキュメントが明言しています。2から3へ渡ったからといって引っ越しが終わるわけではなく、airflow.sdkへの移住はまだ進行中です。マイグレーション予算にこの後続作業も入れておくのが現実的です。
機能側の流れは手短にだけ触れます。3.1(2025-09-25)はDeadline Alerts、Human-in-the-Loop、UI多言語化を携えて登場し、3.2(2026-04-07)の目玉はasset partitioning — assetの全体ではなく特定のパーティションの変更だけで下位DAGをトリガーする、日付でパーティション化されたデータレイクと相性の良い機能 — と、multi-teamデプロイ(一つのAirflow内でチームごとにDAG・コネクション・プールを分離、experimental表記)でした。3.3.0(2026-07-06)はそのパーティショニングをRollupMapper、FanOutMapperのようなマッパーとwait_policyで拡張し、リトライポリシーをプラガブルにし(AIP-105)、タスク・asset状態ストア(AIP-103)を追加し、Pythonではない言語でタスクを書くCoordinatorレイヤー(AIP-108)をexperimentalとして搭載しました — Java SDKは2026-07-13に1.0.0-beta1がタグ付けされました。新機能の多くにexperimental表記が付いている点は、そのまま読んでおくのがよいでしょう。これらの機能を理由に急ぐ必要は薄く、急ぐべき理由はあくまで2.xのEOLです。
では今何をすべきか
状況別に整理するとこうです。
まだ2.xを運用中なら。 セキュリティパッチのないソフトウェアをスケジューラ権限で動かしている状態です。最初の一歩は3ではなく2.11.2です — そこでairflow config lint、ruff AIR301/AIR302、そして直接DBアクセスコードのインベントリという三つの計測をまず行ってください。計測なしにはこのマイグレーションの見積もり自体が出ません。標準オペレーターのproviderパッケージ移行のように2.xで先にやっておける作業も、この時点で一緒にやればよいでしょう。
SLA、SubDAG、直接DBアクセスに深く依存しているなら。 機能ごとの代替の状態をリリースノートで直接確認し、移行設計を先に行ってください。SLAはDeadline Alertsへの再設計が必要で(まだexperimental表記である点を含めて)、SubDAGはTaskGroup・assetスケジューリングへ、DBアクセスはPython Clientベースへ書き直す必要があります。この三つがなければ、マイグレーションは思ったより機械的です。
すでに3.xなら。 最新マイナーを追う半年周期のカレンダーを作り、airflow.exceptionsと旧serde経路から出るDeprecatedImportWarningを今のうちに潰しておいてください — Airflow 4で削除予定だとすでに告知されている項目です。
最後にレイヤーの区別を一つ。Airflowはバッチデータパイプラインの制御面であり、アプリケーションコードの耐久実行(durable execution)は別レイヤーの問題です — その話はTemporal Worker Versioning GA編で扱いました。オーケストレーターが指揮する実行エンジン側の最近の変化はPySpark 4.2のArrow UDFデフォルト化編を、オーケストレーションツール地形全体の比較はデータオーケストレーションガイドとワークフローエンジン2026編を参考にしてください。
おわりに
まとめるとこうです。Airflow 2は2026年4月22日付で終わり、最後のパッチは2.11.2(2026-03-14)でした。2から3への道はインポート置換ではなくアーキテクチャ転換です — ワーカーのDB直接アクセス除去が核であり、その上にexecution_date系の削除、クロンセマンティクスの変化、xcom_pullの動作変化のようなセマンティクスの地雷が乗っています。公式ツール(ruffのAIRルール、config lint/update、2.11ブリッジ)は機械的な部分をかなり減らしてくれますが、直接DBアクセスコードとSLA依存は設計をやり直さないと解けません。
そして3.xは到着地ではなく走り続ける列車です — マイナーは半年周期で出て前のマイナーのパッチは止まり、airflow.sdkへの移住は4.0での削除予告とともに続いています。だから計画は二つ必要です。2から3へ渡る一回限りのプロジェクト、そして渡った後に最新マイナーを追い続ける運用のリズム。前者を先延ばしにできる時間は、この4月で終わりました。
参考資料
- Upgrading to Airflow 3 — 公式アップグレードガイド
- Apache Airflow リリースノート (3.0.0-3.3.0、2.11.x)
- apache/airflow README — Version Life Cycle表
- apache/airflow GitHubリリース
- apache-airflow PyPIリリース履歴
- endoflife.date — Apache Airflow
- Ruff AIRルール (Airflowマイグレーションlint)
- apache/airflow-client-python — 公式Pythonクライアント
- issue #49187 — 直接DBアクセスコードの移行例集
현재 단락 (1/81)
2026年4月22日、Apache Airflow 2がEOL(サポート終了)を迎えました。プロジェクトREADMEの[バージョンライフサイクル表](https://github.com/apache...