MLflow es una plataforma open source para gestionar el ciclo de vida de proyectos de machine learning: registra los parámetros, métricas y artefactos de cada entrenamiento, permite compararlos desde una interfaz web y mantiene un registro versionado de modelos. En este tutorial desplegarás un servidor de seguimiento de MLflow en Ubuntu 24.04 con PostgreSQL como base de datos, artefactos en disco local, un servicio systemd y Nginx con HTTPS y autenticación básica delante. Al final registrarás un experimento y un modelo desde un script de Python en tu equipo.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM y espacio en disco para los artefactos (los modelos pueden ocupar varios GB).
- Un usuario no root con privilegios
sudo. - Un nombre de dominio con un registro A apuntando a la IP del servidor. En esta guía se usa
mlflow.your_domain. - Los puertos 22, 80 y 443 abiertos en el firewall.
- En tu equipo local, Python 3.10 o superior para ejecutar el experimento de ejemplo.
Paso 1: Instalar PostgreSQL y crear la base de datos
MLflow guarda los metadatos (experimentos, ejecuciones, parámetros, métricas y registro de modelos) en una base de datos SQL. PostgreSQL es la opción más robusta para varios usuarios. Instálalo junto con las herramientas de Python:
sudo apt update
sudo apt install postgresql python3-venv
Genera una contraseña aleatoria para el usuario de base de datos. Usa una hexadecimal para que no contenga caracteres que haya que escapar en la URI de conexión:
openssl rand -hex 16
Copia el valor y úsalo en lugar de your_db_password en los siguientes comandos. Crea el usuario y la base de datos:
sudo -u postgres psql -c "CREATE USER mlflow WITH PASSWORD 'your_db_password';"
sudo -u postgres createdb -O mlflow mlflow
Comprueba que puedes conectarte con ese usuario:
psql "postgresql://mlflow:[email protected]/mlflow" -c "SELECT current_user;"
current_user
--------------
mlflow
(1 row)
Paso 2: Instalar MLflow en un entorno virtual
Ubuntu 24.04 no permite instalar paquetes con pip en el Python del sistema, y además conviene aislar MLflow para poder actualizarlo sin afectar a nada más. Crea un usuario de sistema y un entorno virtual en /opt/mlflow:
sudo useradd --system --home-dir /var/lib/mlflow --create-home --shell /usr/sbin/nologin mlflow
sudo python3 -m venv /opt/mlflow/venv
sudo /opt/mlflow/venv/bin/pip install --upgrade pip
sudo /opt/mlflow/venv/bin/pip install mlflow psycopg2-binary
psycopg2-binary es el driver que usa MLflow para hablar con PostgreSQL. Verifica la instalación:
/opt/mlflow/venv/bin/mlflow --version
mlflow, version 3.4.0
Crea el directorio donde se guardarán los artefactos (modelos, gráficos, ficheros) y dáselo al usuario mlflow:
sudo mkdir -p /var/lib/mlflow/artifacts
sudo chown -R mlflow:mlflow /var/lib/mlflow
Paso 3: Crear el servicio systemd
Guarda la cadena de conexión en un archivo de entorno aparte para que la contraseña no aparezca en la unidad de systemd ni en ps:
sudo mkdir -p /etc/mlflow
sudo nano /etc/mlflow/mlflow.env
MLFLOW_BACKEND_STORE_URI=postgresql://mlflow:[email protected]:5432/mlflow
Restringe los permisos para que solo root y el grupo mlflow puedan leerlo:
sudo chown root:mlflow /etc/mlflow/mlflow.env
sudo chmod 640 /etc/mlflow/mlflow.env
Crea la unidad del servicio:
sudo nano /etc/systemd/system/mlflow.service
[Unit]
Description=MLflow tracking server
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=mlflow
Group=mlflow
WorkingDirectory=/var/lib/mlflow
EnvironmentFile=/etc/mlflow/mlflow.env
ExecStart=/opt/mlflow/venv/bin/mlflow server \
--backend-store-uri ${MLFLOW_BACKEND_STORE_URI} \
--artifacts-destination /var/lib/mlflow/artifacts \
--host 127.0.0.1 \
--port 5000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Estas son las opciones clave:
--backend-store-uri: base de datos de metadatos. En el primer arranque MLflow crea las tablas automáticamente.--artifacts-destination: dónde guarda el servidor los artefactos. Los clientes los suben a través del propio servidor HTTP, así que no necesitan acceso al disco ni credenciales de almacenamiento.--host 127.0.0.1: MLflow solo escucha en local; el acceso externo pasará por Nginx con TLS y contraseña.
Carga la unidad y arranca el servicio:
sudo systemctl daemon-reload
sudo systemctl enable --now mlflow
Comprueba su estado y el endpoint de salud:
systemctl status mlflow --no-pager
curl http://127.0.0.1:5000/health
OK
El primer arranque tarda unos segundos mientras se crean las tablas. Si curl falla, revisa los logs con sudo journalctl -u mlflow -n 50 --no-pager.
Paso 4: Publicar MLflow con Nginx, HTTPS y autenticación
El servidor de MLflow no protege la interfaz ni la API con contraseña. Nginx se encargará del TLS y de la autenticación básica. Instala Nginx, Certbot y la utilidad htpasswd:
sudo apt install nginx certbot python3-certbot-nginx apache2-utils
Crea un usuario para el acceso. El comando pide la contraseña de forma interactiva:
sudo htpasswd -c /etc/nginx/.htpasswd-mlflow your_user
Crea el bloque de servidor:
sudo nano /etc/nginx/sites-available/mlflow
server {
listen 80;
listen [::]:80;
server_name mlflow.your_domain;
client_max_body_size 1G;
location / {
auth_basic "MLflow";
auth_basic_user_file /etc/nginx/.htpasswd-mlflow;
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
}
}
client_max_body_size permite subir artefactos grandes como modelos serializados. No se reenvía la cabecera Host original: MLflow recibe 127.0.0.1:5000, que es el host en el que escucha.
Activa el sitio, valida la configuración y recarga Nginx:
sudo ln -s /etc/nginx/sites-available/mlflow /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Abre los puertos web en UFW y obtén el certificado. Certbot modifica el bloque de servidor para añadir HTTPS y la redirección desde HTTP:
sudo ufw allow 'Nginx Full'
sudo certbot --nginx -d mlflow.your_domain
Comprueba que el acceso sin credenciales se rechaza y con ellas funciona:
curl -I https://mlflow.your_domain/health
curl -u your_user https://mlflow.your_domain/health
HTTP/1.1 401 Unauthorized
...
OK
Abre https://mlflow.your_domain en el navegador, introduce las credenciales y verás la interfaz de MLflow con el experimento Default vacío.
Paso 5: Registrar un experimento desde Python
Los clientes de MLflow se configuran con variables de entorno. MLFLOW_TRACKING_USERNAME y MLFLOW_TRACKING_PASSWORD hacen que el cliente envíe las credenciales de autenticación básica que exige Nginx. En tu equipo local, crea un entorno virtual con MLflow y scikit-learn:
python3 -m venv ~/mlflow-client
~/mlflow-client/bin/pip install mlflow scikit-learn
Exporta la configuración del servidor en la sesión actual:
export MLFLOW_TRACKING_URI=https://mlflow.your_domain
export MLFLOW_TRACKING_USERNAME=your_user
export MLFLOW_TRACKING_PASSWORD=your_password
Crea el script de entrenamiento:
nano entrenar_iris.py
import mlflow
import mlflow.sklearn
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score
from sklearn.model_selection import train_test_split
mlflow.set_experiment("clasificacion-iris")
params = {"n_estimators": 100, "max_depth": 5, "random_state": 42}
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=params["random_state"]
)
with mlflow.start_run(run_name="random-forest-base") as run:
mlflow.log_params(params)
modelo = RandomForestClassifier(**params)
modelo.fit(X_train, y_train)
predicciones = modelo.predict(X_test)
mlflow.log_metric("accuracy", accuracy_score(y_test, predicciones))
mlflow.log_metric("f1", f1_score(y_test, predicciones, average="weighted"))
mlflow.sklearn.log_model(
sk_model=modelo,
name="modelo",
input_example=X_test[:2],
registered_model_name="IrisClassifier",
)
print(f"Run ID: {run.info.run_id}")
El script registra los hiperparámetros, dos métricas y el modelo entrenado. Con registered_model_name, MLflow además crea (o incrementa) una versión del modelo IrisClassifier en el registro. Ejecútalo:
~/mlflow-client/bin/python entrenar_iris.py
Successfully registered model 'IrisClassifier'.
Created version '1' of model 'IrisClassifier'.
Run ID: 3f2b8c1d9e7a4b5c8d6e1f0a2b3c4d5e
En la interfaz web, abre el experimento clasificacion-iris: verás la ejecución con sus parámetros y métricas, y en la pestaña de artefactos el modelo serializado. Cambia max_depth o n_estimators, vuelve a ejecutar el script y usa el botón de comparación para ver las ejecuciones lado a lado.
En el servidor, confirma que el artefacto se ha guardado en disco:
sudo find /var/lib/mlflow/artifacts -name MLmodel
Paso 6: Gestionar versiones con aliases en el registro de modelos
Los antiguos estados Staging y Production están obsoletos en MLflow. La forma actual de marcar qué versión se usa en cada entorno son los aliases: nombres móviles que apuntan a una versión concreta. Crea un script para asignar el alias champion a la versión 1:
nano promover.py
from mlflow import MlflowClient
client = MlflowClient()
client.set_registered_model_alias("IrisClassifier", "champion", 1)
version = client.get_model_version_by_alias("IrisClassifier", "champion")
print(f"champion -> versión {version.version}")
~/mlflow-client/bin/python promover.py
champion -> versión 1
Cualquier aplicación puede cargar ahora el modelo por su alias, sin conocer el número de versión:
import mlflow.pyfunc
modelo = mlflow.pyfunc.load_model("models:/IrisClassifier@champion")
print(modelo.predict([[5.1, 3.5, 1.4, 0.2]]))
Cuando entrenes una versión mejor, basta con mover el alias a la nueva versión: las aplicaciones que cargan @champion pasan a usarla sin cambios de código.
Paso 7: Hacer copias de seguridad
Todo el estado de MLflow está en dos sitios: la base de datos y el directorio de artefactos. Ambos deben copiarse juntos para que las referencias sean coherentes. Un volcado de PostgreSQL y un archivo de los artefactos son suficientes:
sudo mkdir -p /var/backups/mlflow
sudo -u postgres pg_dump -Fc mlflow | sudo tee /var/backups/mlflow/mlflow-$(date +%F).dump > /dev/null
sudo tar -czf /var/backups/mlflow/artifacts-$(date +%F).tar.gz -C /var/lib/mlflow artifacts
Comprueba que los archivos existen con ls -lh /var/backups/mlflow y cópialos fuera del servidor, por ejemplo a un almacenamiento de objetos.
Solución de problemas
El servicio no arranca y el log muestra password authentication failed for user "mlflow". La contraseña de /etc/mlflow/mlflow.env no coincide con la de PostgreSQL. Cámbiala con sudo -u postgres psql -c "ALTER USER mlflow WITH PASSWORD 'your_db_password';" y reinicia con sudo systemctl restart mlflow.
502 Bad Gateway en el navegador. Nginx no llega a MLflow. Comprueba que el servicio está activo y escuchando con sudo ss -tlnp | grep 5000.
413 Request Entity Too Large al registrar un modelo. El artefacto supera client_max_body_size. Aumenta el valor en el bloque de Nginx y recarga con sudo systemctl reload nginx.
El cliente devuelve 401 o pide credenciales. Las variables MLFLOW_TRACKING_USERNAME y MLFLOW_TRACKING_PASSWORD no están exportadas en la sesión que ejecuta el script. Compruébalo con env | grep MLFLOW.
Conclusión
Tienes un servidor de MLflow en Ubuntu 24.04 con PostgreSQL para los metadatos, artefactos servidos por el propio servidor, un servicio systemd y acceso protegido con HTTPS y contraseña. Tu equipo ya puede registrar experimentos y versionar modelos en un único sitio. Como siguientes pasos, programa las copias de seguridad con un timer de systemd, mueve los artefactos a un almacenamiento compatible con S3 si el disco se queda corto y sirve el modelo @champion con mlflow models serve o en tu propia API.
