ワンクリックで
airflow
Airflow DAG patterns, KubernetesPodOperator, and debugging. Use on 'dag', 'airflow', 'task', 'operator', 'KPO', 'scheduler', 'XCom'.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Airflow DAG patterns, KubernetesPodOperator, and debugging. Use on 'dag', 'airflow', 'task', 'operator', 'KPO', 'scheduler', 'XCom'.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Create and debug AWS IAM policies with least-privilege. Use on 'IAM policy', 'permission denied', 'access denied', 'not authorized', 'create role'.
Create architecture diagrams with draw.io MCP server. Use on 'diagram', 'architecture', 'drawio', 'flowchart', 'visualize'.
Git operations including commits, branches, worktrees, and safety protocols. Use on 'git', 'commit', 'branch', 'worktree', 'rebase', 'merge'.
Helm chart policies, bitnami legacy repo, and release management. Use on 'helm-skill' or 'load helm skill'.
Kubernetes security best practices for manifests and Helm charts. Use on 'k8s security', 'manifest audit', 'helm security review', 'pod hardening'.
Build Python/K8s/AWS with minimal code. Every line is a liability. Use on 'build', 'implement', 'add', 'create', 'deploy'. Prioritizes deletion, reuse, and configuration over new code.
| name | airflow |
| description | Airflow DAG patterns, KubernetesPodOperator, and debugging. Use on 'dag', 'airflow', 'task', 'operator', 'KPO', 'scheduler', 'XCom'. |
Minimal, production-grade Airflow patterns for Airflow 2 and 3.
Before proposing any changes, confirm the Airflow version:
Detection order (use first available):
requirements.txt, constraints.txt, pyproject.toml)airflow version inside scheduler/webserver podHard rules:
Docs rule:
https://airflow.apache.org/docs/apache-airflow/<major>.<minor>.*/stable/ docs unless explicitly on current stable releaseThis skill supports both Airflow 2.x and Airflow 3.x. Key differences:
from airflow.sdk import DAG, task instead of airflow.modelsDataset → Asset, DatasetEvent → AssetEventschedule_interval removed: Use unified schedule parametercatchup=False by default: Explicit opt-in for backfillslogical_date=None for manual/asset triggers: No data interval for ad-hoc runsPythonOperator, BashOperator now in apache-airflow-providers-standardexecution_date context variableairflow.models, airflow.decorators)airflow.sdk imports for forward compatibilityfrom airflow import __version__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
"""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()
"""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()
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,
)
Same as Airflow 3.x - no breaking changes to KubernetesPodOperator parameters.
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,
)
| 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.
# 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:
xcom_pull(key="key") requires task_ids parameter (no more implicit pulls)xcom_pull() without task_ids allowed but ambiguous (avoid)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:
from airflow.sdk import Assetfrom airflow.datasets import Datasettriggering_asset_events (3.x) vs triggering_dataset_events (2.x)# 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())
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
# Heavy imports at top
import tensorflow as tf # Slow import, every heartbeat
# Import inside task
@task
def train():
import tensorflow as tf
Version note: CLI commands same in 2.x and 3.x, but 3.x has airflow api-server instead of airflow webserver.
# 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
# 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
# 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
# Check parse times
airflow dags report
# Find slow DAGs (> 1s parse time is bad)
# Optimize: remove top-level imports, reduce file count
# 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>
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>
Before adding DAG code:
@task decorator instead of classic operator?catchup=False if backfill not needed?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
# 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.
airflow.sdk instead of airflow.models, airflow.decoratorsschedule_interval with scheduleexecution_date with dag_run.logical_date (handle None for manual triggers)xcom_pull(): Always specify task_ids parameterapache-airflow-providers-standard packagecatchup=False explicitly (if you rely on current behavior)airflow config lintruff check --select AIR30 --preview to find migration issuesfrom airflow import __version__
if __version__.startswith("3"):
from airflow.sdk import DAG, task
else:
from airflow import DAG
from airflow.decorators import task