How to Develop Your Python Docker Applications Faster

ethanjjackson

Ethan J. Jackson

Posted on July 2, 2020

How to Develop Your Python Docker Applications Faster

Docker has many benefits that make deploying applications easier. But the process of developing Python with Docker can be frustratingly slow. That's because testing your Python code in Docker is a real pain.

Luckily, there's a technique you can use to reduce time you spend testing. In this tutorial, we'll show you how to use Docker's host volumes and runserver to make developing Python Docker applications easier and faster.

(If you're a Node.JS developer, see How to Develop Your Node.Js Docker Applications Faster.)

How Host Volumes and Runserver Can Speed Up Your Python Development

As every Python developer knows, the best way to develop your application is to iterate through short, quick cycles of coding and testing. But if you're developing using Docker, every time you change your code, you're stuck waiting for the container to rebuild before you can test.

As a result, you end up with a development workflow that looks like this:

  • You make a change.
  • You wait for the container to rebuild.
  • You make another change.
  • You wait some more.

And if your team uses CI/CD, so you're constantly running your code through automated tests? You're going to be spending even more time waiting for the container to rebuild.

Coding and waiting and coding and waiting is not a recipe for developer productivity -- or developer happiness.

But there's a way to modify a container's code without having to rebuild it. The trick is to use a Docker host volume.

Host volumes sync file changes between a local host folder and a container folder. If you use a host volume to mount the code you're working on into a container, any edits you make to your code on your laptop will automatically appear in the container. And as you will see in the next section, you can use the runserver package to automatically restart your application without having to rebuild the container -- a technique known as "live reloading."

The result: instead of wasting lots of time waiting for your containers to rebuild, your code-test-debug loop is almost instantaneous.

Example: Using Host Volumes and Runserver in Python Docker Development

The idea of using a host volume to speed up your Python coding might seem a little daunting, but it's pretty straightforward.

To demonstrate this, let's use a Python example: django-polls, a basic poll app that’s part of Django’s introductory tutorial. To clone the repo:

$git clone https://github.com/kelda/django-polls

The repo assumes you are using Docker Compose. You can also use
Blimp, our Compose alternative that scales to the cloud.

Here's the docker-compose.yml file for django-polls:

version: '3'
services:
  web:
    build: .
    command:
      - sh
      - -c
      - "./wait-for-postgres.sh && python manage.py migrate ; python manage.py shell < init-db.py ; python manage.py runserver 0.0.0.0:8000"
    ports:
      - "8000:8000"
    depends_on:
      - db
    volumes:
      - ".:/code"
  db:
    image: "postgres:12"
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=polls
      - POSTGRES_PASSWORD=polls
      - POSTGRES_DB=polls 

This file tells Docker to boot a container, the Django application, and a Postgres database where the application stores the poll. It also tells Docker to mount a host volume:

volumes:
    - ".:/code"

As a result, Docker will mount the ./ directory on your laptop, which contains the code you're developing, into the container at /code.

Next, you need to set up your Docker container so that whenever you edit your code, Docker automatically restarts your Python application. That way, your application will always use the latest version of your code.

If you are creating a Django app, the easiest way to do that is to have your .yml file tell Docker to use runserver, Django's development web server:

  web:
    build: .
    command:
      - sh
      - -c
      - "./wait-for-postgres.sh && python manage.py migrate ; python manage.py shell < init-db.py ; python manage.py runserver 0.0.0.0:8000"

As a result, whenever you modify your code on your laptop, runserver restarts the process without rebuilding the container.

In short, by using a host volume and runserver, you can set up your Python application's container so it automatically syncs code changes between the container and your laptop. If you didn't do this, you'd have to rebuild the container every single time you made a change to your code.

Over time, this technique can substantially speed up your Python development. For example, we've heard from users that it's not uncommon for container rebuilds to take 5-30 minutes. With host volumes and runserver, your code sync is almost instantaneous. Imagine what your day would look like if you could save yourself 5-30 minutes every time you modify and test your code.

Syncing Your Own Code When Developing a Python Application

Now that you've seen how to use this technique in a sample application, the rest of this tutorial will show you how to enable code syncing in one of your existing Python projects.

Prerequisites

Just like the example above, your Python project should include the following:

  • A git repo that contains your code
  • A Dockerfile that builds that code into a working container
  • A docker-compose.yml file you use to run that container

How to Configure Your Container to Automatically Sync Your Python Code

1) Locate the folder in your Docker container that has your code. The easiest way to figure out where your code is stored in your container is to look at your Dockerfile's COPYcommands. In the django-polls example, you can see from the Dockerfile that the container expects the code to be in /code:

FROM python:3
RUN apt update && apt install -y netcat && rm -rf /var/lib/apt/lists/*
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install --no-cache-dir -r requirements.txt 

2) Find the path to the folder on your laptop that has the same Python code.

3) Add a host volume to your docker-compose file. Find the container in your docker-compose file that you want to sync code with, and add a volume instruction underneath that container:

volumes:
  "/path-to-laptop-folder:/path-to-container-folder"

4) Make sure your Docker Compose file configures your container for live reloading. In the django-poll example, you implemented it by using runserver as your web server:

  web:
    build: .
    command:
      - sh
      - -c
      - "./wait-for-postgres.sh && python manage.py migrate ; python manage.py shell < init-db.py ; python manage.py runserver 0.0.0.0:8000"

5) Run Docker Compose or Blimp. Now all you need to do is either run docker-compose:

$ docker-compose up

Or if you're using Blimp:

$ blimp up

As a result, Docker will update the container's code with the code that's on your laptop.

Now that your container is set up to use a host volume and runserver, whenever you modify the Python code on your laptop, your new code will automatically appear in the container.

Conclusion

At first, the idea of using host volumes to sync the Python code on your laptop with your container might seem a little weird. But once you get used to this workflow, you'll see how much more efficient it is. With just a few tweaks to your Docker containers' set up, developing your Python Docker app is easier and faster.

Resources

Try the Python example on Blimp

Read How to Develop Your Node.Js Docker Applications Faster

Read about common mistakes with host
volumes

that can slow down your application

Check out Blimp, our team's project to improve developer productivity for Docker Compose.

💖 💪 🙅 🚩
ethanjjackson
Ethan J. Jackson

Posted on July 2, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related