MLOps Pipeline

MLflow: Experiment Tracking & Model Registry

● Intermediate ⏱ 40 min read MLOps Pipeline

Kubeflow Trainer takes care of creating training jobs, managing workers, scheduling resources, and running the training process. But once training finishes, a new problem shows up: where do we store the trained model, which dataset produced it, how do we compare models from different runs, and which version should go to production? That's where MLflow comes in.

What is Experiment Tracking?

Each time you train a model, a new training run is created. An experiment is a collection of those runs. As covered in the model training guide, you pick an algorithm, set hyperparameters, point it to a dataset version, and train — if something changes, you train again.

Experiment tracking means automatically logging four things about every training run:

  • Parameters — algorithm, hyperparameters, dataset version
  • Metrics — accuracy, F1 score, training time
  • Artifacts — the model file, environment files, etc.
  • Metadata — artifact location, model signature, input example, custom metadata
📌
DevOps Insight This is similar to tracking commit SHA, build number, and logs in a CI system — a training run without tracking is like a CI build without build history.

What is MLflow?

MLflow is the git for machine learning experiments — an open-source platform that tracks and manages the complete lifecycle of a model: who trained it, with what parameters, on what data, what the results were, and which version is approved for production. By tracking every run, it makes it easy to compare experiments and identify which combination of data, parameters, and code produced the best-performing model. Since the metadata is stored, you can also reproduce any run using the same code, parameters, and dataset.

MLflow operates as a client-server system with four core components:

ComponentRole
MLflow SDK (client)A Python package used to connect to the tracking server — installed locally or called from ML training workflows
Tracking serverA lightweight FastAPI-based web server with a UI and REST API; training code sends data to it over HTTP
Backend storeA relational database storing experiment, run, and trace metadata — PostgreSQL, MySQL, SQLite, or MSSQL
Artifact storeObject storage for model weights, images, and data files — AWS S3, MinIO, GCS, or Azure Blob Storage

MLflow Functional Components

  1. Experiments — a logical container for your ML work. Everything related to the attrition model falls under a single experiment.
  2. Runs — a single execution of your training code inside an experiment. Every run records the parameters, metrics, artifacts, and code version used. Train 50 times with different hyperparameters and you get 50 runs under one experiment.
  3. Model Registry — a centralized repository for managing versioned models. Once you identify the best-performing run, you register its model in the Model Registry.

How Kubeflow Pipelines Talk to MLflow

With Airflow, DVC, Feast, and Kubeflow already in the stack, where does MLflow fit? The integration happens inside the training script that runs as part of a Kubeflow Pipeline. When the training component executes, the script connects to the MLflow tracking server using the configured tracking URI, and during training it logs hyperparameters, evaluation metrics, model artifacts, and metadata — with model artifacts pushed to the configured artifact store (such as S3).

Hands-on: MLflow on Kubernetes (EKS)

We'll set up MLflow on Kubernetes, configure S3 as the artifact store, run a training script locally, and explore the results in the MLflow UI.

⚠️
This walkthrough is based on an EKS cluster with S3 Pod Identity integration. On a different setup, adjust the S3 integration accordingly.

1. Create an S3 bucket

bash
aws s3api create-bucket \
  --bucket dcube-mlflow-artifact-store \
  --region us-west-2 \
  --create-bucket-configuration LocationConstraint=us-west-2 \
  --no-cli-pager

aws s3api head-bucket \
  --bucket dcube-mlflow-artifact-store \
  --no-cli-pager

2. Deploy PostgreSQL as the backend store

bash
helm install mlflow-postgres oci://registry-1.docker.io/bitnamicharts/postgresql \
  --namespace mlflow \
  --create-namespace \
  --set auth.username=mlflow \
  --set auth.password=mlflow123 \
  --set auth.database=mlflow \
  --set primary.persistence.size=10Gi
⚠️
The username and password above are inline for demo purposes only. In production, store secrets in an external secret manager such as AWS Secrets Manager and inject them at deploy time.

Confirm the Postgres pod is running:

NAMEREADYSTATUSRESTARTSAGE
mlflow-postgres-postgresql-01/1Running035m

3. Point the Helm values at your S3 bucket

In the MLflow Helm values file, set artifactsDestination to your S3 bucket name, and enable a NodePort so the UI is reachable.

4. Deploy MLflow

bash
helm install mlflow . --namespace mlflow -f mlflow.yaml

$ kubectl get po -n mlflow
NAME                             READY   STATUS    RESTARTS   AGE
mlflow-mlflow-68cddb7f64-r277z   1/1     Running   0          98s
mlflow-postgres-postgresql-0     1/1     Running   0          58m

5. Access the MLflow UI

bash
# Via NodePort
kubectl get svc mlflow-mlflow -n mlflow

# Or via port-forward
kubectl port-forward deployment/mlflow-mlflow 5000:5000 -n mlflow

The UI is then reachable at localhost:5000.

6. Configure EKS Pod Identity for S3 access

Set up EKS Pod Identity so the MLflow deployment can access the S3 bucket securely without storing AWS credentials in Kubernetes. Update the cluster and bucket names in the setup script, then run it:

bash
chmod +x eks-s3.sh
./eks-s3.sh create

7. Run the training script

Update MLFLOW_TRACKING_URI to the EKS node IP + NodePort address (or http://127.0.0.1:5000 if using kubectl port-forward), then train:

bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

python train_and_log_model.py

This trains a scikit-learn gradient boosting model on the employee attrition dataset, logs parameters, metrics (accuracy, precision, recall, F1, ROC-AUC), and the model to the tracking server, and registers the trained model in the Model Registry. log_params(), log_metrics(), and set_tags() send metadata, while mlflow.sklearn.log_model() sends the trained model artifact along with the files needed to reproduce and serve it.

8. Explore the run in the UI

The MLflow UI shows an employee-attrition experiment; each execution of the training script creates a new run under it. Selecting a run shows everything logged during training — parameters, metrics, and model artifacts.

💡
Key Insight Every training run creates a model artifact, but not all models become part of the Model Registry — only the best-performing ones get explicitly registered, which is covered below.

Model Signature

Every model expects inputs in a certain schema and format. If you pass inputs that don't match, you can get wrong predictions with no obvious error. A model signature is the schema of a model — the expected input features, their data types, the prediction output format, and optional inference parameters. It's stored in the run's MLmodel metadata file alongside the S3 artifact location.

For any consumer, the signature acts as a contract: it tells you exactly what the model expects as input and what output format to expect — the model equivalent of an API spec. CI/CD tooling can use it to compare schema changes against the production model before deployment.

Key Insight In the feature store guide we covered why feature order matters during inference. The model signature solves this — it captures the exact order of features the model expects at inference time.

Register & Promote a Champion Model

Training isn't a one-time activity — teams train with different algorithms, hyperparameters, and datasets, then use the compare feature in the UI to determine which run produced the best model.

After comparing runs, register the best-performing one:

bash
python register_model.py

This creates a new version of the registered model with its artifacts, metadata, metrics, lineage, and version history, and assigns it the champion alias.

Champion Alias The @champion alias acts like a :stable container image tag — it always points to the model version approved for production, so deployment systems can reference models:/my-model@champion without hardcoding version numbers. See the KServe guide for how a deployed InferenceService pulls a registered model like this.
💡
Key Insight In production MLOps setups, this whole workflow is automated through CI/CD: after a model passes quality gates — accuracy ≥ 95%, F1 ≥ 94%, data quality validation — the pipeline registers the model, promotes it with the champion alias, and deploys it to the inference platform.

Clean up

bash
helm uninstall mlflow -n mlflow
helm uninstall mlflow-postgres -n mlflow
./eks-s3.sh delete

aws s3 rb s3://dcube-mlflow-artifact-store --force
What's Next With a registered @champion model in MLflow, the next step is serving it to real users at scale — covered in the Deploying with KServe guide, and monitoring it for drift and decay over time in the Data Drift & Model Decay guide.