๐ฅ Niveau 3 : Avancรฉ
๐ผ Advanced Orchestration pour Data Engineers
Bienvenue dans ce module avancรฉ oรน tu vas maรฎtriser l'orchestration de pipelines data ร l'รฉchelle. Tu apprendras ร dรฉployer Airflow sur Kubernetes, ร exploiter la TaskFlow API, ร comparer les orchestrateurs modernes, et ร mettre en place le data lineage avec OpenLineage โ des compรฉtences essentielles pour un Data Engineer Senior !
Prรฉrequis
| Niveau | Compรฉtence |
|---|---|
| โ Requis | Avoir suivi le module 12_orchestration_pipelines (Airflow basics) |
| โ Requis | Avoir suivi les modules 15_kubernetes_fundamentals et 27_kubernetes_deep_dive |
| โ Requis | Maรฎtriser les DAGs, Operators, XCom dans Airflow |
| โ Requis | Connaissances solides en Python et Docker |
| ๐ก Recommandรฉ | Un cluster K8s accessible (Minikube, kind, ou cloud) |
๐ฏ Objectifs du module
ร la fin de ce module, tu seras capable de :
- Dรฉployer et configurer Airflow sur Kubernetes
- Utiliser le KubernetesExecutor et le KubernetesPodOperator
- รcrire des DAGs modernes avec la TaskFlow API
- Implรฉmenter le Dynamic Task Mapping
- Comparer et choisir entre Airflow, Dagster et Prefect
- Mettre en place le data lineage avec OpenLineage
- Utiliser Astronomer (Astro CLI et Astro SDK) pour industrialiser
Rappel : Ce qu'on a vu vs Ce qu'on va approfondir
| Module M12 (Beginner) | Ce module M28 (Advanced) |
|---|---|
| Architecture Airflow basique | Airflow sur Kubernetes |
| DAGs, Operators simples | TaskFlow API, Dynamic Mapping |
| XCom manuel | XCom automatique avec TaskFlow |
| Mention des alternatives | Comparatif dรฉtaillรฉ + exemples de code |
| โ | OpenLineage (data lineage) |
| โ | Astronomer (plateforme enterprise) |
Schรฉma : De l'orchestration basique ร l'orchestration avancรฉe
M12 Orchestration Basics M28 Advanced Orchestration (ce module)
โโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โข Cron / Task Scheduler โ โ โข Airflow sur Kubernetes โ
โ โข DAGs basics โ โ โข KubernetesExecutor โ
โ โข Operators simples โ โโโโโโโถ โ โข TaskFlow API โ
โ โข XCom manuel โ โ โข Dynamic Task Mapping โ
โ โข SequentialExecutor โ โ โข Dagster, Prefect (comparatif) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โข OpenLineage (lineage) โ
โ โข Astronomer (enterprise) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ก Ce module est orientรฉ "production ร l'รฉchelle" โ tu vas apprendre ร faire tourner des centaines de DAGs avec des milliers de tรขches sur Kubernetes.
โน๏ธ Le savais-tu ?
Apache Airflow a รฉtรฉ crรฉรฉ par Airbnb en 2014 pour orchestrer leurs pipelines data. Il a รฉtรฉ donnรฉ ร la Apache Foundation en 2016.
Aujourd'hui, Airflow est utilisรฉ par des milliers d'entreprises dont Uber, Lyft, Twitter, Slack, Adobe, et bien d'autres.
Astronomer, fondรฉ en 2018, est devenu le leader des solutions Airflow managรฉes, levant plus de 200 millions de dollars et รฉtant le principal contributeur au projet open-source Airflow.
1. Airflow sur Kubernetes
Dรฉployer Airflow sur Kubernetes permet de bรฉnรฉficier de l'รฉlasticitรฉ, de l'isolation et de la scalabilitรฉ native de K8s.
Pourquoi Airflow sur K8s ?
| Aspect | Sans K8s (Celery/Local) | Avec K8s |
|---|---|---|
| Scaling | Workers fixes | Pods ร la demande |
| Isolation | Dรฉpendances partagรฉes | Chaque tรขche dans son pod |
| Ressources | Allocation statique | Requests/Limits par tรขche |
| Coรปt | Serveurs 24/7 | Pay-per-use (pods รฉphรฉmรจres) |
| Maintenance | Gรฉrer les workers | K8s gรจre tout |
Architecture Airflow sur Kubernetes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AIRFLOW ON KUBERNETES โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Namespace: airflow โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Scheduler โ โ Webserver โ โ Triggerer (Airflow 2) โ โ โ
โ โ โ (Pod) โ โ (Pod) โ โ (Pod) โ โ โ
โ โ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โ Crรฉe des pods pour chaque tรขche โ โ
โ โ โผ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Worker Pods (รฉphรฉmรจres) โ โ โ
โ โ โ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โ โ โ
โ โ โ โTask 1 โ โTask 2 โ โTask 3 โ โTask 4 โ โTask 5 โ โ โ โ
โ โ โ โ Pod โ โ Pod โ โ Pod โ โ Pod โ โ Pod โ โ โ โ
โ โ โ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ PostgreSQL โ โ DAGs (PVC/Git-Sync) โ โ โ
โ โ โ (Metadata DB) โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Les Executors Airflow
| Executor | Description | Quand l'utiliser |
|---|---|---|
| SequentialExecutor | 1 tรขche ร la fois | Dev/test uniquement |
| LocalExecutor | Parallรจle sur 1 machine | Petite production |
| CeleryExecutor | Workers Celery distribuรฉs | Production classique |
| KubernetesExecutor | 1 pod K8s par tรขche | Production K8s |
| CeleryKubernetesExecutor | Hybrid Celery + K8s | Workloads mixtes |
Installation avec le Helm Chart Officiel
# Ajouter le repo Helm officiel Airflow
helm repo add apache-airflow https://airflow.apache.org
helm repo update
# Crรฉer le namespace
kubectl create namespace airflow
# Installer Airflow avec KubernetesExecutor
helm install airflow apache-airflow/airflow \
--namespace airflow \
--set executor=KubernetesExecutor \
--set webserver.defaultUser.password=admin \
--set dags.persistence.enabled=true \
--set dags.gitSync.enabled=true \
--set dags.gitSync.repo=https://github.com/myorg/airflow-dags.git \
--set dags.gitSync.branch=main \
--set dags.gitSync.subPath=dags
# Vรฉrifier l'installation
kubectl get pods -n airflow
# Accรฉder au webserver
kubectl port-forward svc/airflow-webserver -n airflow 8080:8080
# http://localhost:8080 (admin / admin)
Configuration values.yaml avancรฉe
# values-production.yaml
executor: KubernetesExecutor
# Webserver
webserver:
replicas: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
# Scheduler
scheduler:
replicas: 2 # HA Scheduler (Airflow 2.0+)
resources:
requests:
cpu: "500m"
memory: "1Gi"
# Configuration KubernetesExecutor
config:
kubernetes:
# Namespace pour les worker pods
namespace: airflow
# Supprimer les pods aprรจs exรฉcution
delete_worker_pods: "True"
delete_worker_pods_on_failure: "False" # Garder pour debug
# Image par dรฉfaut pour les workers
worker_container_repository: apache/airflow
worker_container_tag: 2.8.0-python3.11
# Git-Sync pour les DAGs
dags:
persistence:
enabled: false
gitSync:
enabled: true
repo: git@github.com:myorg/airflow-dags.git
branch: main
subPath: dags
sshKeySecret: airflow-git-ssh-key
wait: 60 # Sync toutes les 60 secondes
# Logs dans un stockage externe
logs:
persistence:
enabled: true
size: 10Gi
# PostgreSQL (ou utiliser un service externe)
postgresql:
enabled: true
auth:
postgresPassword: airflow
username: airflow
password: airflow
database: airflow
KubernetesExecutor : Comment รงa marche
1. DAG est schedulรฉ
โ
โผ
2. Scheduler parse le DAG et identifie les tรขches ร exรฉcuter
โ
โผ
3. Pour chaque tรขche, le Scheduler crรฉe un Pod K8s
โ
โโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Pod Spec gรฉnรฉrรฉ automatiquement : โ
โ - Image: airflow (ou custom) โ
โ - Command: airflow tasks run ... โ
โ - Env: connexions, variables โ
โ - Resources: requests/limits โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
4. K8s schedule le pod sur un node
โ
โผ
5. La tรขche s'exรฉcute
โ
โผ
6. Pod terminรฉ โ supprimรฉ (si delete_worker_pods=True)
KubernetesPodOperator
Pour exรฉcuter des tรขches avec des images Docker personnalisรฉes :
from airflow import DAG
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
from datetime import datetime
with DAG(
dag_id="etl_with_custom_image",
start_date=datetime(2024, 1, 1),
schedule="@daily",
catchup=False,
) as dag:
# Tรขche avec image Spark
spark_job = KubernetesPodOperator(
task_id="run_spark_etl",
name="spark-etl-job",
namespace="airflow",
image="my-registry/spark-etl:1.0",
cmds=["spark-submit"],
arguments=[
"--master", "k8s://https://kubernetes.default.svc",
"/app/etl_job.py"
],
# Ressources
container_resources={
"requests": {"cpu": "1", "memory": "2Gi"},
"limits": {"cpu": "2", "memory": "4Gi"},
},
# Variables d'environnement
env_vars={
"SOURCE_PATH": "s3://bucket/raw/",
"DEST_PATH": "s3://bucket/processed/",
},
# Secrets
secrets=[
{"secret": "aws-credentials", "key": "AWS_ACCESS_KEY_ID", "env": "AWS_ACCESS_KEY_ID"},
{"secret": "aws-credentials", "key": "AWS_SECRET_ACCESS_KEY", "env": "AWS_SECRET_ACCESS_KEY"},
],
# Configuration K8s
is_delete_operator_pod=True,
get_logs=True,
startup_timeout_seconds=300,
# Affinity/Tolerations
affinity={
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
"matchExpressions": [{
"key": "node-type",
"operator": "In",
"values": ["compute"]
}]
}]
}
}
},
)
# Tรขche avec image Python custom
python_job = KubernetesPodOperator(
task_id="run_python_transform",
name="python-transform",
namespace="airflow",
image="my-registry/python-etl:2.0",
cmds=["python", "/app/transform.py"],
container_resources={
"requests": {"cpu": "500m", "memory": "1Gi"},
"limits": {"cpu": "1", "memory": "2Gi"},
},
is_delete_operator_pod=True,
get_logs=True,
)
spark_job >> python_job
pod_template_file : Personnalisation avancรฉe
Pour des configurations complexes, utiliser un template YAML :
# pod_template.yaml
apiVersion: v1
kind: Pod
metadata:
name: airflow-worker
labels:
app: airflow-worker
spec:
serviceAccountName: airflow-worker
containers:
- name: base
image: apache/airflow:2.8.0-python3.11
imagePullPolicy: IfNotPresent
env:
- name: AIRFLOW__CORE__EXECUTOR
value: "LocalExecutor"
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
volumeMounts:
- name: dags
mountPath: /opt/airflow/dags
readOnly: true
volumes:
- name: dags
persistentVolumeClaim:
claimName: airflow-dags
restartPolicy: Never
securityContext:
runAsUser: 50000
fsGroup: 50000
# Dans le DAG, rรฉfรฉrencer le template
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
task = KubernetesPodOperator(
task_id="task_with_template",
name="custom-task",
namespace="airflow",
pod_template_file="/opt/airflow/pod_templates/pod_template.yaml",
# Override l'image du template
image="my-custom-image:1.0",
)
2. TaskFlow API
La TaskFlow API (introduite dans Airflow 2.0) permet d'รฉcrire des DAGs de maniรจre Pythonic avec des dรฉcorateurs
@daget@task, en gรฉrant automatiquement les XComs.
Pourquoi TaskFlow ?
| Approche Traditionnelle | TaskFlow API |
|---|---|
PythonOperator(python_callable=fn) |
@task sur la fonction |
xcom_push / xcom_pull manuels |
Passage de donnรฉes automatique |
| Verbeux | Concis et lisible |
Dรฉpendances explicites >> |
Dรฉpendances implicites par appel |
Exemple comparatif
Avant (approche traditionnelle) :
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
def extract(**context):
data = {"users": 100, "orders": 500}
context['ti'].xcom_push(key='extracted_data', value=data)
return data
def transform(**context):
ti = context['ti']
data = ti.xcom_pull(task_ids='extract_task', key='extracted_data')
transformed = {"total": data['users'] + data['orders']}
ti.xcom_push(key='transformed_data', value=transformed)
return transformed
def load(**context):
ti = context['ti']
data = ti.xcom_pull(task_ids='transform_task', key='transformed_data')
print(f"Loading: {data}")
with DAG(
dag_id='traditional_etl',
start_date=datetime(2024, 1, 1),
schedule='@daily',
) as dag:
extract_task = PythonOperator(
task_id='extract_task',
python_callable=extract,
)
transform_task = PythonOperator(
task_id='transform_task',
python_callable=transform,
)
load_task = PythonOperator(
task_id='load_task',
python_callable=load,
)
extract_task >> transform_task >> load_task
Aprรจs (TaskFlow API) :
from airflow.decorators import dag, task
from datetime import datetime
@dag(
dag_id='taskflow_etl',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def taskflow_etl():
@task
def extract() -> dict:
return {"users": 100, "orders": 500}
@task
def transform(data: dict) -> dict:
return {"total": data['users'] + data['orders']}
@task
def load(data: dict):
print(f"Loading: {data}")
# Dรฉpendances implicites par appel de fonction !
raw_data = extract()
transformed_data = transform(raw_data)
load(transformed_data)
# Instancier le DAG
taskflow_etl()
Avantages TaskFlow
| Avantage | Description |
|---|---|
| Code Pythonic | Ressemble ร du Python standard |
| XCom automatique | Les retours de fonction sont automatiquement passรฉs |
| Type hints | Support des annotations de type |
| Moins de boilerplate | Pas de PythonOperator explicite |
| Dรฉpendances claires | Le flux de donnรฉes dรฉfinit les dรฉpendances |
TaskFlow avec plusieurs outputs
from airflow.decorators import dag, task
from datetime import datetime
@dag(
dag_id='taskflow_multiple_outputs',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def etl_pipeline():
@task(multiple_outputs=True)
def extract() -> dict:
"""Retourne plusieurs valeurs nommรฉes"""
return {
"users": [{"id": 1, "name": "Alice"}],
"orders": [{"id": 101, "amount": 99.99}],
"metadata": {"source": "api", "timestamp": "2024-01-01"}
}
@task
def process_users(users: list) -> list:
return [u['name'].upper() for u in users]
@task
def process_orders(orders: list) -> float:
return sum(o['amount'] for o in orders)
@task
def combine(users: list, total: float, metadata: dict):
print(f"Users: {users}")
print(f"Total orders: {total}")
print(f"Source: {metadata['source']}")
# Extraire les donnรฉes
data = extract()
# Accรฉder aux outputs individuels
processed_users = process_users(data['users'])
orders_total = process_orders(data['orders'])
# Combiner
combine(processed_users, orders_total, data['metadata'])
etl_pipeline()
Mixing TaskFlow avec des Operators classiques
from airflow.decorators import dag, task
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
from airflow.operators.bash import BashOperator
from datetime import datetime
@dag(
dag_id='mixed_taskflow',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def mixed_pipeline():
# TaskFlow task
@task
def prepare_config() -> dict:
return {"batch_size": 1000, "date": "2024-01-01"}
# Operator classique
run_spark = KubernetesPodOperator(
task_id="run_spark",
name="spark-job",
namespace="airflow",
image="apache/spark:3.5.0",
cmds=["spark-submit", "/app/job.py"],
)
# TaskFlow task qui dรฉpend d'un Operator
@task
def validate_output():
print("Validating Spark output...")
return True
# Bash operator
notify = BashOperator(
task_id="notify",
bash_command="echo 'Pipeline completed!'",
)
# Dรฉfinir les dรฉpendances
config = prepare_config()
config >> run_spark >> validate_output() >> notify
mixed_pipeline()
3. Dynamic Task Mapping
Le Dynamic Task Mapping (Airflow 2.3+) permet de crรฉer un nombre variable de tรขches ร runtime, basรฉ sur les donnรฉes.
Pourquoi Dynamic Mapping ?
| Problรจme | Solution |
|---|---|
| Nombre de fichiers inconnu ร l'avance | .expand() sur la liste de fichiers |
| Traiter N partitions dynamiquement | Map sur les partitions |
| Parallรฉliser sur une liste variable | Crรฉer N tรขches automatiquement |
Schรฉma Dynamic Mapping
โโโโโโโโโโโโโโโโโโโ
โ list_files() โ
โ [f1, f2, f3, f4]โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ process(f1) โ โ process(f2) โ โ process(f3) โ ...
โ [mapped] โ โ [mapped] โ โ [mapped] โ
โโโโโโโโโฌโโโโโโโโ โโโโโโโโโฌโโโโโโโโ โโโโโโโโโฌโโโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโ
โ aggregate() โ
โ Combine results โ
โโโโโโโโโโโโโโโโโโโ
Exemple : Traiter des fichiers dynamiquement
from airflow.decorators import dag, task
from datetime import datetime
@dag(
dag_id='dynamic_file_processing',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def process_files_dynamically():
@task
def list_files() -> list[str]:
"""Liste les fichiers ร traiter (nombre variable)"""
# En rรฉalitรฉ : lister depuis S3, GCS, etc.
return [
"s3://bucket/data/file1.parquet",
"s3://bucket/data/file2.parquet",
"s3://bucket/data/file3.parquet",
"s3://bucket/data/file4.parquet",
]
@task
def process_file(file_path: str) -> dict:
"""Traite UN fichier โ sera mappรฉ dynamiquement"""
print(f"Processing: {file_path}")
# Simuler le traitement
row_count = len(file_path) * 100 # Fake
return {"file": file_path, "rows": row_count}
@task
def aggregate_results(results: list[dict]) -> dict:
"""Agrรจge tous les rรฉsultats"""
total_rows = sum(r['rows'] for r in results)
return {
"files_processed": len(results),
"total_rows": total_rows,
}
# Rรฉcupรฉrer la liste de fichiers
files = list_files()
# ๐ฏ DYNAMIC MAPPING : .expand() crรฉe N tรขches
processed = process_file.expand(file_path=files)
# Agrรฉger les rรฉsultats
aggregate_results(processed)
process_files_dynamically()
expand() avec plusieurs paramรจtres
from airflow.decorators import dag, task
from datetime import datetime
@dag(
dag_id='dynamic_multi_param',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def multi_param_mapping():
@task
def get_partitions() -> list[dict]:
return [
{"date": "2024-01-01", "region": "EU"},
{"date": "2024-01-01", "region": "US"},
{"date": "2024-01-02", "region": "EU"},
{"date": "2024-01-02", "region": "US"},
]
@task
def process_partition(date: str, region: str) -> dict:
print(f"Processing {region} for {date}")
return {"date": date, "region": region, "status": "done"}
partitions = get_partitions()
# expand_kwargs pour mapper plusieurs paramรจtres
process_partition.expand_kwargs(partitions)
multi_param_mapping()
Limiter le parallรฉlisme
@task(max_active_tis_per_dag=5) # Max 5 instances en parallรจle
def process_file(file_path: str) -> dict:
# ...
pass
Mapping sur un Operator (non-TaskFlow)
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime
with DAG(
dag_id='dynamic_bash',
start_date=datetime(2024, 1, 1),
schedule='@daily',
) as dag:
# Liste statique (ou XCom d'une tรขche prรฉcรฉdente)
files = ["file1.csv", "file2.csv", "file3.csv"]
process = BashOperator.partial(
task_id="process_files",
).expand(
bash_command=[f"python process.py {f}" for f in files]
)
4. Airflow vs Dagster vs Prefect
Il existe plusieurs orchestrateurs modernes. Voici une comparaison approfondie.
Vue d'ensemble
| Critรจre | Airflow | Dagster | Prefect |
|---|---|---|---|
| Crรฉรฉ par | Airbnb (2014) | Elementl (2018) | Prefect (2018) |
| Philosophie | DAG-centric | Asset-centric | Flow-centric |
| Maturitรฉ | โญโญโญโญโญ | โญโญโญโญ | โญโญโญโญ |
| Communautรฉ | Trรจs large | En croissance | En croissance |
| Adoption | Standard industrie | Startups, ML | Startups, Data |
| Learning curve | Moyenne | Plus raide | Plus douce |
Comparaison dรฉtaillรฉe
| Aspect | Airflow | Dagster | Prefect |
|---|---|---|---|
| Dรฉfinition | DAGs Python | Assets + Ops | Flows + Tasks |
| Scheduling | Cron-like | Cron + Sensors | Cron + Events |
| Data Lineage | Via OpenLineage | Natif (Assets) | Via intรฉgrations |
| Testing | Difficile | Excellent | Bon |
| Type checking | Non natif | Natif (I/O types) | Pydantic |
| UI | Fonctionnelle | Moderne | Moderne |
| Local dev | Complexe | Excellent | Excellent |
| Cloud offering | MWAA, Composer, Astronomer | Dagster Cloud | Prefect Cloud |
Exemples de code comparรฉs
Mรชme pipeline dans les 3 outils :
Airflow (TaskFlow)
from airflow.decorators import dag, task
from datetime import datetime
@dag(
dag_id='etl_pipeline',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def etl_pipeline():
@task
def extract() -> dict:
return {"data": [1, 2, 3, 4, 5]}
@task
def transform(raw: dict) -> dict:
return {"data": [x * 2 for x in raw['data']]}
@task
def load(transformed: dict):
print(f"Loading: {transformed}")
raw = extract()
transformed = transform(raw)
load(transformed)
etl_pipeline()
Dagster (Asset-based)
from dagster import asset, Definitions, define_asset_job
@asset
def raw_data() -> dict:
"""Asset: donnรฉes brutes"""
return {"data": [1, 2, 3, 4, 5]}
@asset
def transformed_data(raw_data: dict) -> dict:
"""Asset: donnรฉes transformรฉes (dรฉpend de raw_data)"""
return {"data": [x * 2 for x in raw_data['data']]}
@asset
def loaded_data(transformed_data: dict) -> None:
"""Asset: donnรฉes chargรฉes"""
print(f"Loading: {transformed_data}")
# Job pour exรฉcuter tous les assets
etl_job = define_asset_job("etl_job", selection="*")
# Dรฉfinitions Dagster
defs = Definitions(
assets=[raw_data, transformed_data, loaded_data],
jobs=[etl_job],
)
Dagster : Concepts clรฉs
| Concept | Description |
|---|---|
| Asset | Objet de donnรฉes persistant (table, fichier) |
| Op | Unitรฉ de calcul (comme un Operator) |
| Graph | Composition d'Ops |
| Job | Graph exรฉcutable avec config |
| Resource | Connexion externe (DB, S3) |
| I/O Manager | Gรจre la persistance des assets |
Prefect (Flow-based)
from prefect import flow, task
@task
def extract() -> dict:
return {"data": [1, 2, 3, 4, 5]}
@task
def transform(raw: dict) -> dict:
return {"data": [x * 2 for x in raw['data']]}
@task
def load(transformed: dict):
print(f"Loading: {transformed}")
@flow(name="ETL Pipeline")
def etl_pipeline():
raw = extract()
transformed = transform(raw)
load(transformed)
# Exรฉcuter
if __name__ == "__main__":
etl_pipeline()
Prefect : Concepts clรฉs
| Concept | Description |
|---|---|
| Flow | Pipeline de tรขches |
| Task | Unitรฉ de travail |
| Deployment | Flow dรฉployรฉ et schedulable |
| Work Pool | Groupe de workers |
| Block | Credentials et configs rรฉutilisables |
Quand utiliser quoi ?
| Scรฉnario | Recommandation | Pourquoi |
|---|---|---|
| Grande entreprise, รฉquipe mature | Airflow | Standard, trรจs documentรฉ, รฉcosystรจme large |
| Pipelines ML avec assets | Dagster | Asset-centric, excellent testing, types |
| Startup, itรฉration rapide | Prefect | Simple, moderne, local dev facile |
| Dรฉjร sur Airflow | Rester sur Airflow | Migration coรปteuse, TaskFlow moderne |
| Nouveau projet, รฉquipe data | Dagster ou Prefect | Approches modernes |
| K8s natif requis | Airflow ou Dagster | KubernetesExecutor mature |
Tableau de dรฉcision
Complexitรฉ du pipeline
Faible โโโโโโโโโโโโโโโโโโโบ รlevรฉe
โ โ
Taille รฉquipe โ โ
โ โ โ
Petite โโโโโโโโโโโโผโโโบ Prefect โโโโโโโโโโโบโ Dagster
โ โ โ
โ โ โ
Grande โโโโโโโโโโโโโผโโโบ Prefect โโโโโโโโโโโบโ Airflow
โ โ ou Dagster โ
โผ โ โ
5. OpenLineage โ Data Lineage
OpenLineage est un standard ouvert pour collecter et partager les mรฉtadonnรฉes de lineage des pipelines data. Il rรฉpond ร la question : "D'oรน viennent mes donnรฉes et oรน vont-elles ?"
Pourquoi le Data Lineage ?
| Question | Lineage rรฉpond |
|---|---|
| D'oรน viennent les donnรฉes de ce dashboard ? | โ Traรงabilitรฉ amont |
| Si cette table change, quoi d'autre est impactรฉ ? | โ Impact analysis |
| Ce job a รฉchouรฉ, quelles donnรฉes sont corrompues ? | โ Root cause analysis |
| Sommes-nous conformes RGPD ? | โ Audit et gouvernance |
Schรฉma OpenLineage
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ OPENLINEAGE ECOSYSTEM โ
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Airflow โ โ Spark โ โ dbt โ โ Flink โ โ
โ โ + OL Pluginโ โ + OL Lib โ โ + OL Pluginโ โ + OL Lib โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โ
โ โ โ โ โ โ
โ โ OpenLineage Events (JSON) โ โ โ
โ โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ OpenLineage Backend โ โ
โ โ (Marquez, Atlan, DataHub...) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Lineage Visualization โ โ
โ โ Impact Analysis โ โ
โ โ Data Catalog โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Concepts OpenLineage
| Concept | Description | Exemple |
|---|---|---|
| Job | Pipeline ou transformation | DAG Airflow, job Spark |
| Run | Exรฉcution d'un job | DAG run avec ID unique |
| Dataset | Source ou destination de donnรฉes | Table SQL, fichier S3 |
| Facet | Mรฉtadonnรฉes additionnelles | Schema, stats, owner |
Activer OpenLineage dans Airflow
# Installer le provider
pip install apache-airflow-providers-openlineage
# airflow.cfg ou variables d'environnement
[openlineage]
transport = '{"type": "http", "url": "http://marquez:5000", "endpoint": "api/v1/lineage"}'
namespace = "my_airflow_instance"
Ou via environnement :
export AIRFLOW__OPENLINEAGE__TRANSPORT='{"type": "http", "url": "http://marquez:5000", "endpoint": "api/v1/lineage"}'
export AIRFLOW__OPENLINEAGE__NAMESPACE="my_airflow_instance"
Marquez : Backend OpenLineage
Marquez est le backend de rรฉfรฉrence pour OpenLineage (open-source par WeWork/Linux Foundation).
# Dรฉployer Marquez avec Docker Compose
git clone https://github.com/MarquezProject/marquez.git
cd marquez
docker-compose up -d
# UI disponible sur http://localhost:3000
# API sur http://localhost:5000
DAG avec lineage explicite
from airflow.decorators import dag, task
from airflow.lineage.entities import Table, File
from datetime import datetime
@dag(
dag_id='etl_with_lineage',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def etl_with_lineage():
@task(
inlets=[File(url="s3://bucket/raw/")],
outlets=[Table(cluster="warehouse", database="analytics", name="staging_orders")],
)
def extract_and_stage():
"""Extract depuis S3 et charge dans staging"""
print("Extracting from S3 to staging...")
@task(
inlets=[Table(cluster="warehouse", database="analytics", name="staging_orders")],
outlets=[Table(cluster="warehouse", database="analytics", name="fact_orders")],
)
def transform_to_fact():
"""Transforme staging en fact table"""
print("Transforming to fact table...")
extract_and_stage() >> transform_to_fact()
etl_with_lineage()
Lineage automatique avec certains Operators
Certains operators extraient automatiquement le lineage :
| Operator | Lineage auto |
|---|---|
SnowflakeOperator |
โ (parse SQL) |
BigQueryOperator |
โ |
PostgresOperator |
โ |
SparkSubmitOperator |
โ (avec Spark OL) |
PythonOperator |
โ (manuel) |
Alternatives ร Marquez
| Outil | Type | Description |
|---|---|---|
| Marquez | Open-source | Backend de rรฉfรฉrence |
| DataHub | Open-source | Catalogue + lineage (LinkedIn) |
| Atlan | SaaS | Plateforme data catalog complรจte |
| Collibra | Enterprise | Gouvernance et lineage |
| Alation | Enterprise | Data catalog |
6. Astronomer โ Airflow Enterprise
Astronomer est la plateforme enterprise pour Apache Airflow. Elle fournit des outils, un hosting managรฉ, et des SDKs pour industrialiser Airflow.
Pourquoi Astronomer ?
| Problรจme | Solution Astronomer |
|---|---|
| Installer/maintenir Airflow est complexe | Astro Cloud (fully managed) |
| Dev local difficile | Astro CLI (environment identique) |
| รcrire du code Airflow verbeux | Astro SDK (API simplifiรฉe) |
| Pas de support | Support enterprise 24/7 |
รcosystรจme Astronomer
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ASTRONOMER ECOSYSTEM โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Astro Cloud โ โ
โ โ โข Airflow fully managed โ โ
โ โ โข Auto-scaling โ โ
โ โ โข Observability intรฉgrรฉe โ โ
โ โ โข CI/CD intรฉgrรฉ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Astro CLI โ โ Astro SDK โ โ
โ โ โข Dev local โ โ โข API Pythonic โ โ
โ โ โข Deploy en 1 cmd โ โ โข SQL/Python tasks โ โ
โ โ โข Tests intรฉgrรฉs โ โ โข Data quality โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Astronomer Registry โ โ
โ โ โข Providers certifiรฉs โ โ
โ โ โข DAGs de rรฉfรฉrence โ โ
โ โ โข Documentation enrichie โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Astro CLI
L'outil en ligne de commande pour dรฉvelopper et dรฉployer Airflow.
# Installation
# macOS
brew install astro
# Linux
curl -sSL install.astronomer.io | sudo bash -s
# Windows (WSL)
curl -sSL install.astronomer.io | sudo bash -s
# Crรฉer un nouveau projet Airflow
mkdir my-airflow-project && cd my-airflow-project
astro dev init
# Structure gรฉnรฉrรฉe :
# .
# โโโ dags/ # Tes DAGs
# โ โโโ example_dag.py
# โโโ include/ # Fichiers additionnels
# โโโ plugins/ # Plugins custom
# โโโ tests/ # Tests
# โโโ Dockerfile # Image Airflow custom
# โโโ packages.txt # Packages systรจme
# โโโ requirements.txt # Dรฉpendances Python
# โโโ airflow_settings.yaml # Variables, connexions
# Dรฉmarrer l'environnement local
astro dev start
# UI sur http://localhost:8080 (admin/admin)
# Voir les logs
astro dev logs
# Exรฉcuter les tests
astro dev pytest
# Parser les DAGs (vรฉrifier les erreurs)
astro dev parse
# Arrรชter
astro dev stop
# Dรฉployer sur Astro Cloud
astro deploy
Astro SDK
Une API Python simplifiรฉe pour รฉcrire des DAGs, particuliรจrement pour les opรฉrations SQL/dataframe.
pip install astro-sdk-python
Exemple : ETL SQL simplifiรฉ
from datetime import datetime
from airflow.decorators import dag
from astro import sql as aql
from astro.files import File
from astro.sql.table import Table
@dag(
dag_id='astro_sdk_etl',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
)
def astro_sdk_etl():
# Charger un CSV dans une table
raw_orders = aql.load_file(
input_file=File("s3://bucket/orders.csv"),
output_table=Table(
name="raw_orders",
conn_id="postgres_conn",
),
)
# Transformation SQL (inline)
@aql.transform
def transform_orders(input_table: Table):
return """
SELECT
order_id,
customer_id,
order_date,
amount,
amount * 1.2 as amount_with_tax
FROM {{ input_table }}
WHERE amount > 0
"""
# Appliquer la transformation
transformed = transform_orders(
input_table=raw_orders,
output_table=Table(
name="transformed_orders",
conn_id="postgres_conn",
),
)
# Exporter vers S3
aql.export_to_file(
input_data=transformed,
output_file=File("s3://bucket/processed/orders.parquet"),
if_exists="replace",
)
# Nettoyer les tables temporaires
aql.cleanup()
astro_sdk_etl()
Fonctionnalitรฉs Astro SDK
| Fonction | Description |
|---|---|
aql.load_file() |
Charger CSV/Parquet/JSON dans une table |
aql.transform() |
SQL transformation avec Jinja |
aql.run_raw_sql() |
Exรฉcuter du SQL brut |
aql.export_to_file() |
Exporter une table vers fichier |
aql.merge() |
Merge/Upsert entre tables |
aql.append() |
Append data to table |
aql.dataframe() |
Transformer avec Pandas |
aql.cleanup() |
Supprimer les tables temporaires |
Astro SDK : Transformation DataFrame
from astro import sql as aql
from astro.sql.table import Table
import pandas as pd
@aql.dataframe
def process_with_pandas(df: pd.DataFrame) -> pd.DataFrame:
"""Transformation avec Pandas"""
df['total'] = df['quantity'] * df['price']
df['processed_at'] = pd.Timestamp.now()
return df
# Dans le DAG :
processed = process_with_pandas(
df=some_table,
output_table=Table(name="processed", conn_id="postgres"),
)
Astro Cloud vs Self-Hosted
| Aspect | Astro Cloud | Self-Hosted (Helm) |
|---|---|---|
| Setup | Minutes | Heures/jours |
| Maintenance | Astronomer | Ton รฉquipe |
| Scaling | Automatique | Manuel |
| Updates | Automatiques | Manuels |
| Coรปt | Par usage | Infrastructure |
| Contrรดle | Moyen | Total |
| Sรฉcuritรฉ | SOC 2, HIPAA | ร implรฉmenter |
7. Exercices Pratiques
Exercice 1 : DAG TaskFlow avec Dynamic Mapping
Objectif : Crรฉer un DAG qui traite dynamiquement une liste de fichiers.
Instructions :
- Crรฉer une tรขche
list_files()qui retourne une liste de chemins - Crรฉer une tรขche
process_file(path)mappรฉe dynamiquement - Crรฉer une tรขche
report(results)qui agrรจge les rรฉsultats - Limiter ร 3 tรขches parallรจles maximum
๐ก Indice
Utilise @task(max_active_tis_per_dag=3) et .expand()
Exercice 2 : KubernetesPodOperator avec Spark
Objectif : Crรฉer un DAG qui exรฉcute un job Spark via KubernetesPodOperator.
Instructions :
- Utiliser l'image
apache/spark:3.5.0 - Configurer les ressources : 2 CPU, 4Gi RAM
- Passer des variables d'environnement pour la config
- Utiliser une node affinity pour cibler les nodes
compute
Exercice 3 : Comparatif Airflow/Dagster/Prefect
Objectif : Implรฉmenter le mรชme pipeline simple dans les 3 outils.
Pipeline :
- Lire un fichier JSON
- Filtrer les enregistrements (amount > 100)
- Calculer une somme
- รcrire le rรฉsultat
Comparer :
- Nombre de lignes de code
- Facilitรฉ de test local
- Lisibilitรฉ
Exercice 4 : OpenLineage avec Marquez
Objectif : Configurer OpenLineage et visualiser le lineage.
Instructions :
- Dรฉployer Marquez en local (Docker Compose)
- Configurer Airflow pour envoyer les events ร Marquez
- Crรฉer un DAG avec
inletsetoutletsexplicites - Visualiser le lineage dans l'UI Marquez
Exercice 5 : Astro CLI Project
Objectif : Crรฉer un projet Airflow complet avec Astro CLI.
Instructions :
- Initialiser un projet avec
astro dev init - Crรฉer un DAG utilisant Astro SDK
- Ajouter des tests dans
/tests - Valider avec
astro dev pytest
8. Mini-Projet : Pipeline Data Production-Ready
Objectif
Crรฉer un pipeline ETL production-ready dรฉployรฉ sur Kubernetes avec monitoring et lineage.
Architecture cible
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MINI-PROJET M28 โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Airflow sur Kubernetes โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ DAG: data_pipeline_advanced โ โ โ
โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ Extract โโโโโถโ Transform โโโโโถโ Load โ โ โ โ
โ โ โ โ (K8sPod)โ โ (TaskFlow) โ โ (Astro SDK) โ โ โ โ
โ โ โ โโโโโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ โ
โ โ โ Dynamic Mapping (N fichiers) โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Scheduler โ โ Webserver โ โ Worker Pods (K8s) โ โ โ
โ โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ OpenLineage Events โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Marquez โ โ
โ โ (Data Lineage Visualization) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Instructions
รtape 1 : Setup Astro Project
mkdir advanced-pipeline && cd advanced-pipeline
astro dev init
รtape 2 : Crรฉer le DAG principal
Le DAG doit :
- Lister des fichiers depuis S3 (simulรฉ)
- Traiter chaque fichier avec Dynamic Mapping
- Utiliser TaskFlow API
- Avoir des inlets/outlets pour le lineage
รtape 3 : Configurer OpenLineage
- Ajouter la config dans
airflow_settings.yaml - Vรฉrifier que les events arrivent dans Marquez
รtape 4 : Tests
- รcrire des tests unitaires pour les tรขches
- Valider avec
astro dev pytest
รtape 5 : Dรฉployer (optionnel)
- Dรฉployer sur un cluster K8s avec le Helm chart
- Ou utiliser Astro Cloud
โ Solution du mini-projet
๐ฅ Afficher la solution complรจte
1. Structure du projet Astro
advanced-pipeline/
โโโ dags/
โ โโโ data_pipeline_advanced.py
โโโ include/
โ โโโ sql/
โ โโโ transform.sql
โโโ tests/
โ โโโ test_pipeline.py
โโโ Dockerfile
โโโ requirements.txt
โโโ airflow_settings.yaml
2. requirements.txt
astro-sdk-python>=1.5.0
apache-airflow-providers-openlineage>=1.0.0
apache-airflow-providers-cncf-kubernetes>=7.0.0
pandas>=2.0.0
3. airflow_settings.yaml
airflow:
connections:
- conn_id: postgres_conn
conn_type: postgres
host: postgres
login: airflow
password: airflow
schema: airflow
port: 5432
variables:
- variable_name: data_bucket
variable_value: s3://my-data-bucket
# OpenLineage config
openlineage:
transport: '{"type": "http", "url": "http://marquez:5000", "endpoint": "api/v1/lineage"}'
namespace: advanced-pipeline
4. dags/data_pipeline_advanced.py
from datetime import datetime
from airflow.decorators import dag, task
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
from airflow.lineage.entities import File, Table
from astro import sql as aql
from astro.sql.table import Table as AstroTable
import pandas as pd
@dag(
dag_id='data_pipeline_advanced',
start_date=datetime(2024, 1, 1),
schedule='@daily',
catchup=False,
tags=['production', 'etl'],
doc_md="""## Pipeline Data Avancรฉ
Ce pipeline:
1. Liste les fichiers ร traiter
2. Extrait chaque fichier (Dynamic Mapping)
3. Transforme avec Astro SDK
4. Charge dans PostgreSQL
""",
)
def data_pipeline_advanced():
# --- รTAPE 1 : Lister les fichiers ---
@task
def list_files() -> list[str]:
"""Liste les fichiers ร traiter (simulรฉ)"""
# En production: lister depuis S3
return [
"orders_2024_01.parquet",
"orders_2024_02.parquet",
"orders_2024_03.parquet",
]
# --- รTAPE 2 : Extraire chaque fichier ---
@task(
max_active_tis_per_dag=3,
inlets=[File(url="s3://bucket/raw/")],
)
def extract_file(file_name: str) -> dict:
"""Extrait un fichier (simulรฉ)"""
print(f"Extracting: {file_name}")
# Simuler des donnรฉes
return {
"file": file_name,
"rows": 1000,
"data": [
{"order_id": 1, "amount": 100},
{"order_id": 2, "amount": 200},
]
}
# --- รTAPE 3 : Transformer avec Pandas ---
@task(
outlets=[Table(cluster="warehouse", database="analytics", name="orders_processed")],
)
def transform_data(extracted_list: list[dict]) -> list[dict]:
"""Agrรจge et transforme les donnรฉes"""
all_data = []
for extracted in extracted_list:
for record in extracted['data']:
record['source_file'] = extracted['file']
record['amount_with_tax'] = record['amount'] * 1.2
all_data.append(record)
print(f"Transformed {len(all_data)} records")
return all_data
# --- รTAPE 4 : Charger dans PostgreSQL ---
@aql.dataframe
def load_to_postgres(data: list[dict]) -> pd.DataFrame:
"""Charge dans PostgreSQL via Astro SDK"""
df = pd.DataFrame(data)
df['loaded_at'] = pd.Timestamp.now()
return df
# --- รTAPE 5 : Rapport final ---
@task
def generate_report(row_count: int):
"""Gรฉnรจre un rapport"""
print(f"Pipeline completed. Total rows: {row_count}")
# --- ORCHESTRATION ---
files = list_files()
# Dynamic mapping sur les fichiers
extracted = extract_file.expand(file_name=files)
# Transformer
transformed = transform_data(extracted)
# Charger
loaded = load_to_postgres(
data=transformed,
output_table=AstroTable(
name="orders_processed",
conn_id="postgres_conn",
),
)
# Rapport
generate_report(len(transformed))
# Cleanup Astro SDK
aql.cleanup()
# Instancier le DAG
data_pipeline_advanced()
5. tests/test_pipeline.py
import pytest
from dags.data_pipeline_advanced import data_pipeline_advanced
def test_dag_loads():
"""Test que le DAG se charge sans erreur"""
dag = data_pipeline_advanced()
assert dag is not None
assert dag.dag_id == "data_pipeline_advanced"
def test_dag_has_expected_tasks():
"""Test que le DAG a les bonnes tรขches"""
dag = data_pipeline_advanced()
task_ids = [t.task_id for t in dag.tasks]
assert "list_files" in task_ids
assert "transform_data" in task_ids
def test_extract_file():
"""Test unitaire de la fonction extract"""
# Import direct de la fonction
from dags.data_pipeline_advanced import extract_file
# Appeler la fonction wrapped
result = extract_file.function("test.parquet")
assert "file" in result
assert "rows" in result
assert result["file"] == "test.parquet"
6. Commandes pour tester
# Dรฉmarrer l'environnement
astro dev start
# Parser les DAGs
astro dev parse
# Lancer les tests
astro dev pytest
# Voir l'UI
open http://localhost:8080
# Dรฉclencher le DAG
astro dev run dags trigger data_pipeline_advanced
7. docker-compose.override.yml (pour Marquez)
version: "3"
services:
marquez:
image: marquezproject/marquez:latest
ports:
- "5000:5000"
- "5001:5001"
environment:
- MARQUEZ_PORT=5000
- MARQUEZ_ADMIN_PORT=5001
marquez-web:
image: marquezproject/marquez-web:latest
ports:
- "3000:3000"
environment:
- MARQUEZ_HOST=marquez
- MARQUEZ_PORT=5000
๐ Ressources pour aller plus loin
๐ Documentation officielle
- Apache Airflow Docs โ Documentation Airflow
- Airflow Helm Chart โ Dรฉploiement K8s
- Dagster Docs โ Documentation Dagster
- Prefect Docs โ Documentation Prefect
- OpenLineage โ Standard de lineage
- Astronomer Docs โ Documentation Astronomer
๐ฎ Pratique
- Astronomer Academy โ Cours gratuits Airflow
- Astro CLI Quickstart โ Dรฉmarrer avec Astro
- Marquez Demo โ Essayer OpenLineage
๐ Livres & Articles
- Data Pipelines with Apache Airflow โ Bas Harenslak & Julian de Ruiter
- Fundamentals of Data Engineering โ Joe Reis & Matt Housley
- Astronomer Blog โ Best practices Airflow
๐ง Outils
- Astronomer Registry โ Providers et DAGs
- Marquez โ Backend OpenLineage
- DataHub โ Data catalog avec lineage
โก๏ธ Prochaine รฉtape
Maintenant que tu maรฎtrises l'orchestration avancรฉe, passons au messaging distribuรฉ !
๐ Module suivant : 29_distributed_messaging โ Kafka avancรฉ, RabbitMQ, Pulsar, Debezium
Tu vas apprendre :
- Kafka avancรฉ (Quotas, Tiered Storage)
- Alternatives : RabbitMQ, Apache Pulsar
- Change Data Capture avec Debezium
- Patterns de messaging distribuรฉ
๐ Fรฉlicitations ! Tu as terminรฉ le module Advanced Orchestration pour Data Engineers.