I’m not a Python developer. (Well, not yet anyway. 😅)
But recently, I started out on creating a Python/Django application.
I had just one (large) problem: navigating the world of Python versions and dependencies.
I know I have to learn things like
venv, but what’s the best practice? What’s the best way to install this stuff on my laptop?
Whenever you’re learning something new, doing all the initial setup work like this can be a drag.
In fact, it can be a drain on your energy, when all you want to do is open up an IDE and start coding. Personally, if I have to do too much of this stuff, I just get bored and move on to something else.
So, since I wanted to get something up and running quickly, I thought about creating a development environment using a tool that I already know: containers.
With a tool like Docker or Podman, I can create a containerised development environment, which contains everything I need to code my app, without having to install it all on my laptop.
It’s basically using a container as a single-purpose virtual machine. A VM that I can create again and again, whenever I need a development environment to work in.
Join me on this little journey as I show you how I did it.
Step 1: Declare Python dependencies
My first step is to declare all of my Python dependencies.
I’m going to do this in a
Later, we’ll use
pip, the Python package manager, to read this file, and install all my requirements into the container.
So, since I’m writing a Django app, my
./requirements.yml looks like this. I’m also declaring a couple of other packages too:
Django>=3.0,<4.0 shortuuid>=1.0.1 gunicorn
Next step: write the Dockerfile to build a container image, with my requirements installed.
Step 2: Write the build instructions
We all love a nice Dockerfile.
I’m going to use a container as a virtual development environment. So I want to write a Dockerfile to build a container image, beginning with the version of Python that I want, and then add all of my dependencies into it.
My starting point is the python:3.9-alpine image on Docker Hub.
(It’s a “Docker Official” image on Docker Hub, so I have, uhhhhh, some confidence about its quality and security. Better to use this than an unofficial one.)
So here’s the first line of my
Then I change the working directory to
/usr/src/app. This tells Docker where to run the remaining commands. So I add this command:
Next, I copy the
requirements.txt file that I just wrote, and put it in the container. I run
pip install, which will download all the packages I need. I add these commands:
COPY requirements.txt . RUN pip install -r requirements.txt
And that’s all I need for my development environment. I’ve made sure that my
requirements.yml is in there, and that Docker should run
pip to install the dependencies.
Next, we’ll build the image.
Step 3: Build the image
So let’s build the image! This will create the image that we’ll use to spin up a container from.
docker build -t myapp-dev .
Or if you’re using Podman:
podman build -t myapp-dev .
This will build an image called
The next step is to create a container, and get coding!
Step 4: Run the container
So we have our container image, or Docker image, containing Python and the requirements that we need.
To start the environment, I run a container, and tell it to launch a command prompt (
docker run -it --name myapp --rm \ --volume $(pwd):/usr/src/app \ --net=host myapp-dev:latest \ sh
There are a few things going on this command, so I think they’re worth calling out:
I’m using a bind mount (
--volume ...), to make the current directory (PWD) on my host operating system also available inside the container. This will allow me to edit files on my host, and have them available inside the container, in real time.
- If you’re using SELinux, you might need to add the
:zmodifier to the end, e.g.
- If you’re using SELinux, you might need to add the
I give the container a name (myapp), because I like to call my containers by their proper names! I also add
--rm, so that when the container terminates, it’ll be deleted (although my source code files will still exist on my host OS).
--net=host, which allows the container to share my host network. So, when I run my Python web site in the container, I will be able to access it on my host operating system, using the localhost address.
And now I can start my project!
Step 5: Doing the coding
Here’s a quick example of how I use the container as a development environment for Django apps.
I start by creating a Django project, running
django-admin, inside the container shell:
# django-admin startproject mysite # cd mysite/ # ls -al total 16 drwxr-xr-x 3 root root 4096 Mar 9 18:55 . drwxr-xr-x 3 root root 4096 Mar 9 18:55 .. -rwxr-xr-x 1 root root 662 Mar 9 18:55 manage.py drwxr-xr-x 2 root root 4096 Mar 9 18:55 mysite
Now I can edit the Python source files on my host operating system (e.g. with PyCharm or Visual Studio Code), and they’ll be instantly visible in the container.
Finally, I boot up the Django app, by running this command inside the container:
# python manage.py runserver
Which gives me a bunch of output:
Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. March 09, 2021 - 19:03:00 Django version 3.1.7, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
And I will now be able to access the Django homepage on my host, using http://localhost:8000
When I’m done with the container, I just press Ctrl+C to terminate the Python process, and then Ctrl+D to exit the container entirely.
When I want to restart the development environment again, I can just use the same
docker run command above.
Hope this has been helpful for you! Happy Pythoning :)
Got any thoughts on what you've just read? Anything wrong, or no longer correct? Sign in with your GitHub account to leave a comment.
(All comments get added as Issues in our GitHub repo here, using the comments tool Utterances)