How to Dockerize a Python Django App for AWS EC2 with AWS RDS as the Database and Secure it with HTTPS using Let’s Encrypt

How to Dockerize a Python Django App for AWS EC2 with AWS RDS as the Database and Secure it with HTTPS using Let’s Encrypt

Dockerizing a Python Django application, deploying it on AWS EC2, and securing it with HTTPS using Let’s Encrypt is a robust way to ensure scalability, manageability, and security. This guide will walk you through each step in detail.

Prerequisites

Before starting, ensure you have the following:

  1. AWS Account: Access to the AWS Management Console.

  2. Docker: Installed on your local machine.

  3. AWS CLI: Configured with appropriate permissions.

  4. EC2 Instance: Running with appropriate security groups.

  5. RDS Instance: Configured and running with PostgreSQL (or another preferred database).

Step 1: Setting Up Your Django Project

First, let's create a Django project. If you already have one, you can skip to the next step.

django-admin startproject myproject
cd myproject

Step 2: Writing the Dockerfile

Create a Dockerfile in your project root directory:

# Use the official Python image from the Docker Hub
FROM python:3.9

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set the working directory
WORKDIR /app

# Install dependencies
COPY requirements.txt /app/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

# Copy the Django project
COPY . /app/

# Run the application
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]

Step 3: Creating the docker-compose.yml

Create a docker-compose.yml file to define the services for your application:

version: '3.8'

services:
  web:
    build: .
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - db

  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

volumes:
  postgres_data:

Step 4: Configuring Environment Variables

Create a .env file to store your environment variables:

DEBUG=1
SECRET_KEY=your-secret-key
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1] your-ec2-public-dns
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=your-database-name
SQL_USER=your-database-user
SQL_PASSWORD=your-database-password
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres

Step 5: Updating Django Settings

Modify your Django settings.py file to use environment variables for database configuration:

import os
import dj_database_url

DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get('DATABASE_URL')
    )
}

# Update the ALLOWED_HOSTS setting
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS').split(' ')

# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Step 6: Building and Running Docker Containers

Build and run your Docker containers using Docker Compose:

docker-compose up --build

Your Django app should now be running in a Docker container.

Step 7: Preparing for AWS Deployment

Before deploying to AWS, ensure you have created an RDS instance and noted down the endpoint, database name, username, and password.

Step 8: Configuring AWS RDS in Django

Update your .env file with your AWS RDS details:

SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=your-rds-database-name
SQL_USER=your-rds-username
SQL_PASSWORD=your-rds-password
SQL_HOST=your-rds-endpoint
SQL_PORT=5432

Step 9: Deploying to AWS EC2

  1. SSH into your EC2 Instance:

     ssh -i path/to/your-key.pem ec2-user@your-ec2-instance-public-dns
    
  2. Install Docker on EC2:

     sudo yum update -y
     sudo amazon-linux-extras install docker
     sudo service docker start
     sudo usermod -a -G docker ec2-user
    
  3. Clone your project from your repository:

     git clone your-repository-url
     cd your-repository-directory
    
  4. Transfer environment variables to EC2:

     scp -i path/to/your-key.pem .env ec2-user@your-ec2-instance-public-dns:/path/to/your-project
    
  5. Build and run Docker containers on EC2:

     docker-compose up --build -d
    

Step 10: Configuring Security Groups

Ensure your EC2 instance's security group allows inbound traffic on port 8000 (or your configured port). Similarly, configure your RDS instance's security group to allow traffic from your EC2 instance.

Step 11: Securing the Site with HTTPS using Let’s Encrypt

To secure your site with HTTPS, use Let's Encrypt and Certbot.

  1. Install Certbot on EC2:

     sudo amazon-linux-extras install epel
     sudo yum install -y certbot python2-certbot-nginx
    
  2. Stop the Docker container temporarily:

     docker-compose down
    
  3. Run Certbot to obtain an SSL certificate:

     sudo certbot certonly --standalone -d your-domain-name --preferred-challenges http
    

    Follow the prompts to obtain the certificate. Certbot will generate certificates and place them in /etc/letsencrypt/live/your-domain-name/.

  4. Create a Dockerfile for Nginx:

    Create a new file nginx/Dockerfile:

     FROM nginx:latest
    
     COPY nginx.conf /etc/nginx/nginx.conf
     COPY ssl/ /etc/letsencrypt/live/your-domain-name/
    
  5. Create Nginx Configuration File:

    Create a new file nginx/nginx.conf:

     server {
         listen 80;
         server_name your-domain-name;
    
         location / {
             proxy_pass http://web: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;
         }
    
         location /.well-known/acme-challenge/ {
             root /var/www/certbot;
         }
     }
    
     server {
         listen 443 ssl;
         server_name your-domain-name;
    
         ssl_certificate /etc/letsencrypt/live/your-domain-name/fullchain.pem;
         ssl_certificate_key /etc/letsencrypt/live/your-domain-name/privkey.pem;
    
         location / {
             proxy_pass http://web: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;
         }
     }
    
  6. Update docker-compose.yml:

    Update your docker-compose.yml to include the Nginx service:

     version: '3.8'
    
     services:
       web:
         build: .
         command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
         volumes:
           - .:/app
         env_file:
           - .env
         depends_on:
           - db
    
       db:
         image: postgres:13
         volumes:
           - postgres_data:/var/lib/postgresql/data/
         environment:
           - POSTGRES_DB=${POSTGRES_DB}
           - POSTGRES_USER=${POSTGRES_USER}
           - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    
       nginx:
         build:
           context: ./nginx
         volumes:
           - certbot-etc:/etc/letsencrypt
         ports:
           - "80:80"
           - "443:443"
         depends_on:
           - web
    
     volumes:
       postgres_data:
       certbot-etc:
    
  7. Build and run your services:

     docker-compose up --build -d
    

Conclusion

By following these steps, you can dockerize your Django application, configure it to use AWS RDS as the database, deploy it on AWS EC2, and secure it with HTTPS. This setup not only simplifies deployment but also ensures your application is scalable, manageable, and secure.

Did you find this article valuable?

Support Ahmad W Khan by becoming a sponsor. Any amount is appreciated!