Aller au contenu

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