
airflow
by pypeaday
le dots
SKILL.md
name: airflow description: "Airflow DAG patterns, KubernetesPodOperator, and debugging. Use on 'dag', 'airflow', 'task', 'operator', 'KPO', 'scheduler', 'XCom'."
Airflow Skill
Minimal, production-grade Airflow patterns for Airflow 2 and 3.
Version Detection (Must Run First)
Before proposing any changes, confirm the Airflow version:
Detection order (use first available):
- Project dependency pins (
requirements.txt,constraints.txt,pyproject.toml) - Deployed image/app version (Helm values, image tags)
- Runtime confirmation:
airflow versioninside scheduler/webserver pod
Hard rules:
- Do not mix constructs between Airflow major/minor versions
- Always validate guidance against the project's current Airflow version pin
- If you cannot determine the version, stop and ask one focused question
Docs rule:
- Prefer
https://airflow.apache.org/docs/apache-airflow/<major>.<minor>.* - Avoid
/stable/docs unless explicitly on current stable release
Version Support
This skill supports both Airflow 2.x and Airflow 3.x. Key differences:
Airflow 3.x Changes
- New import namespace: Use
from airflow.sdk import DAG, taskinstead ofairflow.models - Assets replace Datasets:
Dataset→Asset,DatasetEvent→AssetEvent - No metadata DB access in tasks: Use Airflow REST API or context instead
schedule_intervalremoved: Use unifiedscheduleparametercatchup=Falseby default: Explicit opt-in for backfillslogical_date=Nonefor manual/asset triggers: No data interval for ad-hoc runs- Standard operators moved:
PythonOperator,BashOperatornow inapache-airflow-providers-standard - Removed: SubDAGs, SLAs, pickling,
execution_datecontext variable
When Writing DAGs
- Airflow 2: Use legacy imports (
airflow.models,airflow.decorators) - Airflow 3: Use
airflow.sdkimports for forward compatibility - Check version with
from airflow import __version__
When to Use
- Writing new DAGs
- Debugging task failures
- Optimizing scheduler performance
- Configuring KubernetesPodOperator
- Managing connections and variables
Python's Zen Applied to DAGs
Simple is better than complex. -> Use TaskFlow over classic operators
Explicit is better than implicit. -> Name tasks clearly, document dependencies
Flat is better than nested. -> Avoid deep task groups unless necessary
Sparse is better than dense. -> One DAG per file, focused responsibility
Errors should never pass silently. -> Always set on_failure_callback
DAG Skeleton
Airflow 3.x (Recommended)
"""One-line description of what this DAG does."""
from datetime import datetime
from airflow.sdk import DAG, task
with DAG(
dag_id="my_dag",
start_date=datetime(2024, 1, 1),
schedule="@daily",
catchup=False, # Default in 3.x but explicit is better
tags=["team-name"],
default_args={"owner": "team", "retries": 1},
) as dag:
@task
def my_task() -> dict:
return {"status": "done"}
my_task()
Airflow 2.x (Legacy)
"""One-line description of what this DAG does."""
from datetime import datetime
from airflow import DAG
from airflow.decorators import task
with DAG(
dag_id="my_dag",
start_date=datetime(2024, 1, 1),
schedule_interval="@daily", # 2.x uses schedule_interval
catchup=False, # Must set explicitly in 2.x
tags=["team-name"],
default_args={"owner": "team", "retries": 1},
) as dag:
@task
def my_task() -> dict:
return {"status": "done"}
my_task()
KubernetesPodOperator
Airflow 3.x
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
KubernetesPodOperator(
task_id="job",
image="myimage:v1.0.0",
cmds=["python", "run.py"],
namespace="airflow",
get_logs=True,
is_delete_operator_pod=True,
)
Airflow 2.x
Same as Airflow 3.x - no breaking changes to KubernetesPodOperator parameters.
With Resources & IRSA (Both Versions)
from kubernetes.client import V1ResourceRequirements
KubernetesPodOperator(
task_id="job",
image="myimage:v1.0.0",
namespace="airflow",
service_account_name="my-irsa-sa",
container_resources=V1ResourceRequirements(
requests={"memory": "256Mi", "cpu": "100m"},
limits={"memory": "512Mi", "cpu": "200m"},
),
get_logs=True,
is_delete_operator_pod=True,
)
Common Operators
| Need | Airflow 3.x | Airflow 2.x |
|---|---|---|
| Run Python | @task decorator (airflow.sdk) | @task decorator (airflow.decorators) |
| Run container | KubernetesPodOperator | KubernetesPodOperator |
| Run bash | BashOperator (providers-standard) | BashOperator (airflow.operators.bash) |
| Wait for S3 | S3KeySensor | S3KeySensor |
| Wait for external | ExternalTaskSensor | ExternalTaskSensor |
| Run SQL | PostgresOperator, SnowflakeOperator | PostgresOperator, SnowflakeOperator |
| Call API | SimpleHttpOperator (providers-standard) | SimpleHttpOperator (airflow.operators.http) |
| Branching | @task.branch | @task.branch |
Note: Airflow 3.x moved standard operators (PythonOperator, BashOperator, EmailOperator, SimpleHttpOperator) to apache-airflow-providers-standard package.
XCom Patterns
# Return references, not data
@task
def extract() -> str:
s3.upload(data, "s3://bucket/output.parquet")
return "s3://bucket/output.parquet"
@task
def transform(path: str) -> str:
data = s3.download(path)
# ...
return "s3://bucket/transformed.parquet"
transform(extract())
Version Notes:
- Airflow 3.x:
xcom_pull(key="key")requirestask_idsparameter (no more implicit pulls) - Airflow 2.x:
xcom_pull()withouttask_idsallowed but ambiguous (avoid)
Asset-Based Scheduling (Airflow 3.x)
Airflow 3.x renames Datasets to Assets and enhances event-driven scheduling.
from airflow.sdk import DAG, task, Asset
# Define assets
raw_data = Asset("s3://bucket/raw/data.parquet")
clean_data = Asset("s3://bucket/clean/data.parquet")
# Producer DAG
with DAG(dag_id="producer", schedule="@daily") as producer_dag:
@task(outlets=[raw_data])
def extract():
# Produces raw_data asset
return {"status": "done"}
extract()
# Consumer DAG (triggered by asset)
with DAG(dag_id="consumer", schedule=[raw_data]) as consumer_dag:
@task(inlets=[raw_data], outlets=[clean_data])
def transform():
# Consumes raw_data, produces clean_data
return {"status": "done"}
transform()
Airflow 2.x equivalent: Use Dataset instead of Asset (same pattern).
Key differences:
- 3.x:
from airflow.sdk import Asset - 2.x:
from airflow.datasets import Dataset - Context variable:
triggering_asset_events(3.x) vstriggering_dataset_events(2.x)
Anti-Patterns (What to Hunt)
Critical (Both Versions)
# Top-level code (runs on every scheduler heartbeat)
import pandas as pd
df = pd.read_csv("data.csv") # RUNS AT PARSE TIME
# Move into task
@task
def process():
import pandas as pd
df = pd.read_csv("data.csv")
# Large XCom payloads
@task
def get_data():
return huge_dataframe.to_dict() # Stored in metadata DB!
# Use external storage
@task
def get_data():
s3.upload(data, "s3://bucket/data.parquet")
return "s3://bucket/data.parquet" # Return reference only
# Hardcoded connections
conn = psycopg2.connect(host="prod-db.example.com", password="secret")
# Use Airflow Connections
from airflow.hooks.postgres_hook import PostgresHook
hook = PostgresHook(postgres_conn_id="my_postgres")
# Dynamic unbounded tasks
for i in range(get_count_from_db()): # Unknown at parse time!
task(i)
# Use expand() for dynamic mapping with bounds
@task
def get_items():
return [1, 2, 3] # Bounded list
@task
def process(item):
pass
process.expand(item=get_items())
Version-Specific Anti-Patterns
Airflow 3.x:
# DON'T: Access metadata DB in tasks
from airflow.models import DagRun
dag_runs = DagRun.query.all() # FAILS - no DB access
# DO: Use Airflow REST API or context
from airflow import __version__
# Use requests to call Airflow API
# DON'T: Use execution_date (removed)
def my_task(**context):
date = context["execution_date"] # KeyError in 3.x
# DO: Use logical_date (or handle None for manual triggers)
def my_task(**context):
date = context["dag_run"].logical_date # May be None
# DON'T: Use schedule_interval (removed)
DAG(dag_id="my_dag", schedule_interval="@daily") # Fails in 3.x
# DO: Use schedule
DAG(dag_id="my_dag", schedule="@daily")
Airflow 2.x:
# DON'T: Use deprecated imports (still work but warn)
from airflow.operators.python import PythonOperator # Deprecated
# DO: Start using provider imports for 3.x readiness
from airflow.providers.standard.operators.python import PythonOperator
Performance (Both Versions)
# Heavy imports at top
import tensorflow as tf # Slow import, every heartbeat
# Import inside task
@task
def train():
import tensorflow as tf
Debugging Flow
Version note: CLI commands same in 2.x and 3.x, but 3.x has airflow api-server instead of airflow webserver.
1. DAG Not Appearing
# Check for import errors
airflow dags list-import-errors
# Validate DAG parsing/import via Airflow
airflow dags list
# Optional syntax/import sanity check (not execution)
python dags/my_dag.py
# Check scheduler logs
kubectl logs -l component=scheduler -n airflow --tail=100
2. Task Failing
# Get task logs
airflow tasks logs <dag_id> <task_id> <execution_date>
# Test task locally
airflow tasks test <dag_id> <task_id> <execution_date>
# For KPO: check pod logs
kubectl logs <pod-name> -n airflow
3. Task Stuck
# Check task state
airflow tasks state <dag_id> <task_id> <execution_date>
# Check for zombie tasks
airflow tasks clear <dag_id> -t <task_id> -s <start> -e <end>
# Check executor capacity
kubectl get pods -n airflow -l component=worker
4. Scheduler Slow
# Check parse times
airflow dags report
# Find slow DAGs (> 1s parse time is bad)
# Optimize: remove top-level imports, reduce file count
Quick Commands
# Validate DAG
airflow dags test <dag_id> <execution_date>
# Trigger DAG
airflow dags trigger <dag_id>
# Backfill
airflow dags backfill <dag_id> -s <start> -e <end>
# Clear tasks for re-run
airflow tasks clear <dag_id> -s <start> -e <end>
# List DAGs
airflow dags list
# Show DAG structure
airflow dags show <dag_id>
Response Format
When creating/modifying DAGs:
DAG: <dag_id>
Schedule: <schedule>
Tasks: <task1> -> <task2> -> <task3>
Dependencies: <new providers needed>
When debugging:
Symptom: <what's happening>
Root cause: <why>
Fix: <action>
Minimalism Checklist
Before adding DAG code:
- Can this be a sensor waiting for data instead of polling?
- Can this use existing operators instead of PythonOperator?
- Can this use
@taskdecorator instead of classic operator? - Is XCom payload a reference (path/URI) not data?
- Are all imports inside tasks (not top-level)?
- Is
catchup=Falseif backfill not needed?
Connections & Variables
Airflow 3.x:
# Get connection (Task SDK)
from airflow.sdk import Connection
conn = Connection.get("my_conn")
# Get variable (Task SDK)
from airflow.sdk import Variable
val = Variable.get("my_var")
# Get secret (if Secrets Backend configured)
val = Variable.get("my_secret") # Fetches from Secrets Manager
Airflow 2.x:
# Get connection
from airflow.hooks.base import BaseHook
conn = BaseHook.get_connection("my_conn")
# Get variable
from airflow.models import Variable
val = Variable.get("my_var")
# Get secret (if Secrets Backend configured)
val = Variable.get("my_secret") # Fetches from Secrets Manager
Task Dependencies
# Chain
task1 >> task2 >> task3
# Fan out
task1 >> [task2, task3]
# Fan in
[task1, task2] >> task3
# TaskFlow (implicit)
result = task2(task1())
Version notes: Dependency syntax identical across versions.
Migration Guide (2.x → 3.x)
High Priority
- Update imports:
airflow.sdkinstead ofairflow.models,airflow.decorators - Replace
schedule_intervalwithschedule - Replace
execution_datewithdag_run.logical_date(handleNonefor manual triggers) - Update Dataset → Asset references
- Remove DB access from task code (use Airflow API instead)
- Fix
xcom_pull(): Always specifytask_idsparameter
Medium Priority
- Update operator imports: Move to
apache-airflow-providers-standardpackage - Set
catchup=Falseexplicitly (if you rely on current behavior) - Remove SubDAGs: Replace with TaskGroups
- Remove SLA callbacks: Implement custom alerting
Low Priority
- Review deprecated config options with
airflow config lint - Use
ruff check --select AIR30 --previewto find migration issues - Test in Airflow 2.10+ before upgrading to 3.x
Quick Version Check
from airflow import __version__
if __version__.startswith("3"):
from airflow.sdk import DAG, task
else:
from airflow import DAG
from airflow.decorators import task
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です