devopsbymuh_

MLOps for DevOps Engineers · Part 10 of 10

11 min readby Muhammad Rashid

Build an End-to-End Production MLOps Platform

The finale: assemble the whole series — FastAPI, Docker, GitHub Actions, ECR, Terraform, EKS, MLflow, Prometheus/Grafana, and drift-triggered retraining — into one platform, and trace a change from git push to a promoted model serving traffic. Part 10 of the MLOps series.

MLOpsAWSDevOpsSeries
Build an End-to-End Production MLOps Platform

Ten parts ago, a data scientist dropped a Jupyter notebook in Slack, said "the model works, can you deploy it?", and walked away. We've spent the whole series answering that request properly. This finale doesn't introduce anything new — it steps back and shows how the nine pieces we built lock together into one platform, and then traces exactly what happens when a change enters it. If you've followed along, this is the article where it stops being nine tutorials and becomes one system.

The whole architecture on one page

Here's everything we built, and how a request and a model flow through it:

text
        git push
           │
   ┌───────▼────────┐
   │ GitHub Actions │  test ▸ build ▸ Trivy scan ▸ push        (part 4)
   └───────┬────────┘
           │ image:<sha>
      ┌────▼─────┐
      │   ECR    │  immutable tags                             (part 7)
      └────┬─────┘
           │        Terraform provisions it all: VPC/EKS/ECR/S3/IAM  (part 7)
   ┌───────▼─────────┐
   │   EKS cluster   │  Deployment ▸ Service ▸ Ingress ▸ HPA   (part 6)
   │  ┌───────────┐  │
   │  │ FastAPI   │  │  validated I/O, /health, /metrics       (parts 2,3)
   │  │ container │◀─┼── models:/house-price@production
   │  └─────┬─────┘  │                    ▲
   └────────┼────────┘                    │ promote (champion/challenger)
            │                    ┌────────┴────────┐
   scrape /metrics               │ MLflow Registry │            (part 5)
            │                    └────────▲────────┘
   ┌────────▼─────────┐                   │ retrain challenger
   │ Prometheus +     │          ┌────────┴────────┐
   │ Grafana + alerts │─────────▶│ drift detected  │            (parts 8,9)
   └──────────────────┘  signal  └─────────────────┘

Every box in that diagram was a part of this series. Nothing here is aspirational — it's the code in the repo you've been building. What makes it a platform rather than a pile of tools is that the boxes are wired to each other: the image CI builds is the image Kubernetes runs; the model MLflow promotes is the model the pods load; the metric the app emits is the metric that triggers the retrain.

The full platform: git push through GitHub Actions to ECR, EKS running FastAPI with the MLflow registry, Prometheus and Grafana, and drift-triggered retraining
The whole platform on one page — every box a part of this series, wired to the next.

The two loops that make it MLOps

Step back far enough and the whole platform is two feedback loops sharing infrastructure. Seeing them as two loops is the single most useful mental model in this series.

The deployment loop is the one you already knew from DevOps: code changes, tests run, an image is built and scanned, it ships to a cluster, and monitoring watches it. git push to running pod, automated end to end. If this were a normal web service, that loop would be the whole story.

The model loop is the one that makes it MLOps: data changes, drift is detected, a challenger is trained and tracked, and it's promoted only if it beats the champion — with no code change and no image rebuild (a rollout restart, or the next pod cycle, moves the pods onto the new model). This is the loop a plain web app doesn't have, and building it is the reason the last five parts existed. The two loops meet at exactly one place: the running FastAPI service, which is both "the latest deployed code" and "the current production model." Deploy the code through one loop; promote the model through the other.

Two loops sharing the running service: the deployment loop (code to image to cluster) and the model loop (data to drift to retrain to promote)
Two loops meeting at the running service: deploy code through one, promote the model through the other.

Trace one change through the system

Concrete beats abstract, so let's follow a real change. Say retraining produces a better model. Here's every step it takes to reach a user, and which part built each one:

  • The weekly drift job runs detect_drift.py, sees incomes have shifted, and exits non-zero (part 9).
  • That triggers a retrain: train_mlflow.py fits a challenger and registers it as house-price v4 in MLflow (part 5).
  • promote.py compares the challenger's MAE to the champion's. It's lower, so the production alias moves to v4 (part 5).
  • The pods are wired to load models:/house-price@production (part 5), so the next time a pod starts — a rollout, a scale-up, or a restart — it loads v4. No image rebuild, no code change (parts 2, 6).
  • Prometheus keeps scraping /metrics; the predicted-price histogram shifts to reflect the new model, and latency/error alerts stay quiet (part 8).
  • If v4 had been worse, promote.py would have refused it and nothing would have changed. The gate is the safety.

Now trace a code change instead — say you improve the batch endpoint. Push it; GitHub Actions runs the tests, builds an image tagged with the commit SHA, scans it with Trivy, and pushes it to ECR (part 4). You bump the tag in the Deployment; Kubernetes rolls it out pod by pod, each new pod gated by the readiness probe so traffic only moves once it's actually serving (parts 3, 6). Two completely different kinds of change — a better model and better code — travel two separate, automated paths and meet in the same running service. That separation is the point.

What ties it together: everything is code

The quiet through-line of all ten parts is that nothing lives only in someone's head or someone's console. The model is a versioned artifact in a registry. The API and its container are code. The pipeline is code. The Kubernetes state is code. The AWS infrastructure is code. The drift check and retrain trigger are code. You could delete the entire AWS account, run terraform apply and the pipeline, and rebuild the whole platform from a git repo. That reproducibility — the same discipline we demanded of the model in part one, applied to every layer — is what separates a platform from a pile of scripts that happen to work.

What we deliberately left out

An honest finale names its own edges. This series is a spine, not the whole skeleton. We didn't build a feature store (worth it once several models share features), model explainability (SHAP and friends, which matter the moment a prediction affects a person), true production A/B testing or shadow deployments (evaluating a challenger on live traffic before promoting), GPU serving and batching (a different world for deep learning), or a data-versioning tool like DVC alongside the model registry. Each is a real part of mature MLOps. None of them changes the shape you now have — they slot into it. That's the payoff of building the spine first.

The whole series, in one breath

  • 1 — MLOps is DevOps plus versioning, quality gates, and drift monitoring; training and inference are separate lifecycles.
  • 2 — FastAPI turns the model artifact into a validated, documented, real-time-and-batch service.
  • 3 — Docker welds the model to its exact runtime so it runs the same everywhere.
  • 4 — GitHub Actions tests, builds, scans, and ships the image on every change, keylessly.
  • 5 — MLflow tracks every experiment and promotes models by champion/challenger, not by date.
  • 6 — Kubernetes runs it with self-healing, autoscaling, and zero-downtime, readiness-gated rollouts.
  • 7 — Terraform makes the VPC, cluster, registry, and IAM reproducible and destroyable.
  • 8 — Prometheus and Grafana make the service and the model's behavior visible, with alerts.
  • 9 — Drift detection plus a champion/challenger retrain closes the feedback loop automatically.
  • 10 — It's all one platform, two loops, entirely code.

If you started this series able to deploy a web app and ended it able to deploy, monitor, and automatically retrain a model in production, you didn't learn a new career — you extended the one you have. That was the whole premise back in part one: you already knew 90% of this. The other 10% is everything we just built, and it's the part that turns "the model works on my laptop" into a system you'd trust at 3am. The complete platform is in the series repo. Thanks for building it with me — now go put a model in production.

$ subscribe --free

Want to build this hands-on? Every guide becomes a project on the channel.

Subscribe on YouTube