Serving and deploying models

MLflow · Advanced · 12 min

The point of packaging a model is to run it somewhere else with the same behaviour.

Any pyfunc-compatible model can be served without writing a web app:

mlflow models serve -m "models:/churn@champion" -p 5001

This starts a server (using an environment built from the model's requirements, or the current one with --env-manager local) with endpoints:

  • POST /invocations for predictions, accepting JSON such as {"dataframe_split": {"columns": [...], "data": [[...]]}}, {"dataframe_records": [...]}, {"inputs": ...} for tensors, plus optional "params";
  • GET /ping and /health for health checks; /version.

For containers, mlflow models build-docker -m <uri> -n churn-model builds an image that serves the model on port 8080, ready for Kubernetes or a cloud container service. mlflow models generate-dockerfile emits the Dockerfile instead, so you can customise it.

Other deployment paths:

  • Batch: load with mlflow.pyfunc.load_model in a job, or mlflow.pyfunc.spark_udf to score Spark DataFrames in parallel.
  • Cloud targets: deployment plugins for SageMaker, Azure ML, Databricks and others via mlflow deployments.

Before deploying, check the model in isolation with mlflow models predict -m <uri> -i input.json or mlflow.models.predict(...), which runs it in a fresh environment and catches missing dependencies early.

Serve by alias URI so redeploying picks up the new champion; pin the version in the image tag for traceability.

Example

# Shell:
#   mlflow models serve -m "models:/iris-clf@champion" -p 5001 --env-manager local
#   mlflow models build-docker -m "models:/iris-clf@champion" -n iris-clf:v3
import json
import urllib.request

payload = {
    "dataframe_split": {
        "columns": ["sepal length (cm)", "sepal width (cm)",
                    "petal length (cm)", "petal width (cm)"],
        "data": [[5.1, 3.5, 1.4, 0.2], [6.7, 3.0, 5.2, 2.3]],
    }
}
req = urllib.request.Request(
    "http://127.0.0.1:5001/invocations",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json"},
)
# with urllib.request.urlopen(req) as resp:
#     print(json.loads(resp.read()))      # {'predictions': [0, 2]}
print(json.dumps(payload)[:40])

Remember

  • mlflow models serve exposes /invocations for any pyfunc model
  • build-docker packages model plus server into an image
  • Validate with mlflow models predict in a fresh environment before deploying

Watch out for

  • Sending a bare list of rows to /invocations; the payload must use a key such as dataframe_split, dataframe_records or inputs.

Check yourself

  1. Which request body shape does the /invocations endpoint accept for tabular data?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: mlflow.org/docs/latest/ml/deployment/deploy-model-locally/

Builds on

Unlocks

Nothing builds on this yet.