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.
Accordingly to their own definition, Python is a programming language:
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 is a high-level Python web framework. It’s free and open source.
Docker is a platform to build run and share applications using the idea of containers.
- Python installed
- pip installed
- docker installed
- 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
In some cases, you need to add a 3 at the end of Python:
verify that pip is installed
verify that docker is installed
install django-admin using pip
pip install django-admin
use django-admin to create your Django application, using this command:
django-admin startproject python_docker
get into the folder:
and open it using your favorite IDE. For example, if you use Yosual Studio Code, you can type
and your project should look like this:
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:
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
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
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
now let's create a file called Dockerfile (capital D).
A Dockerfile is a simple yet powerful text file, with the format:
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)
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
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