Dockerizing a Django + MySQL project
Tags: django, docker
Published: 2020-10-05 13:52:22

If you are here, I assume you have already embraced the merits of using Docker to set up your development environment such that it matches your staging and production environments. 

In this guide I show you how to run your Django app in one Docker container and your MySQL database in another and make them talk to each other.

We are going to manage and run these two containers using [Docker Compose](https://docs.Docker.com/compose/).

👉 If you want to learn how to do this for a Django + Postgres app read [this post](https://foadmoha.com/blog/dockerize-django-postgres-project.html).

I am assuming you already have a Django project locally that is using a local database and that your project has a `requirements.txt` file. You should also [install Docker on your machine](https://docs.Docker.com/get-Docker/) if you haven't already.

Let's dive right in:

The first step is to create a `Dockerfile` in the root of your project (this is where you have your `requirements.txt` and `manage.py` files).
``` Dockerfile
FROM python:3.8
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
COPY . /app
```

Let's go through that line by line:
* `FROM python:3.8` is the base image your Docker image will be built upon. This will be pulled from DockerHub.
* `ENV PYTHONUNBUFFERED 1` ensures that the output Django writes to the terminal comes out in real time without being buffered somewhere. This makes your Docker logs useful and complete.
* `WORKDIR /app` creates a new folder in your container called `app` which will be your project's root inside the container. It then sets that as the work directory in which the subsequent commands will be executed in.
* `COPY requirements.txt /app/requirements.txt` copies your local `requirements.txt` file to the Docker container.
* `RUN pip install -r requirements.txt` will make sure you have all your project dependencies installed inside the container.
*  `COPY . /app` and finally it's time to copy your project's content into the Docker container.

Now create a new file in your local project root. This one is called `docker-compose.yml` and is used for configuring Docker Compose.

``` yml
version: '3'
services:
  db:
    image: mysql:8
    ports:
      - "3306:3306"
    environment:
      - MYSQL_DATABASE='mydatabase'
      - MYSQL_USER='root'
      - MYSQL_PASSWORD='some_password'
      - MYSQL_ROOT_PASSWORD='some_password'
      - MYSQL_HOST=''
    volumes:
      - /tmp/app/mysqld:/var/run/mysqld
      - ./db:/var/lib/mysql
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    ports:
      - "8000:8000"
    volumes:
      - .:/app
      - /tmp/app/mysqld:/run/mysqld
    depends_on:
      - db
```

This defines two services: **db** and **web**.


**db** uses an official MySQL image from DockerHub. We attach local port 3306 to port 3306 of this container. We then set a bunch of environment variables that are required for MySQL. Make sure you also set these in your Django database settings. (You can put these in a `.env` file to avoid committing sensitive information to version control. Read [here](https://docs.docker.com/compose/compose-file/#env_file) to learn how.) 

`volume` define two volume mappings: 
* The first one maps the contents of `var/run/mysqld` of the container to a local folder on your machine. This file contains socket information that enables the **web** service (your Django app) to talk to the database service. 
* `./db:/var/lib/mysql` creates a local `db` folder in your project root so that database information can be saved if you destroy the **db** service.

**web** build its image using the `Dockerfile` we created earlier. It then runs the Django server and exposes port 8000 to your machine. We map your local project folder to the app folder and we bring in the socket file. We tell Docker Compose that this service is dependant on the **db** service.

Now all we have to do is to start everything up. In your project root run:

``` bash
$ docker-compose up
```

This should build your images and start the containers. Open a browser and go to `0.0.0.0:8000`. You should see your app. Now if you open a new terminal and run a `docker ps` you will see the **web** and **db** containers are running:

``` bash
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                        NAMES
77a320233bf2        my_project_web      "python manage.py ru…"   4 minutes ago       Up 4 minutes        0.0.0.0:8000->8000/tcp       my_project_web_1
66324c20d91e        mysql:8             "docker-entrypoint.s…"   4 minutes ago       Up 4 minutes        0.0.0.0:3306->3306/tcp       my_project_db_1
```

If you want to stop Docker Compose, press *Ctrl+C* in the first terminal or run `docker-compose stop` in the project root folder in another terminal.

To run `manage.py` commands in the Django container (**web**):

``` bash
$ docker-compose run --rm web python manage.py migrate
```

<a href="https://chipper-musician-9131.ck.page/71c2932d23">![signup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rx6dciscpdwtk3z6j4u6.png)</a>