feat(etl): ordonnance la détection d'alertes et les recommandations par un DAG Airflow
Le DAG `alertes` enchaîne `app.detection.internal_alerts` puis `app.cli generate-recommendations`, à la quinzième minute de chaque heure. Le décalage laisse finir `ml_score`, qui écrit à l'heure pile les prédictions dont la règle `anomaly` a besoin, sans créer de dépendance entre les deux DAGs : quatre règles de détection sur cinq ne touchent pas au modèle, et un modèle jamais entraîné ne doit pas priver le parc de ses alertes. L'image Airflow porte un second environnement uv, `/opt/backend/.venv`, puisque la logique vit dans le backend (ADR 0006) et qu'aucune route HTTP ne l'expose. Le `UV_PROJECT_ENVIRONMENT` global hérité de l'issue #115 disparaît : il vaut pour tous les projets, donc `uv run` depuis `/opt/ml` résolvait le venv du backend. uv prend `<projet>/.venv` par défaut, se placer dans le dossier suffit. La CI vérifie maintenant que les deux environnements s'importent sans réseau. Le conteneur reçoit `DATABASE_URL` en asyncpg et une `APP_SECRET_KEY` distincte de celle de l'API, alimentée par `AIRFLOW_APP_SECRET_KEY` : la détection ne signe aucun jeton, et Airflow permet d'exécuter du code depuis son interface.
This commit is contained in:
@@ -4,8 +4,9 @@ name: Airflow
|
||||
# (contrairement à backend.yml et ml.yml) : apache-airflow 2.10 ne supporte pas 3.14. Le 3.14 de
|
||||
# ml/ ne vit que dans l'image Docker, dans son propre environnement (cf. etl/airflow/Dockerfile).
|
||||
#
|
||||
# Piège : l'image COPY les fichiers de dépendances et le code de ml/. Une modification de ml/
|
||||
# peut donc casser sa construction, d'où ces chemins dans les déclencheurs.
|
||||
# Piège : l'image COPY les fichiers de dépendances et le code de ml/ et de apps/backend/. Une
|
||||
# modification de l'un ou de l'autre peut donc casser sa construction, d'où ces chemins dans
|
||||
# les déclencheurs, alors même que ce workflow ne teste ni le modèle ni l'API.
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -14,6 +15,9 @@ on:
|
||||
- "ml/pyproject.toml"
|
||||
- "ml/uv.lock"
|
||||
- "ml/enervision_ml/**"
|
||||
- "apps/backend/pyproject.toml"
|
||||
- "apps/backend/uv.lock"
|
||||
- "apps/backend/app/**"
|
||||
- ".github/workflows/airflow.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -21,6 +25,9 @@ on:
|
||||
- "ml/pyproject.toml"
|
||||
- "ml/uv.lock"
|
||||
- "ml/enervision_ml/**"
|
||||
- "apps/backend/pyproject.toml"
|
||||
- "apps/backend/uv.lock"
|
||||
- "apps/backend/app/**"
|
||||
- ".github/workflows/airflow.yml"
|
||||
|
||||
permissions:
|
||||
@@ -73,7 +80,7 @@ jobs:
|
||||
- name: Récupère le dépôt
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Construit l'image (contexte à la racine, elle COPY ml/)
|
||||
- name: Construit l'image (contexte à la racine, elle COPY ml/ et apps/backend/)
|
||||
run: docker build -f etl/airflow/Dockerfile -t enervision-airflow:ci .
|
||||
|
||||
# Vérifie ce qui ne casse qu'à l'exécution, pas à la construction : libgomp1 absent
|
||||
@@ -82,3 +89,10 @@ jobs:
|
||||
run: >
|
||||
docker run --rm --network none enervision-airflow:ci
|
||||
bash -c "cd /opt/ml && env -u VIRTUAL_ENV uv run --no-sync python -m enervision_ml.train --help"
|
||||
|
||||
# `--help` sort par argparse avant `get_settings()` : ni base ni secret requis, et
|
||||
# l'import du module prouve que l'environnement /opt/backend est complet.
|
||||
- name: Vérifie que la détection d'alertes s'importe sans réseau
|
||||
run: >
|
||||
docker run --rm --network none enervision-airflow:ci
|
||||
bash -c "cd /opt/backend && env -u VIRTUAL_ENV uv run --no-sync python -m app.detection.internal_alerts --help"
|
||||
|
||||
Reference in New Issue
Block a user