Django Application inside a Docker Container
I have a Django application that I want to dockerize it for local development. I am also new to Docker, so everything I do in this post might not be suitable for your production environment. So please do check Docker best practices for production apps. This tutorial is meant to be a basic introduction to Docker. In this post, I am going to use Docker Machine and Docker Compose. You can get them by installing the awesome Docker Toolbox.
I assume you are already familiar with Docker and it’s use cases. If you haven’t yet started using Docker, I strongly recommend you do soon.
Before we start, we need to break down our requirements so we can individually build the required components. For my particular application, we need these:
- Django App Server
- MySQL Database Server
- Redis Server
We will build images for these separately so we can create individual containers and link them together to compose our ultimate application. We shall build our Django App server and use pre-built images for MySQL and Redis.
Building the Django App Server
Before we begin, let’s talk
Dockerfiles. Dockerfiles are scripts to customize our docker builds. It allows us control and flexibility over how we build the images for our applications. We will use our custom Dockerfile to build the Django app server.
To build an image for a Django application we need to go through these following steps:
- Select a Linux image, we choose Ubuntu
- Install required packages for the distro.
- Install Python packages which are required for the app
- Provide a default command to run and ports to expose
Here’s the Dockerfile we shall use:
FROM phusion/baseimage MAINTAINER masnun ENV DEBIAN_FRONTEND noninteractive RUN apt-get update RUN apt-get install -y python python-pip python-dev RUN apt-get install -y libxml2-dev libxslt-dev libffi-dev libssl-dev RUN apt-get install -y libmysqlclient-dev ADD requirements.txt /app/src/requirements.txt WORKDIR /app/src RUN pip install -r requirements.txt WORKDIR /app/src/lisp CMD [ "python", "manage.py", "runall"] EXPOSE 8000
So what are we doing here:
- We’re choosing
phusion/baseimageas our base image. It’s a barebone image based on Ubuntu. Ubuntu by default comes with many packages which we don’t need to run inside docker. This base image gets rid of those and provides a very lean and clean image to start with.
- We just provide a Maintainer name
- We set
DEBIAN_FRONTENDto be non interactive. This will not display any interactive prompts during the build process. Since the docker build process is automated, we really don’t have any way to interact during it. So we disable interaction. And as you might have guessed already
ENVsets an environment variable.
- We install some packages we shall need.
- We copy our
/app/src/requirements.txt, change the work directory and install the packages using pip.
ADDis used to copy any files or directories to the container while it builds. You might wonder why we didn’t copy over our entire project – that’s because we want to use docker for our development. We will use a nice featire of Docker which would allow us to mount our local directories directly inside the container. Doing this, we would not need to copy files every time they change. More on this will come later.
- We change directory to
/app/src/lispand run the
runallmanagement command. This command runs the Django default server along with some other services my application needs. But usually we would want to just do
If you go through the Dockerfile References you will notice – we can do a lot more with Dockerfiles.
Docker Compose and Linking Services
As we mentioned earlier, we shall use pre-built images for MySQL and Redis. We could build them ourselves too but why not take advantage of the well maintained images from the generous folks in the docker community?
We can link multiple docker containers to compose a final application. We can do that using the
docker command manually. But Docker Compose is a very nice tool which allows us to define the services we need in a very easy to read syntax. With docker compose, we don’t need to run them manually, we can just use simple commands to do complex docker magic! Here’s our
web: build: . restart: always volumes: - .:/app/src ports: - "8000:8000" links: - redis - mysql redis: image: redis:latest volumes: - /var/lib/redis ports: - "6379" mysql: image: mysql:latest volumes: - /var/lib/mysql ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: su53RsEc53T!
In our docker-compose file, we define 3 components:
- For the web, we pass the path to Dockerfile to
buildkey. We ask to restart always and define volumes to mount.
.:/app/srcmeans – mount the current directory on my OS X as
/app/src/on the container. We also define which ports to expose and which containers should be linked with it
- We also define the mysql and redis components with respective configurations. Note that we define the pre-built image name in the
imagekey. Please make sure the
volumepaths exist and are accessible.
You can consult the Compose File Reference for more details.
Running The Services
To run the application, we can do:
Please note, the Django server might throw errors if the MySQL / Redis server takes time to initialize. So I usually run them separately:
docker-compose start mysql docker-compose start redis ### After some time docker-compose start web
Database Configuration for Django
Our MySQL server is running on the IP of the Docker Machine. You need to use this IP address in your Django settings file. To get the IP of a docker machine, type in:
### Here `default` is the machine name docker-machine ip default
Creating Initial Databases
We can pass a
MYSQL_DATABASE environment value to the
mysql image so the database is created when creating the service. Or we can also connect to the docker machine manually and create our databases.
Django: Running management commands inside a Docker container
Okay, so we have dockerized our django app and we need to run a
manage.py command for some task. How do we do that? Simple, we have to locate the container that runs the django app, login and then run the command.
Locate The Container
It’s very likely that our app uses multiple containers to compose the entire system. For exmaple, I have one container running MySQL, one container running Redis and another running the actual Django app. If we want to run
manage.py commands, we have to login to the one that runs Django.
While our app is running, we can find the running docker containers using the
docker ps command like this:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 308f40bba888 crawler_testscript "/sbin/my_init" 31 hours ago Up 3 seconds 5000/tcp crawler_testscript_1 3a5ccc872215 crawler_web "bash run_web.sh" 31 hours ago Up 4 seconds 0.0.0.0:8000->8000/tcp crawler_web_1 14f0e260fb2c redis:latest "/entrypoint.sh redis" 31 hours ago Up 4 seconds 0.0.0.0:6379->6379/tcp crawler_redis_1 252a7092870d mysql:latest "/entrypoint.sh mysql" 31 hours ago Up 4 seconds 0.0.0.0:3306->3306/tcp crawler_mysql_1
In my case, I am using Docker Compose and I know my Django app runs using the
crawler_web image. So we note the name of the container. In the above example, that is –
Nice, now we know which container we have to login to.
Logging Into The Container
We use the name of the container to login to it, like this:
docker exec -it crawler_web_1 bash
The command above will connect us to the container and land us on a bash shell. Now we’re ready to run our command.
Running the command
cd into the directory if necessary and then run the management command.
cd /project python manage.py <command>
docker psto list running containers and locate the one
docker exec -it [container_name] bashto login to the bash shell on that container
cdto the django project and run
python manage.py [command]