Migrating a Python Django DRF Monolith to Microservices - Part 2: Dockerizing the Microservices

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...

Containerization is a crucial step in preparing your microservices for deployment. By using Docker, we can package each microservice with its dependencies, ensuring consistency across development, testing, and production environments. In this part, we will:
Write Dockerfiles for each microservice.
Use docker-compose for local development.
Optimize the Docker images with multi-stage builds.
Set up a shared network for microservices to communicate seamlessly.
By the end of this guide, your microservices will be containerized and ready for orchestration with Kubernetes in the next steps.
Docker is a platform that allows you to package applications and their dependencies into lightweight containers. Containers run consistently regardless of the underlying environment.
Why Docker?
Ensures environment consistency.
Simplifies dependency management.
Makes scaling and deployment easier.
Dockerfile: Instructions to build a Docker image.
Image: A lightweight, standalone package of software.
Container: A runtime instance of an image.
Docker Compose: A tool to define and run multi-container applications.
Each microservice will have its own Dockerfile. Let’s start with the User Service.
Base Image: Use a lightweight Python image for better performance.
FROM python:3.10-slim
Working Directory: Set the working directory inside the container.
WORKDIR /app
Dependencies: Install required Python packages from requirements.txt.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Application Code: Copy the service code into the container.
COPY . .
Command: Run the Django development server (or Gunicorn in production).
CMD ["gunicorn", "user_service.wsgi:application", "--bind", "0.0.0.0:8000"]
Final Dockerfile:
# Dockerfile for User Service
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "user_service.wsgi:application", "--bind", "0.0.0.0:8000"]
Repeat the same process for the Trading Service:
# Dockerfile for Trading Service
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "trading_service.wsgi:application", "--bind", "0.0.0.0:8000"]
Multi-stage builds help reduce the size of the final Docker image by separating the build and runtime environments.
Example for User Service:
# Multi-stage Dockerfile
FROM python:3.10-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /app /app
COPY . .
CMD ["gunicorn", "user_service.wsgi:application", "--bind", "0.0.0.0:8000"]
To simplify running multiple services locally, we use Docker Compose.
docker-compose.yml FileThe docker-compose.yml file defines the configuration for all microservices, including networking and volumes.
Example for User and Trading Services:
version: '3.8'
services:
user_service:
build:
context: ./user_service
ports:
- "8001:8000"
environment:
- DATABASE_URL=postgres://user:password@db:5432/user_service_db
depends_on:
- db
trading_service:
build:
context: ./trading_service
ports:
- "8002:8000"
environment:
- DATABASE_URL=postgres://user:password@db:5432/trading_service_db
depends_on:
- db
db:
image: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: user_service_db
ports:
- "5432:5432"
Start the services:
docker-compose up --build
Access the User Service:
http://localhost:8001/api/users/.Validate Containers:
Check running containers:
docker ps
Test APIs:
Use curl or Postman to test the endpoints:
curl -X GET http://localhost:8001/api/users/
curl -X POST http://localhost:8002/api/trades/
Inspect Logs:
View container logs for debugging:
docker logs user_service
Keep Images Small:
Use multi-stage builds.
Avoid installing unnecessary packages.
Environment Variables:
Health Checks:
Add health checks in docker-compose.yml to ensure services are running:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
Shared Volumes:
Use volumes for sharing data between services or persisting database data:
volumes:
- db_data:/var/lib/postgresql/data
At the end of Part 2, your microservices are containerized using Docker, and you have a working local setup using Docker Compose. You’ve learned how to:
Write efficient Dockerfiles.
Use Docker Compose to manage multiple containers.
Optimize images for production.
Next Steps: In Part 3, we will deploy these Dockerized microservices to a Kubernetes cluster, setting up production-ready orchestration and scaling.
Happy Deployment!