Shipping a machine learning model that works in a Jupyter notebook is the easy part. Shipping it in a way that is reliable, reproducible, monitored, and deployable by a team — that is MLOps. The gap between a notebook and production is where most ML projects quietly die.Colocar em produção um modelo de machine learning que funciona num Jupyter notebook é a parte fácil. Fazê-lo de forma confiável, reproduzível, monitorada e implantável por um time — isso é MLOps. A lacuna entre um notebook e a produção é onde a maioria dos projetos de ML silenciosamente fracassa.
This article covers the complete lifecycle: from experiment tracking and model packaging to serving infrastructure, automated retraining, and production monitoring. It is based on patterns I've applied in real production systems handling both batch inference and low-latency real-time prediction.Este artigo cobre o ciclo de vida completo: do rastreamento de experimentos e empacotamento de modelos até a infraestrutura de serving, retreinamento automatizado e monitoramento em produção. É baseado em padrões aplicados em sistemas reais de produção com inferência batch e predição em tempo real de baixa latência.
Why MLOps Is a Software Architecture ProblemPor Que MLOps é um Problema de Arquitetura de Software
ML systems have all the challenges of traditional software — CI/CD, versioning, testing, observability — plus several unique ones:Sistemas de ML têm todos os desafios do software tradicional — CI/CD, versionamento, testes, observabilidade — além de vários desafios únicos:
- Data dependency: The model is only as good as its training data. Data pipelines must be version-controlled alongside model code.Dependência de dados: O modelo é tão bom quanto seus dados de treinamento. Pipelines de dados devem ser versionados junto com o código do modelo.
- Experiment reproducibility: You must be able to re-run any experiment months later and get the same artifact.Reprodutibilidade de experimentos: Você deve ser capaz de re-executar qualquer experimento meses depois e obter o mesmo artefato.
- Concept drift: The world changes. A model trained in January may degrade silently by June without any code changes.Concept drift: O mundo muda. Um modelo treinado em janeiro pode se degradar silenciosamente até junho sem nenhuma alteração de código.
- Dual ownership: Data scientists own the model logic; engineers own the infrastructure. Friction at this boundary kills velocity.Propriedade dual: Cientistas de dados são responsáveis pela lógica do modelo; engenheiros pela infraestrutura. Atrito nessa fronteira mata a velocidade.
Key insight: The most impactful thing you can do for an ML team is not a better algorithm — it is reducing the time from "model ready" to "model in production serving real traffic" from weeks to hours.Insight chave: A coisa mais impactante que você pode fazer por um time de ML não é um algoritmo melhor — é reduzir o tempo de "modelo pronto" para "modelo em produção servindo tráfego real" de semanas para horas.
The MLOps PipelineO Pipeline de MLOps
A production-grade MLOps pipeline has six stages:Um pipeline MLOps de nível de produção tem seis etapas:
The critical property is that this pipeline is automated end-to-end. Manual steps — "run this notebook, then upload the pkl file to S3" — are a reliability hazard and a knowledge silo.A propriedade crítica é que este pipeline é automatizado de ponta a ponta. Passos manuais — "execute este notebook, depois faça upload do pkl para o S3" — são um risco de confiabilidade e um silo de conhecimento.
Experiment Tracking with MLflowRastreamento de Experimentos com MLflow
Before talking about deployment, you need reproducible experiments. MLflow is the de facto standard for tracking parameters, metrics, and artifacts across training runs.Antes de falar sobre deploy, você precisa de experimentos reproduzíveis. O MLflow é o padrão de facto para rastrear parâmetros, métricas e artefatos entre execuções de treinamento.
import mlflow
import mlflow.sklearn
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_auc_score
mlflow.set_experiment("churn-prediction-v2")
with mlflow.start_run():
params = {"n_estimators": 200, "max_depth": 4, "learning_rate": 0.05}
mlflow.log_params(params)
model = GradientBoostingClassifier(**params)
model.fit(X_train, y_train)
auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
mlflow.log_metric("roc_auc", auc)
mlflow.log_metric("precision", precision_score(y_test, model.predict(X_test)))
mlflow.sklearn.log_model(model, "model", registered_model_name="churn-classifier")Every run records its parameters, metrics, and the model artifact. The model registry tracks versions and their promotion through stages: Staging → Production → Archived. No artifact ever leaves MLflow without a registered version.Cada execução registra seus parâmetros, métricas e o artefato do modelo. O registro de modelos rastreia versões e sua promoção pelas etapas: Staging → Production → Archived. Nenhum artefato sai do MLflow sem uma versão registrada.
Packaging: From Artifact to Docker ImageEmpacotamento: Do Artefato à Imagem Docker
A trained model artifact (pkl, ONNX, SavedModel) is not deployable by itself. It needs a serving runtime — an HTTP endpoint that can receive feature vectors and return predictions. Packaging as a Docker image makes this runtime portable and reproducible.Um artefato de modelo treinado (pkl, ONNX, SavedModel) não é implantável por si só. Ele precisa de um runtime de serving — um endpoint HTTP que recebe vetores de features e retorna predições. Empacotar como imagem Docker torna esse runtime portável e reproduzível.
# Dockerfile for a FastAPI model serving container
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY model/ ./model/
COPY app/ ./app/
ENV MODEL_URI=./model/artifact
ENV PORT=8080
EXPOSE 8080
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]# app/main.py — minimal FastAPI serving endpoint
from fastapi import FastAPI
import mlflow.pyfunc, pandas as pd
app = FastAPI()
model = mlflow.pyfunc.load_model("./model/artifact")
@app.post("/predict")
def predict(features: dict):
df = pd.DataFrame([features])
prediction = model.predict(df)
return {"prediction": int(prediction[0]), "model_version": model.metadata.run_id}Feature Preprocessing Must Be Inside the ContainerPré-processamento de Features Deve Estar Dentro do Container
A common mistake is packaging only the model estimator and handling preprocessing in the calling service. This creates a training-serving skew: the preprocessing logic in the notebook diverges from the preprocessing in the API over time. Always package the full pipeline — scaler, encoder, and estimator — as a single MLflow pyfunc model.Um erro comum é empacotar apenas o estimador do modelo e tratar o pré-processamento no serviço chamador. Isso cria um training-serving skew: a lógica de pré-processamento no notebook diverge da lógica na API ao longo do tempo. Sempre empacote o pipeline completo — scaler, encoder e estimador — como um único modelo MLflow pyfunc.
CI/CD for Machine LearningCI/CD para Machine Learning
The ML CI/CD pipeline differs from software CI/CD in one critical way: the "build" step (training) can take hours and requires GPU resources. The pipeline must be designed to run training only when necessary.O pipeline CI/CD de ML difere do CI/CD de software em um aspecto crítico: a etapa de "build" (treinamento) pode levar horas e requer recursos de GPU. O pipeline deve ser projetado para executar o treinamento somente quando necessário.
# .github/workflows/ml-pipeline.yml (simplified)
name: ML Pipeline
on:
push:
paths:
- 'src/features/**'
- 'src/train.py'
- 'data/schema.yaml'
jobs:
train-and-evaluate:
runs-on: self-hosted # GPU runner
steps:
- uses: actions/checkout@v4
- name: Run training pipeline
run: python src/train.py --config config/prod.yaml
- name: Evaluate against threshold
run: python src/evaluate.py --min-auc 0.82
- name: Build and push Docker image
run: |
docker build -t registry/churn-model:${{ github.sha }} .
docker push registry/churn-model:${{ github.sha }}
- name: Deploy to staging
run: kubectl set image deployment/churn-model app=registry/churn-model:${{ github.sha }}The evaluate step is a quality gate: if the new model's AUC drops below the threshold (compared to the champion model in MLflow), the pipeline fails and the image is never deployed. This prevents regressions from reaching production.A etapa de avaliação é um quality gate: se o AUC do novo modelo cair abaixo do limite (comparado ao modelo campeão no MLflow), o pipeline falha e a imagem nunca é implantada. Isso impede que regressões cheguem à produção.
Deployment StrategiesEstratégias de Deploy
Blue/Green DeploymentDeploy Blue/Green
Run two identical environments — blue (current) and green (new). After deploying the new model to green and validating it with a subset of traffic, switch the load balancer. Rollback is instant: flip back to blue.Execute dois ambientes idênticos — blue (atual) e green (novo). Após implantar o novo modelo no green e validá-lo com um subconjunto do tráfego, troque o load balancer. O rollback é instantâneo: volte para o blue.
Canary ReleasesReleases Canary
Route a small percentage of traffic (5–10%) to the new model version. Monitor key metrics — prediction latency, error rate, and business metrics like conversion rate — before progressively increasing the percentage.Direcione uma pequena porcentagem do tráfego (5–10%) para a nova versão do modelo. Monitore métricas chave — latência de predição, taxa de erros e métricas de negócio como taxa de conversão — antes de aumentar progressivamente a porcentagem.
Shadow ModeModo Shadow
The new model receives all production traffic but its predictions are discarded. This lets you compare the new model's outputs against the champion model on real data, without any user impact. Invaluable for validating a major model architecture change before going live.O novo modelo recebe todo o tráfego de produção, mas suas predições são descartadas. Isso permite comparar as saídas do novo modelo com o modelo campeão em dados reais, sem impacto ao usuário. Inestimável para validar uma grande mudança de arquitetura de modelo antes do lançamento.
Recommendation: For business-critical models (fraud detection, pricing, personalization), always use canary releases with a minimum 24-hour observation window. Aggregate metrics can hide problems that only surface over a full daily traffic cycle.Recomendação: Para modelos críticos para o negócio (detecção de fraude, precificação, personalização), sempre use canary releases com uma janela de observação mínima de 24 horas. Métricas agregadas podem esconder problemas que só aparecem em um ciclo de tráfego diário completo.
Model Serving at Scale: Batch vs. Real-TimeServindo Modelos em Escala: Batch vs. Tempo Real
Real-Time (Online) InferenceInferência em Tempo Real (Online)
Low-latency prediction on demand. Served via a REST or gRPC endpoint. Requirements: p99 latency under 100ms, horizontal scaling, stateless containers. Tools: FastAPI + Kubernetes, AWS SageMaker, BentoML, Triton Inference Server.Predição de baixa latência sob demanda. Servida via endpoint REST ou gRPC. Requisitos: latência p99 abaixo de 100ms, escalabilidade horizontal, containers sem estado. Ferramentas: FastAPI + Kubernetes, AWS SageMaker, BentoML, Triton Inference Server.
Batch InferenceInferência em Batch
Generate predictions for a large dataset on a schedule (e.g., score all customers nightly for the next day's campaign). Requirements: throughput over latency, cost-efficiency, checkpoint/restart. Tools: Apache Spark, AWS Batch, Kubernetes Jobs, Airflow DAGs.Gere predições para um grande conjunto de dados de forma agendada (ex: pontuar todos os clientes à noite para a campanha do dia seguinte). Requisitos: throughput acima de latência, eficiência de custo, checkpoint/restart. Ferramentas: Apache Spark, AWS Batch, Kubernetes Jobs, Airflow DAGs.
Architecture tip: For batch inference at scale, run inference in parallel chunks using Spark or Ray. Pre-load the model once per executor, not once per row. A 10GB model loaded 10 million times is a performance disaster.Dica de arquitetura: Para inferência batch em escala, execute a inferência em chunks paralelos usando Spark ou Ray. Carregue o modelo uma vez por executor, não uma vez por linha. Um modelo de 10GB carregado 10 milhões de vezes é um desastre de performance.
The Feature StoreO Feature Store
As models multiply, the same features get computed independently in multiple pipelines — different implementations, different bugs, different latencies. A Feature Store centralizes feature computation and serves them to both training pipelines (offline store) and inference endpoints (online store, typically sub-millisecond Redis/DynamoDB).À medida que os modelos se multiplicam, as mesmas features são computadas independentemente em múltiplos pipelines — implementações diferentes, bugs diferentes, latências diferentes. Um Feature Store centraliza a computação de features e as serve tanto para pipelines de treinamento (offline store) quanto para endpoints de inferência (online store, tipicamente Redis/DynamoDB abaixo de 1ms).
- Offline store: Historical feature values for training (Parquet on S3, Delta Lake, BigQuery).Offline store: Valores históricos de features para treinamento (Parquet no S3, Delta Lake, BigQuery).
- Online store: Latest feature values for real-time serving (Redis, DynamoDB, Cassandra).Online store: Últimos valores de features para serving em tempo real (Redis, DynamoDB, Cassandra).
- Point-in-time correctness: Training data uses the feature values as they existed at prediction time — not the future-leaking values. This is the most subtle and costly bug in ML pipelines.Correção point-in-time: Os dados de treinamento usam os valores de features como existiam no momento da predição — não os valores com vazamento futuro. Este é o bug mais sutil e custoso em pipelines de ML.
Key TakeawaysPontos Principais
- Automate the full pipeline — data ingestion to deployment. Manual steps are reliability risks.Automatize o pipeline completo — da ingestão de dados ao deploy. Passos manuais são riscos de confiabilidade.
- Track every experiment in MLflow. Never deploy an artifact without a registered version.Rastreie cada experimento no MLflow. Nunca implante um artefato sem uma versão registrada.
- Package the full preprocessing pipeline inside the model artifact to prevent training-serving skew.Empacote o pipeline de pré-processamento completo dentro do artefato do modelo para evitar training-serving skew.
- Use quality gates in CI to catch model regressions before they reach production.Use quality gates no CI para detectar regressões de modelo antes que cheguem à produção.
- Deploy with canary releases and shadow mode for high-stakes models.Implante com canary releases e modo shadow para modelos de alto risco.
- Invest in a Feature Store as soon as you have more than 3–4 models in production.Invista em um Feature Store assim que tiver mais de 3–4 modelos em produção.