MLflow — Suivi d'expériences & Registre de modèles¶
MLflow fournit le suivi d'expériences ML et un registre de modèles pour AKKO. Les data scientists enregistrent leurs expériences depuis les notebooks JupyterHub, comparent les exécutions et publient les modèles prêts pour la production — le tout avec les métadonnées stockées dans PostgreSQL et les artefacts stockés dans object storage (compatible S3).
Architecture¶
JupyterHub (notebooks) Airflow (DAGs)
\ /
+-------v-------v-------+
| MLflow (:5000) |
| Serveur de suivi + UI |
+-------+-------+-----------+
| |
+------------v---+ +------v-----------+
| PostgreSQL | | object storage (S3) |
| (métadonnées : | | s3://akko-warehouse/mlflow/
| expériences, | | (artefacts : modèles,
| exécutions, | | jeux de données,
| paramètres, | | graphiques)
| métriques) | |
+----------------+ +------------------+
- PostgreSQL stocke les métadonnées des expériences (exécutions, paramètres, métriques, tags)
- object storage stocke les artefacts (binaires de modèles, jeux de données, graphiques) dans
s3://akko-warehouse/mlflow/ - L'interface MLflow permet la comparaison d'expériences, la visualisation des métriques et le versioning des modèles
URLs¶
| Mode | URL |
|---|---|
| Kubernetes (k3d) | https://experiments.akko.local |
Utilisation¶
Depuis les notebooks (JupyterHub)¶
La variable d'environnement MLFLOW_TRACKING_URI est pré-configurée dans le spawner JupyterHub. Aucune configuration nécessaire :
import mlflow
# Automatique — MLFLOW_TRACKING_URI est défini par le spawner
mlflow.set_experiment("banking-risk-model")
with mlflow.start_run():
mlflow.log_param("algorithm", "random_forest")
mlflow.log_param("n_estimators", 100)
mlflow.log_metric("accuracy", 0.94)
mlflow.log_metric("f1_score", 0.91)
# Enregistrer le modèle entraîné
mlflow.sklearn.log_model(model, "model")
Comparaison d'expériences¶
import mlflow
# Rechercher les exécutions dans les expériences
runs = mlflow.search_runs(
experiment_names=["banking-risk-model"],
order_by=["metrics.accuracy DESC"],
max_results=10,
)
print(runs[["params.algorithm", "metrics.accuracy", "metrics.f1_score"]])
Registre de modèles¶
import mlflow
# Enregistrer le meilleur modèle
result = mlflow.register_model(
model_uri="runs:/<run-id>/model",
name="banking-risk-classifier",
)
# Passer en production
client = mlflow.tracking.MlflowClient()
client.transition_model_version_stage(
name="banking-risk-classifier",
version=result.version,
stage="Production",
)
Depuis les DAGs Airflow¶
Les workers Airflow ont MLFLOW_TRACKING_URI configuré, les DAGs peuvent donc enregistrer des exécutions et publier des modèles :
import mlflow
mlflow.set_experiment("airflow-etl-quality")
with mlflow.start_run(run_name="daily-etl"):
mlflow.log_metric("rows_processed", 125000)
mlflow.log_metric("data_quality_score", 0.98)
Configuration¶
Kubernetes (Helm)¶
akko-mlflow:
enabled: true
image:
repository: localhost:5050/akko-mlflow # image personnalisée (k3d : k3d-akko-registry:5050/akko-mlflow)
tag: "2026.03"
database:
host: "akko-postgresql"
name: "mlflow"
artifacts:
root: "s3://akko-warehouse/mlflow/"
s3Endpoint: "http://akko-minio:9000"
ingress:
host: "experiments.akko.local"
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi # Surcharge dev : 768Mi
Variables d'environnement (Notebooks & Airflow)¶
| Variable | Valeur | Fonction |
|---|---|---|
MLFLOW_TRACKING_URI |
http://akko-akko-mlflow:5000 |
Connecte les notebooks/DAGs au serveur MLflow |
MLFLOW_S3_ENDPOINT_URL |
http://akko-minio:9000 |
Point d'accès pour le stockage d'artefacts |
AWS_ACCESS_KEY_ID |
(depuis le secret) | Identifiants object storage pour l'accès aux artefacts |
AWS_SECRET_ACCESS_KEY |
(depuis le secret) | Identifiants object storage pour l'accès aux artefacts |
Fonctionnalités principales¶
| Fonctionnalité | Description |
|---|---|
| Suivi d'expériences | Enregistre paramètres, métriques et tags pour chaque exécution |
| Registre de modèles | Versioning des modèles, transitions d'étapes (Staging/Production/Archived) |
| Stockage d'artefacts | Stocke modèles, jeux de données et graphiques sur object storage (compatible S3) |
| Interface utilisateur | Compare les expériences, visualise les métriques, explore les artefacts |
| Autologging | mlflow.autolog() pour scikit-learn, PyTorch, XGBoost, LightGBM |
Authentification¶
MLflow est protégé par OAuth2-Proxy (middleware ForwardAuth). Les utilisateurs doivent être authentifiés via Keycloak SSO pour accéder à l'interface depuis l'ingress.
Pour les appels internes entre services (dans le cluster), aucune authentification n'est requise — les services se connectent directement à http://akko-akko-mlflow:5000.
Healthcheck¶
MLflow expose un endpoint /health utilisé par les sondes Kubernetes :
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 10
periodSeconds: 10
Ressources requises¶
| Composant | RAM minimum | Recommandé |
|---|---|---|
| Serveur MLflow | 256 Mi | 512 Mi |
Dépendance PostgreSQL
MLflow nécessite que la base de données mlflow existe dans PostgreSQL. Le job akko-init la crée automatiquement lors du premier déploiement.
Dépannage¶
Échec du stockage d'artefacts (connexion object storage)¶
Symptômes : mlflow.log_artifact() ou mlflow.sklearn.log_model() lève ClientError: An error occurred (NoSuchBucket) ou ConnectionRefusedError. L'interface MLflow affiche les exécutions mais les artefacts sont absents.
Cause : object storage est injoignable depuis le pod MLflow, le bucket akko-warehouse n'existe pas, ou les identifiants S3 (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) sont incorrects.
Solution :
# Vérifier que le pod object storage tourne
kubectl get pods -n akko -l app.kubernetes.io/name=minio
# Vérifier si le bucket existe
kubectl exec -n akko deploy/akko-minio -- mc ls local/akko-warehouse/mlflow/ 2>/dev/null || echo "Bucket ou chemin manquant"
# Vérifier les variables d'environnement de MLflow
kubectl exec -n akko deploy/akko-akko-mlflow -- env | grep -E "AWS_|S3_ENDPOINT|ARTIFACT"
# Consulter les logs MLflow pour les erreurs S3
kubectl logs -n akko deploy/akko-akko-mlflow --tail=50 | grep -i "s3\|minio\|artifact\|bucket"
Échec de connexion à la base de données (PostgreSQL)¶
Symptômes : Le pod MLflow entre en CrashLoopBackOff. Les logs affichent OperationalError: could not connect to server ou FATAL: database "mlflow" does not exist.
Cause : Le pod PostgreSQL n'est pas prêt, la base de données mlflow n'a pas été créée par le job d'initialisation, ou les identifiants de la base dans la configuration MLflow sont incorrects.
Solution :
# Vérifier le statut du pod PostgreSQL
kubectl get pods -n akko -l app.kubernetes.io/name=akko-postgresql
# Vérifier que la base mlflow existe
kubectl exec -n akko deploy/akko-postgresql -- psql -U postgres -c "\l" | grep mlflow
# Si elle est manquante, la créer manuellement
kubectl exec -n akko deploy/akko-postgresql -- psql -U postgres -c "CREATE DATABASE mlflow;"
# Vérifier la chaîne de connexion de MLflow
kubectl logs -n akko deploy/akko-akko-mlflow --tail=50 | grep -i "database\|postgres\|connection"
# Redémarrer MLflow après correction de la base
kubectl rollout restart -n akko deploy/akko-akko-mlflow
Échecs du suivi d'expériences¶
Symptômes : mlflow.set_experiment() ou mlflow.start_run() lève MlflowException: API request failed depuis les notebooks. L'interface MLflow retourne une erreur 500.
Cause : La variable d'environnement MLFLOW_TRACKING_URI dans le spawner de notebooks pointe vers une adresse incorrecte ou injoignable, ou le serveur MLflow est surchargé / en cours de redémarrage.
Solution :
# Vérifier que le pod MLflow est en bonne santé
kubectl get pods -n akko -l app.kubernetes.io/name=akko-mlflow
kubectl exec -n akko deploy/akko-akko-mlflow -- curl -s http://localhost:5000/health
# Vérifier l'URI de suivi configurée dans JupyterHub
kubectl exec -n akko deploy/akko-jupyterhub -- env | grep MLFLOW_TRACKING_URI
# Tester la connectivité depuis un pod notebook
kubectl exec -n akko $(kubectl get pod -n akko -l app=jupyterhub -o jsonpath='{.items[0].metadata.name}') \
-- curl -s http://akko-akko-mlflow:5000/api/2.0/mlflow/experiments/search
# Consulter les logs du serveur MLflow
kubectl logs -n akko deploy/akko-akko-mlflow --tail=100
Erreurs de permissions du registre de modèles¶
Symptômes : mlflow.register_model() lève RestException: RESOURCE_ALREADY_EXISTS ou PERMISSION_DENIED. Les transitions de version de modèle échouent silencieusement.
Cause : Un modèle avec le même nom existe déjà dans une expérience différente, ou le serveur MLflow fonctionne en mode lecture seule à cause d'un problème de migration de base de données.
Solution :
# Lister les modèles enregistrés via l'API
kubectl exec -n akko deploy/akko-akko-mlflow -- \
curl -s http://localhost:5000/api/2.0/mlflow/registered-models/search | python3 -m json.tool
# Vérifier les problèmes de migration de base
kubectl logs -n akko deploy/akko-akko-mlflow --tail=100 | grep -i "migration\|alembic\|upgrade"
# Si le schéma de base est obsolète, déclencher une migration manuelle
kubectl exec -n akko deploy/akko-akko-mlflow -- mlflow db upgrade postgresql://postgres:$POSTGRES_PASSWORD@akko-postgresql:5432/mlflow
Interface MLflow inaccessible (502 Bad Gateway)¶
Symptômes : L'accès à https://experiments.akko.local retourne une erreur 502. Le pod tourne mais l'ingress ne peut pas l'atteindre.
Cause : La sonde de disponibilité n'est pas encore passée (MLflow est encore en cours d'initialisation), le mappage de port du service est incorrect, ou OAuth2-Proxy échoue à authentifier.
Solution :
# Vérifier la disponibilité du pod
kubectl get pods -n akko -l app.kubernetes.io/name=akko-mlflow -o wide
# Tester l'endpoint de santé directement
kubectl exec -n akko deploy/akko-akko-mlflow -- curl -s http://localhost:5000/health
# Vérifier la configuration de l'ingress
kubectl get ingress -n akko | grep mlflow
kubectl describe ingress -n akko akko-akko-mlflow
# Consulter les logs OAuth2-Proxy si l'authentification échoue
kubectl logs -n akko deploy/akko-oauth2-proxy --tail=50 | grep -i mlflow