In this article, we will see how to dockerize a Django Python application.
What is the advantage of Dockerizing your Python application?
- you can run the app without installing Python.
- no version clash on hosts with a different version of Python installed.
- no need to install dependencies.
- no need to remember which is the command to run your Python app.
The last one can sound trivial for pure Python developers but think about a big system made by microservices and using 3/4 different languages with different versions.
We just want to run our application, and probably with different commands for development and production.
Python
Accordingly to their own definition, Python is a programming language:
- high-level
- interpreted
- general-purpose
Its design emphasizes code readability with its use of significant indentation (no semicolons).
Its language constructs as well as its object-oriented approach aim to help programmers write clear and logical code.
Django
Django is a high-level Python web framework. It’s free and open source.
Docker
Docker is a platform to build run and share applications using the idea of containers.
Step By Step guide
Prerequisites:
- Python installed
- pip installed
- docker installed
Steps:
- verify prerequisites
- install django-admin with pip
- create a Django hello world app
- run Django app locally (without Docker)
- create requirements.txt file
- create the Dockerfile, docker-compose.yml, .dockerignore
- build and run in just one command
verify prerequisites
python --version
In some cases, you need to add a 3 at the end of Python:
verify that pip is installed
pip --version
verify that docker is installed
docker version
install django-admin with pip
install django-admin using pip
pip install django-admin
create a Django hello world app
use django-admin to create your Django application, using this command:
django-admin startproject python_docker
get into the folder:
cd python_docker
and open it using your favorite IDE. For example, if you use Yosual Studio Code, you can type
code .
and your project should look like this:
run Django app locally (without Docker)
Run your Django project locally without docker using this command:
python manage.py runserver
(replace with python3 if you have that version)
Now visit localhost:8000 on your browser:
create requirements.txt file
Before creating the Docker image, let's create a requirements.txt file for this project.
For simplicity, we will use the command pip freeze and redirect the output to a file called requirements.txt
pip freeze -l > requirements.txt
the -l option (short for --local) is to install only the local dependencies.
Note that the best way to do this is to create a virtual environment first, here is a link
https://docs.python.org/3/library/venv.html
Docker
Let's start the process of containerization for our Python application.
- create .dockerignore file
- create Dockerfile
- create docker-compose.yml file
- build and run the service with just one command
- final test
.dockerignore
in the same folder, create a file called .dockerignore (starts with a dot)
this file, in a way similar to .gitignore, is to ignore some file when we will copy files inside the Docker image. For example, we don't want the .git folder in our image, so we can populate the .dockerignore file with
.git
Dockerfile
now let's create a file called Dockerfile (capital D).
A Dockerfile is a simple yet powerful text file, with the format:
KEY value
KEY value
KEY value
Let's populate the Dockerfile like this:
FROM python:3
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python","manage.py","runserver","0.0.0.0:8000"]
Let's explain line by line what's happening here:
FROM python:3 FROM is to set the base image, in this case python:3 in a production-ready environment we can use a more fine-grained version
ENV PYTHONUNBUFFERED=1 We are setting an environment variable with a key=PYTHONUNBUFFERED to 1. Setting PYTHONUNBUFFERED to a non-empty value ensures that the python output is sent straight to the terminal without being first buffered, so you can see the output of your application in real-time.
WORKDIR /code We are setting the default directory for the subsequent COPY and CMD commands. This line is not mandatory but is to avoid the app being at the root level of the Docker Image filesystem
COPY requirements.txt . This will copy the requirements.txt file in the folder defined as workdir
RUN pip install -r requirements.txt This will install the python dependencies INSIDE the Docker Image
COPY . . This command will copy all the file and folders (included the Dockerfile) in the Image filesystem. al files and folders included in the .dockerignore file will be ignored
EXPOSE 8000 This command is to inform Docker that you will use the port 8000 as an external note. (Note: this command is not mandatory, as long as you publish the port when you run the container)
CMD ["python","manage.py","runserver","0.0.0.0:8000"] this will set the default command to run when we will run the container based on this image. this replaces the command we previously used, and it makes transparent for the deployment (you don't have to memorize which was the command to run the python application)
Docker compose and docker-compose.yml file
An easy and comfortable way to run our hello world application is to use Docker Compose, even if we have just a small app. In case we add a database or another service later, we are set already!
Create a docker-compose.yml file and populate it like this:
version: "3.9"
services:
django:
image: django-hello-world:0.0.1
build: .
ports:
- "8000:8000"
Here we are defining:
- a single service (container) called "django"
- the service is based on an image called "django-hello-world:0.0.1". if this image is not available locally, Docker will build it (feel free to rename this image name)
- build: . mind the dot! this is the path we are using to build our image
- ports: here we are defining the port we want to publish.
That's it. Very simple but it allows us to avoid long docker build command on the command line
Your folder structure should look like this
Build and run in just one command
Now some magic. Just run
docker compose up --build
the --build option is to force to recreate the image if it's not available already. It's not really needed the first time, since the image is not available, but it's useful during development to just make some changes and to test if everything works test
Now visit 127.0.0.1:8000 (ignore the log message it can be deceiving)
Thank you for reading, you can follow me on Twitter here: https://twitter.com/intent/follow?screen_name=FrancescoCiull4
GitHub Repository
https://github.com/FrancescoXX/python-docker