DEV Community

Cover image for Comprehensive Guide to Setting Up Load Balancing with Traefik, Docker, Django, and React
Ezechiel Kiregha
Ezechiel Kiregha

Posted on

Comprehensive Guide to Setting Up Load Balancing with Traefik, Docker, Django, and React

This documentation covers the successful setup and configuration of load balancing for Docker services using Traefik 2. It demonstrates how to serve a Django API backend and a React frontend through Traefik, with detailed explanations of configurations and implementation steps.


Backend Configuration

Backend 1 Dockerfile

# Backend 1 Dockerfile
FROM python:3.12-alpine

# Prevent Python from writing bytecode and buffer output
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set work directory
WORKDIR /app

# Copy project files
COPY . .

# Create virtual environment and install dependencies
RUN python3 -m venv /opt/venv1 && \
    /opt/venv1/bin/pip install --no-cache-dir --upgrade pip && \
    /opt/venv1/bin/pip install --no-cache-dir -r requirements.txt

# Create entrypoint script
RUN echo '#!/bin/sh' > /entrypoint.sh && \
    echo 'set -e' >> /entrypoint.sh && \
    echo '/opt/venv1/bin/python manage.py makemigrations --noinput' >> /entrypoint.sh && \
    echo '/opt/venv1/bin/python manage.py migrate --noinput' >> /entrypoint.sh && \
    echo '/opt/venv1/bin/python manage.py collectstatic --noinput' >> /entrypoint.sh && \
    echo 'exec "$@"' >> /entrypoint.sh && \
    chmod +x /entrypoint.sh

# Entrypoint
ENTRYPOINT ["/entrypoint.sh"]
Enter fullscreen mode Exit fullscreen mode

Django Configuration in settings.py

ALLOWED_HOSTS = ["*"]

CORS_ALLOWED_ORIGINS = [
    "http://localhost",
    "http://localhost:80",
]

CORS_ALLOW_METHODS = [
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
]

CORS_ALLOW_HEADERS = [
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
]
Enter fullscreen mode Exit fullscreen mode

Frontend Configuration

Frontend Dockerfile

# Base image
FROM node:20-alpine AS builder

# Set build-time environment variable
ARG REACT_APP_API_URL
ENV REACT_APP_API_URL=${REACT_APP_API_URL:-/}

# Set working directory
WORKDIR /opt/web

# Copy package files and install dependencies
COPY package*.json ./
RUN npm install

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM nginx:latest

# Copy Nginx configuration template
COPY nginx.conf /etc/nginx/default.conf

# Copy built assets from builder stage
COPY --from=builder /opt/web /usr/share/nginx/html

# Expose port 80
EXPOSE 80

# Default command
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

Nginx Configuration

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html;
            try_files $uri /index.html;
        }

        location /static/ {
            root /usr/share/nginx/html;
        }

        location /api/ {
            proxy_pass http://traefik:80;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Docker Compose Configuration

docker-compose.yml

services:
  traefik:
    image: traefik:latest
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks:
      - portfolio-network

  frontend__24673_ui:
    build:
      context: ./frontend-elearning
      dockerfile: Dockerfile
    volumes:
      - static_volume:/usr/share/nginx/html/static
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend__24673_ui.rule=PathPrefix(`/`)"
      - "traefik.http.services.frontend__24673_ui.loadbalancer.server.port=80"
    networks:
      - portfolio-network
    depends_on:
      - backend_24673_1
      - backend_24673_2

  backend_24673_1:
    build: ./backend-1
    command: /opt/venv1/bin/gunicorn -b 0.0.0.0:8000 --worker-class=gevent --worker-connections=1000 --workers=3 elearning.wsgi
    volumes:
      - static_volume:/app/static
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.backend_24673_1.rule=PathPrefix(`/api`)"
      - "traefik.http.services.backen_24673_1.loadbalancer.server.port=8000"
      - "traefik.http.middlewares.backend_24673_1-stripprefix.stripprefix.prefixes=/api"
      - "traefik.http.routers.backend_24673_1.middlewares=backend_24673_1-stripprefix"
    networks:
      - portfolio-network

  backend_24673_2:
    build: ./backend-1
    command: /opt/venv1/bin/gunicorn -b 0.0.0.0:8001 --worker-class=gevent --worker-connections=1000 --workers=3 elearning.wsgi
    volumes:
      - static_volume:/app/static
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.backend_24673_2.rule=PathPrefix(`/api`)"
      - "traefik.http.services.backend_24673_2.loadbalancer.server.port=8001"
      - "traefik.http.middlewares.backend_24673_2-stripprefix.stripprefix.prefixes=/api"
      - "traefik.http.routers.backend_24673_2.middlewares=backend_24673_2-stripprefix"
    networks:
      - portfolio-network

volumes:
  static_volume:

networks:
  portfolio-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Axios API Configuration

Axios Instance

import axios from 'axios';

const baseurl = process.env.REACT_APP_API_URL || '/';

const AxiosInstance = axios.create({
    baseURL: baseurl,
    timeout: 10000,
    headers: {
        "Content-Type": 'application/json',
        Accept: 'application/json',
    },
});

// Adding an Axios interceptor
AxiosInstance.interceptors.response.use(
    response => response, // Pass through successful responses
    async error => {
        const config = error.config;
        if (!config._retry) {
            config._retry = true; // Mark request as retried
            return AxiosInstance(config); // Retry the request
        }
        return Promise.reject(error); // Reject if retry also fails
    }
);

export default AxiosInstance;
Enter fullscreen mode Exit fullscreen mode

This documentation provides the necessary configuration and setup steps to implement a load-balanced, containerized deployment of a Django-React application with Traefik. Each component is explained for clarity, ensuring reproducibility for similar projects.

Top comments (0)