Automating Django App Deployment with CI/CD and Docker

29-04-2024

Introduction

This post outlines my experience deploying a Django To-Do List app using CI/CD with GitHub Actions/Github Container Registry and Docker. The goal was to automate the entire deployment process so that every code change would trigger an automatic update on my server.

images/11-DjangoToDoList.png


Overview of the Setup

The Django app was containerized using Docker, which ensured consistency across environments. I then set up GitHub Actions to handle the CI/CD pipeline. This pipeline automates the following steps:

  • Builds the Docker image for the Django app
  • Pushes the image to Docker Hub (Then switching to Github Container Registry due to limitations)
  • SSHs into my server and pulls the latest Docker image
  • Deploys the updated app on my Proxmox server

Key Python Files in the Django To-Do List App

  • settings.py:

    • This file contains the configuration settings for the entire Django project.
    • Key settings include:
      • Database Configuration: Defines the database (e.g., SQLite, PostgreSQL) the app will use. In the default Django setup, it's often set to use SQLite.
      • Installed Apps: Lists all Django apps and third-party apps that are part of the project.
      • Middleware: Specifies middleware that handles request/response processing.
      • Static & Media Files: Configures the paths for static files (CSS, JS, images) and media files (uploaded by users).
  • urls.py:

    • Defines the URL routing for the app. It maps specific URLs to the corresponding view functions.
    • Each URL pattern directs to a view that handles the logic for displaying pages like the task list, task creation, and editing.
  • models.py:

    • Defines the structure of the database tables by using Django’s ORM (Object-Relational Mapping) system.
    • In this project, a Task model would represent the to-do list tasks. Each task has fields like title, description, completed (boolean), and created_at (timestamp).
    • Django automatically generates the SQL queries based on these models.
  • views.py:

    • Contains the logic for what happens when a user interacts with different pages.
    • Each function or class-based view retrieves data from the database, processes it, and renders an appropriate HTML page.
    • Example views:
      • Task List View: Displays all tasks.
      • Task Create View: Handles adding a new task.
      • Task Update View: Allows users to edit an existing task.
      • Task Delete View: Confirms and deletes a task from the database.
  • admin.py:

    • This file registers models with the Django admin site.
    • By registering the Task model, it becomes manageable through Django's built-in admin interface, allowing you to create, update, or delete tasks from the admin panel.
  • forms.py:

    • Contains Django Form classes for handling user input (such as task creation and editing).
    • It defines forms that match the structure of the models, allowing for easy validation and processing of form data in views.
  • manage.py:

    • This is a command-line utility for interacting with your Django project.
    • Common commands include:
      • python manage.py runserver: Starts the development server.
      • python manage.py migrate: Applies database migrations.
      • python manage.py createsuperuser: Creates an admin user for the Django admin interface.

How the Database is Run

  • Django uses its ORM to interact with databases, making it easy to work with different databases without writing raw SQL.
  • Migrations:
    • Migrations are used to apply changes to the database schema. They are created automatically when you modify models and run python manage.py makemigrations.
    • These migrations are applied using python manage.py migrate, which syncs your database with the current state of your models.
  • SQLite (Default):
    • By default, Django projects use an SQLite database, which is a lightweight file-based database.
    • The database file is typically called db.sqlite3, and it resides in the root directory of your Django project.
    • For production environments, it’s common to switch to a more robust database like PostgreSQL or MySQL by changing the database settings in settings.py.

Why Use Docker and CI/CD?

Docker allowed me to package my app in a container, making it easy to run anywhere, while CI/CD with GitHub Actions automated the entire build and deployment process. This reduced manual intervention, saved time, and ensured that the app is always up-to-date.


Using GitHub Container Registry Instead of Docker Hub

Initially, I planned to use Docker Hub to store and pull the Docker image of my Django app. However, Docker Hub imposes a limit on the number of free private repositories and the number of pulls. Given these restrictions, especially when scaling or frequently updating the app, I decided to use GitHub Container Registry instead.

GitHub Container Registry (GHCR) provides a reliable and integrated solution, especially when you're already using GitHub for your code. It offers:

  • Higher pull limits compared to Docker Hub’s free tier.
  • Seamless integration with GitHub Actions for CI/CD pipelines.
  • Private container images without the additional cost of upgrading a Docker Hub account.

Running on Proxmox

I deployed the app on my Proxmox server. By using Proxmox as the host for my Docker containers, I could take advantage of its powerful virtualization features, making my deployment environment flexible and easy to manage.


Conclusion

This setup provided a smooth, automated workflow for deploying updates to my Django app. With Docker and CI/CD, I can push changes to GitHub and have them automatically reflected on my server, ensuring that the app is always live with the latest code.


Sources: