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.
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), andcreated_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.
- Migrations are used to apply changes to the database schema. They are created automatically when you modify models and run
- 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: