Skip to main content

airflow

Airflow DAG patterns, KubernetesPodOperator, and debugging. Triggers on "dag", "airflow", "task", "operator", "KPO", "scheduler", "XCom".

Datos de origen

Repositorio
pypeaday/dotfiles
Última actividad en el origen
5 de octubre de 2026 a las 17:30
Idioma detectado de SKILL.md
inglés
Estrellas
2
Forks
0

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
airflow
description
Airflow DAG patterns, KubernetesPodOperator, and debugging. Triggers 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):** 1. Project dependency pins (`requirements.txt`, `constraints.txt`, `pyproject.toml`) 2. Deployed image/app version (Helm values, image tags) 3. Runtime confirmation: `airflow version` inside 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, task` instead of `airflow.models` - **Assets replace Datasets**: `Dataset` -> `Asset`, `DatasetEvent` -> `AssetEvent` - **No metadata DB access in tasks**: Use Airflow REST API or context instead - **`schedule_interval` removed**: Use unified `schedule` parameter - **`catchup=False` by default**: Explicit opt-in for backfills - **`logical_date=None` for manual/asset triggers**: No data interval for ad-hoc runs - **Standard operators moved**: `PythonOperator`, `BashOperator` now in `apache-airflow-providers-standard` - **Removed**: SubDAGs, SLAs, pickling, `execution_date` context variable ### When Writing DAGs - **Airflow 2**: Use legacy imports (`airflow.models`, `airflow.decorators`) - **Airflow 3**: Use `airflow.sdk` imports 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) ```python """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) ```python """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 ```python 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) ```python 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 ```python # 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")` requires `task_ids` parameter (no more implicit pulls) - **Airflow 2.x**: `xcom_pull()` without `task_ids` allowed but ambiguous (avoid) ## Asset-Based Scheduling (Airflow 3.x) Airflow 3.x renames Datasets to Assets and enhances event-driven scheduling. ```python 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) vs `triggering_dataset_events` (2.x) ## Anti-Patterns (What to Hunt) ### Critical (Both Versions) ```python # 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") ``` ```python # 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 ``` ```python # 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") ``` ```python # 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**: ```python # 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 ``` ```python # 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 ``` ```python # 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**: ```python # 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) ```python # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # Check parse times airflow dags report # Find slow DAGs (> 1s parse time is bad) # Optimize: remove top-level imports, reduce file count ``` ## Quick Commands ```bash # 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> ```
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub