Guide to Deploying Machine Learning Models with Django and Docker to AWS

Search for a command to run...

No comments yet. Be the first to comment.
There's a question that most people feel but almost nobody says out loud. Not because it's complicated. Because saying it threatens everything built on top of not saying it. The question is simple. Wh

Public discourse around personal finance in India increasingly relies on lifestyle narratives, absolute numbers, and loosely imported benchmarks. Concepts such as middle class, financial security, high income, or wealthy are often used without refere...

By Ahmad W Khan (interactive at https://ahmadwkhan.com/india-work-landscape) Summary (TL;DR): India’s labour market is vast, diversified, and uneven. Government roles remain tenure‑rich but hard to enter; healthcare and licensed professional practice...

Audience: Intermediate PHP devs (comfortable with OOP, Composer, basic MVC) who are new/rusty with SymfonyOS Assumptions: macOS/Linux primary; Windows notes included (PowerShell + WSL2)Target PHP & Symfony: PHP 8.2+ and Symfony 7.3.x (current stable ...

Money, unlike geography, is invisible.We know where a country starts and ends on a map. But wealth and income? They’re like air currents, everywhere, yet hard to see. Percentiles help make them visible: where do you sit compared to your neighbors, yo...

Deploying machine learning models is a crucial step in transforming your trained models into usable applications. Deployment enables you to serve predictions via an API, integrate them with web applications, and make them accessible to end-users. This guide explores how to deploy machine learning models using Django REST Framework (DRF) and Docker. We will cover setting up a Django project, creating endpoints for model prediction, containerizing the application with Docker, deploying it to AWS, setting up CI/CD, and scaling the deployment.
Model deployment is essential because it:
Transforms models into usable applications: It enables models to serve predictions to users or other systems.
Enables integration: Models can be integrated with web applications, mobile apps, or other services.
Facilitates scalability and maintenance: Deployment allows for scaling model usage and maintaining version control.
Provides Real-time Access: Deployed models can provide real-time predictions, improving decision-making processes.
Operationalizes Machine Learning: Deployment brings machine learning models into production, allowing them to impact business processes and operations.
Django REST Framework (DRF): A powerful and flexible toolkit for building Web APIs in Django. It makes it easy to create RESTful APIs and provides various features for serialization, authentication, and view handling.
Docker: A platform for developing, shipping, and running applications inside containers. Docker ensures that applications run consistently across different environments by packaging all dependencies and configurations together.
Before starting, ensure you have the following installed on your system:
Python 3.8+
pip (Python package installer)
Docker
Git
Virtual Environment: To keep dependencies isolated.
Create a Virtual Environment: Creating a virtual environment helps to isolate your project dependencies. This ensures that the packages you install for one project do not interfere with those of another project.
python -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
Install Django and DRF: After activating the virtual environment, install Django and Django REST Framework using pip.
pip install django djangorestframework
Create a Django Project: Use the django-admin command to start a new Django project.
django-admin startproject ml_deploy
cd ml_deploy
Create a Django App: Within the project directory, create a new app called api.
python manage.py startapp api
Add api to INSTALLED_APPS in settings.py: Open ml_deploy/settings.py and add 'api' and 'rest_framework' to the INSTALLED_APPS list.
INSTALLED_APPS = [
...
'rest_framework',
'api',
]
Create a Serializer (api/serializers.py): Serializers in DRF are used to convert complex data types, such as querysets and model instances, into native Python data types that can then be easily rendered into JSON, XML, or other content types.
from rest_framework import serializers
class PredictionSerializer(serializers.Serializer):
input_data = serializers.ListField(
child=serializers.FloatField()
)
Create a View (api/views.py): Create a view to handle POST requests, which will accept input data, process it using the machine learning model, and return the prediction.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import PredictionSerializer
import pickle
class PredictView(APIView):
def post(self, request, *args, **kwargs):
serializer = PredictionSerializer(data=request.data)
if serializer.is_valid():
input_data = serializer.validated_data['input_data']
prediction = self.make_prediction(input_data)
return Response({'prediction': prediction}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def make_prediction(self, input_data):
# Load your pre-trained model
model_path = 'path/to/your/model.pkl'
with open(model_path, 'rb') as file:
model = pickle.load(file)
prediction = model.predict([input_data])
return prediction[0]
Create URLs (api/urls.py): Define the URL pattern for the prediction endpoint.
from django.urls import path
from .views import PredictView
urlpatterns = [
path('predict/', PredictView.as_view(), name='predict'),
]
Include URLs in Project URLs (ml_deploy/urls.py): Include the api URLs in the main project's URL configuration.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
Input Data: The input data is handled by the PredictionSerializer, which validates and processes the incoming request data.
Output Data: The output data (model prediction) is returned as a JSON response.
Create a test case to ensure that the prediction endpoint works as expected.
from django.test import TestCase
from rest_framework.test import APIClient
from rest_framework import status
class PredictViewTestCase(TestCase):
def setUp(self):
self.client = APIClient()
def test_prediction(self):
input_data = {"input_data": [5.1, 3.5, 1.4, 0.2]}
response = self.client.post('/api/predict/', input_data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('prediction', response.data)
Create a Dockerfile: A Dockerfile contains instructions on how to build a Docker image.
# Use an official Python runtime as a parent image
FROM python:3.8-slim
# Set the working directory in the container
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Make port 8000 available to the world outside this container
EXPOSE 8000
# Define environment variable
ENV DJANGO_SETTINGS_MODULE=ml_deploy.settings
# Run the Django development server
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "ml_deploy.wsgi:application"]
Create a requirements.txt: List all the dependencies required for your Django project in a requirements.txt file.
django
djangorestframework
gunicorn
Build the Docker Image: Use the docker build command to create a Docker image from the Dockerfile.
docker build -t ml_deploy .
Run the Docker Container: Use the docker run command to start a container from the built image.
docker run -p 8000:8000 ml_deploy
Use a .dockerignore File: Exclude unnecessary files from the Docker image to keep it small and secure.
venv/
__pycache__/
.pytest_cache/
*.pyc
*.pyo
.DS_Store
Minimize Layers: Combine related commands to minimize layers in the Docker image, which can improve build times and reduce the image size.
Security: Avoid running containers as the root user. Use non-root users wherever possible to enhance security.
Multi-Stage Builds: Optimize your Dockerfile by using multi-stage builds to separate build and runtime environments, reducing the final image size.
# Stage 1: Build
FROM python:3.8-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Stage 2: Runtime
FROM python:3.8-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages
COPY . /app
EXPOSE 8000
ENV DJANGO_SETTINGS_MODULE=ml_deploy.settings
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "ml_deploy.wsgi:application"]
Choose a CI Service: Popular choices include GitHub Actions, Travis CI, CircleCI, and Jenkins.
GitHub Actions Example:
Create a .github/workflows/ci.yml file:
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install django djangorestframework
pip install -r requirements.txt
- name: Run tests
run: python manage.py test
Set Up DockerHub: Create a DockerHub repository for your project.
GitHub Actions for CD:
Create a .github/workflows/cd.yml file:
name: CD
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Log in to DockerHub
run: echo "${{ secrets.DOCKERHUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
- name: Build and push Docker image
run: |
docker build -t your-dockerhub-username/ml_deploy:${{ github.sha }} .
docker push your-dockerhub-username/ml_deploy:${{ github.sha }}
- name: Deploy to EC2
env:
DOCKER_IMAGE: your-dockerhub-username/ml_deploy:${{ github.sha }}
run: |
ssh -o StrictHostKeyChecking=no -i your-key-pair.pem ec2-user@your-ec2-public-dns << 'EOF'
docker pull $DOCKER_IMAGE
docker stop ml_deploy || true
docker rm ml_deploy || true
docker run -d -p 8000:8000 --name ml_deploy $DOCKER_IMAGE
EOF
Environment Configuration: Use environment variables to configure the application for different environments (development, staging, production). Store these variables securely, for instance, using AWS Secrets Manager or environment variable files.
Automated Tests: Ensure all tests pass before deploying to staging or production. Include unit tests, integration tests, and end-to-end tests in your CI pipeline to catch any issues early.
Staging Environment: Deploy to a staging environment first to perform final checks before production deployment. This environment should closely mirror the production environment to identify any issues that might occur in production.
Blue-Green Deployment: Implement a blue-green deployment strategy to minimize downtime and reduce risk during deployment. This involves running two identical production environments (blue and green). During deployment, traffic is routed to the new version (green), and if it performs well, it is promoted to production.
Canary Deployment: Gradually roll out new versions to a small subset of users before making it available to all users. This approach helps in identifying issues without affecting all users.
Rollback Mechanism: Implement a rollback mechanism to quickly revert to the previous stable version if an issue is detected after deployment.
Logging: Use Django’s built-in logging framework to log important events and errors. Example configuration in settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/var/log/django/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
Monitoring: Use monitoring tools like AWS CloudWatch, Prometheus, Grafana, or third-party services like Datadog or New Relic to monitor the application’s performance and resource usage. Set up alerts to notify you of any issues.
make_prediction method to load the correct model version.class PredictView(APIView):
def make_prediction(self, input_data):
model_version = 'v1.0' # Example version
model_path = f'path/to/your/model_{model_version}.pkl'
with open(model_path, 'rb') as file:
model = pickle.load(file)
prediction = model.predict([input_data])
return prediction[0]
Using Django Migrations: Django’s migration framework helps you keep track of changes to your models and propagate them to your database schema.
python manage.py makemigrations
python manage.py migrate
Handling Migrations in Docker: Ensure that migrations are run as part of the container startup process.
CMD ["sh", "-c", "python manage.py migrate && gunicorn ml_deploy.wsgi:application --bind 0.0.0.0:8000"]
Database Backups: Regularly back up your database to prevent data loss. Use AWS RDS automated backups or custom scripts to back up your PostgreSQL database.
Restore Procedures: Have a tested procedure in place to restore your database from backups in case of data loss or corruption.
Launch an EC2 Instance:
Go to the AWS Management Console.
Launch a new EC2 instance with the appropriate specifications.
Select a suitable Amazon Machine Image (AMI) (e.g., Ubuntu).
Configure Security Groups:
Open port 22 for SSH access.
Open port 8000 for accessing the Django application.
SSH into the Instance: Use your SSH key pair to connect to the EC2 instance.
ssh -i your-key-pair.pem ec2-user@your-ec2-public-dns
Install Docker: Update the package index and install Docker.
sudo apt update
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
Transfer the Docker Image: Use scp or an alternative method to transfer the Docker image to the EC2 instance, or build the Docker image directly on the EC2 instance.
Run the Docker Container: Start the Docker container on the EC2 instance.
docker run -d -p 8000:8000 ml_deploy
Install Nginx: Install Nginx to act as a reverse proxy for your Django application.
sudo apt update
sudo apt install nginx
Configure Nginx: Create a new configuration file in /etc/nginx/sites-available/ml_deploy.
server {
listen 80;
server_name your-ec2-public-dns;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable the Configuration: Create a symbolic link to enable the configuration and restart Nginx.
sudo ln -s /etc/nginx/sites-available/ml_deploy /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx
Install Certbot: Certbot is a free tool to obtain SSL certificates from Let's Encrypt.
sudo apt install certbot python3-certbot-nginx
Obtain and Configure SSL Certificate: Use Certbot to obtain and automatically configure SSL for Nginx.
sudo certbot --nginx -d your-ec2-public-dns
Install Docker Compose: Docker Compose is a tool for defining and running multi-container Docker applications.
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Create a docker-compose.yml File: Define your application services, networks, and volumes in a docker-compose.yml file.
version: '3'
services:
web:
image: ml_deploy
build: .
command: gunicorn ml_deploy.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- db
db:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
volumes:
postgres_data:
Deploy with Docker Compose: Use Docker Compose to build and start your application.
docker-compose up -d
Load Balancing: Use a load balancer (e.g., AWS Elastic Load Balancing - ELB) to distribute traffic across multiple instances of your application. This ensures high availability and reliability.
Example Nginx Configuration for Load Balancing:
upstream django_app {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 80;
server_name your-ec2-public-dns;
location / {
proxy_pass http://django_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Auto Scaling: Configure auto-scaling groups in AWS to automatically scale the number of instances based on demand. Auto-scaling ensures that your application can handle varying loads by dynamically adding or removing instances.
Increase Instance Size: Upgrade your EC2 instance type to one with more CPU and memory resources. This can improve performance for resource-intensive tasks.
Optimize Resource Usage: Ensure efficient use of CPU and memory by optimizing your application and Docker configuration. For example, use caching to reduce database load and optimize your Django application to handle requests more efficiently.
Use Gunicorn: Use Gunicorn as the WSGI server instead of the built-in Django development server. Gunicorn is more robust and suitable for production environments.
gunicorn ml_deploy.wsgi:application --bind 0.0.0.0:8000
Database Indexing: Optimize database queries and add indexes where necessary to improve query performance.
Cache Static Files: Use a Content Delivery Network (CDN) to cache static files and improve loading times for users.
Django Caching Framework: Utilize Django’s caching framework to cache views, templates, and data. This can significantly improve performance by reducing the load on the database.
Redis or Memcached: Use Redis or Memcached for caching. These in-memory data stores can cache query results, session data, and other frequently accessed data.
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
Application Metrics: Collect and analyze application metrics to monitor performance and detect issues. Tools like Prometheus and Grafana can help you visualize metrics and set up alerts.
Logging: Use a centralized logging solution like ELK Stack (Elasticsearch, Logstash, Kibana) or Graylog to collect and analyze logs from your application.
Use HTTPS: Ensure that all communication between clients and your server is encrypted using HTTPS. This protects data in transit from interception and tampering.
Environment Variables: Store sensitive information like API keys and database credentials in environment variables or secure secrets management services.
Regular Updates: Keep your Django, Docker, and other dependencies up to date with security patches to protect against vulnerabilities.
Automated Backups: Implement automated backups for your databases and critical data. Use AWS Backup or similar services to schedule regular backups and ensure data is recoverable.
Disaster Recovery Plan: Develop a disaster recovery plan that outlines steps to restore services in the event of a failure. Test the plan regularly to ensure it works as expected.
Microservices Architecture: Consider breaking your application into smaller microservices that can be developed, deployed, and scaled independently.
Serverless Computing: Use serverless computing services like AWS Lambda for specific functions or tasks within your application. This can help reduce operational overhead and scale automatically.
In this post, we explored how to deploy machine learning models using Django REST Framework and Docker on AWS. We covered setting up a Django project, creating endpoints for model prediction, containerizing the application with Docker, deploying it to an AWS EC2 instance, setting up CI/CD pipelines, streamlining deployment on staging and production environments, and scaling the deployment. By following these steps, you can efficiently deploy your machine learning models and make them accessible as APIs.
If you want to discuss your MLOps or DevOps process, then feel free to visit me at AhmadWKhan.com.