Docker for Machine Learning: A Deep Dive into Layers and Deployment

Docker
Machine Learning
Deployment
Author

Ravi Kalia

Published

March 3, 2025

Docker for Machine Learning: A Deep Dive into Layers and Deployment

Introduction

Docker has revolutionized software deployment by making applications portable and reproducible. For machine learning practitioners, Docker provides an efficient way to package models, dependencies, and runtime environments into lightweight, consistent containers. This article explores how Docker works, with a special focus on its layered filesystem and an end-to-end example of deploying a machine learning model using Flask.

Understanding Docker Layers

Docker uses a layered filesystem to optimize storage, speed up builds, and ensure efficient image management. Each instruction in a Dockerfile creates a new read-only layer, and the final container has a writable layer on top.

How Layers Work

  • Each RUN, COPY, or ADD command in a Dockerfile creates a new layer.
  • Layers are cached, meaning if a layer does not change, Docker reuses it to speed up builds.
  • The topmost layer in a running container is writable, while all underlying layers remain read-only.

Example: Dockerfile and Its Layers

# Base Image (Layer 1)
FROM python:3.10  

# Create a working directory (Layer 2)
WORKDIR /app  

# Copy files into the container (Layer 3)
COPY requirements.txt .  

# Install dependencies (Layer 4)
RUN pip install -r requirements.txt  

# Copy rest of the code (Layer 5)
COPY . .  

# Run the application (Layer 6, but doesn’t persist in images)
CMD ["python", "app.py"]

How Docker Builds This

Each instruction adds a layer to the final image:

Step Instruction Layer Type Cached?
1 FROM python:3.10 Base Image Yes
2 WORKDIR /app Metadata Yes
3 COPY requirements.txt . File System Yes (if unchanged)
4 RUN pip install -r requirements.txt Execution Yes (if no changes in requirements.txt)
5 COPY . . File System Yes (if no changes in files)
6 CMD ["python", "app.py"] Metadata (no new layer) Yes

Optimizing Docker Builds with Caching

The order of Dockerfile instructions matters for caching. To make the most of layer reuse:

  • Place frequently changing instructions at the end of the Dockerfile.
  • Separate dependency installation from application code to reuse dependency layers.
  • Use .dockerignore to exclude unnecessary files from the build context.

Example: Deploying a Machine Learning Model with Docker

This section demonstrates how to train a machine learning model, build a Flask API to serve it, and package everything in a Docker container.

Project Structure

ml-docker-example/
│── model.pkl                  # Pre-trained ML model
│── app.py                      # Flask API for predictions
│── requirements.txt            # Python dependencies
│── Dockerfile                  # Instructions to build the image
│── .dockerignore               # Files to ignore

Train and Save the Machine Learning Model

import pickle
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# Load dataset
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)

# Train model
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)

# Save model
with open("model.pkl", "wb") as f:
    pickle.dump(model, f)

print("Model trained and saved as model.pkl")

Run this script to generate model.pkl:

python train_model.py

Create Flask API to Serve Predictions

from flask import Flask, request, jsonify
import pickle
import numpy as np

# Load trained model
with open("model.pkl", "rb") as f:
    model = pickle.load(f)

app = Flask(__name__)

@app.route("/predict", methods=["POST"])
def predict():
    data = request.get_json()
    features = np.array(data["features"]).reshape(1, -1)
    prediction = model.predict(features).tolist()
    return jsonify({"prediction": prediction})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Define Dependencies

flask
numpy
scikit-learn

Dockerfile

FROM python:3.10

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

EXPOSE 5000

CMD ["python", "app.py"]

Create .dockerignore

__pycache__/
*.pyc
*.pyo
*.pkl

Build and Run the Docker Container

docker build -t ml-flask-app .
docker run -p 5000:5000 ml-flask-app

The Flask server is now running inside the container and listens for requests on http://localhost:5000/predict.

Make a Prediction Request

Using curl:

curl -X POST http://localhost:5000/predict -H "Content-Type: application/json" -d '{"features": [5.1, 3.5, 1.4, 0.2]}'

Using Python:

import requests

data = {"features": [5.1, 3.5, 1.4, 0.2]}
response = requests.post("http://localhost:5000/predict", json=data)
print(response.json())

Deploying to the Cloud

To deploy the model on a cloud service, push the image to Docker Hub or AWS ECR:

docker tag ml-flask-app mydockerhubusername/ml-flask-app:latest
docker push mydockerhubusername/ml-flask-app:latest

Then, pull and run it on a cloud server or Kubernetes cluster.

Conclusion

Docker provides an efficient way to package and deploy machine learning models, ensuring consistency across environments. By leveraging Docker’s layered filesystem, caching, and optimization strategies, machine learning practitioners can significantly improve build times and resource efficiency. Understanding how layers work allows for better container management and deployment strategies in production environments.