Skip to content

Instantly share code, notes, and snippets.

@toolness
Last active May 1, 2018 21:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toolness/751b32c0bdcdc4affef025f8322d0c40 to your computer and use it in GitHub Desktop.
Save toolness/751b32c0bdcdc4affef025f8322d0c40 to your computer and use it in GitHub Desktop.
Super simple scaffolding for building a Django app using Docker.
version: '3'
services:
app:
build: .
volumes:
- .:/stuff
working_dir: /stuff
FROM python:3.6
COPY requirements.txt .
RUN pip install -r requirements.txt
@toolness
Copy link
Author

toolness commented Apr 30, 2018

Here is a very simple setup to start playing with Django using Docker (i.e., without having to go through installing Python and dealing with this whole mess locally).

If you haven't already, you should install Docker Community Edition.

Instructions

  1. Put all the files in this gist in a directory. Maybe call it stuff.

  2. Run docker-compose build. This will fetch the official Docker python image off the internet and then run the Dockerfile on top of it, which will install Django. What you have now is a Docker image. It's kind of like a pristine VM that contains Debian Linux, Python 3.6, and Django.

  3. Run docker-compose run app bash. You should see a bash shell, and you'll be in a directory called /stuff, which is mapped to the directory your files are in.

    You are now "inside" a Docker container, which is a temporary instantiation of the Docker image created in step 2. From here, you can run python, pip, django-admin, or a bunch of other things. You'll probably want to start following the official Django tutorial from here, if you're unfamiliar with Django itself. You can also go ahead and just use sqlite as the database for now.

Wait, did you say "temporary"?

Yup. This is one of the most unintuitive and confusing things about Docker.

Docker containers are meant to be fairly ephemeral. If you install anything via apt-get or pip while in a bash session in your container, for example, you'll notice that once you exit that bash session and start a new one with docker-compose run app bash, the thing you just installed is gone. That's because every time you run docker-compose run it creates a brand-new container, with the filesystem based off the pristine docker image created in step 2 above.

There's one exception, though: if you look at your docker-compose.yml, you'll see a volumes section. That sets up a sort of "volume mount" on the otherwise temporary filesystem: /stuff in the container is mapped to the directory with your stuff on your computer, so any changes you make to files in that directory will be reflected in your container and vice versa. That makes it persistent, unlike everything else on the filesystem.

What is this "docker-compose" thing? I thought I was going to use Docker.

There is a docker command-line tool, but it's super low-level and you will almost never need to use it. docker-compose, on the other hand, is a higher-level tool that comes built-in with the rest of Docker Community Edition, and you will be using it a lot.

You might even find it useful to make a bash alias that aliases dcr to docker-compose run, because you'll be using that one in particular a ton.

Networking

Eventually, once you've created a Django project, you'll want to start running its development server. This is where things get a bit tricky, because by default, Docker containers' network ports aren't accessible from your web browser. To make them accessible, we'll have to do a few things:

  1. Tell Docker to map port 8000 of your localhost to port 8000 of your docker container. One way to do this is by running docker-compose run -p 8000:8000 app bash. Note this is the same thing you've been running, just with -p 8000:8000 added on, which tells Docker to do the port mapping.

  2. Tell Django to bind to all network interfaces. Normally, Django's development server only binds to 127.0.0.1 on the host system, but that's not very useful when done in a Docker container. So when you run python manage.py runserver inside the Docker container, you'll want to add the argument 0.0.0.0:8000.

Once you've done those things, you should be able to visit http://localhost:8000 on your web browser and see your Django project.

Note that this is kind of cumbersome, and not very easy to remember. That's why most projects will modify their docker-compose.yml to include command and ports directives, so that a developer just has to run docker-compose up to get their server up and running. I'm leaving that as an exercise to the reader, though!

Going further

To maintain some degree of dev/prod parity you might want to use a production database backend like Postgres instead of sqlite. Actually setting this up is out of scope of this little tutorial, though, so I'll just give you some pointers on how this is done:

  • You'll want to add a new service, probably called db to your docker-compose.yml. It will probably use the image directive to pull directly from the official Postgres Docker image. Now running docker-compose up should also start up a postgres server in a separate Docker container, which is nifty.

  • The Postgres docker container started in docker-compose up has a hostname on the little mini Docker network that exists inside your computer. The hostname is the same as the service name, so if you used db, then telling Django to connect to a Postgres server on the host db should do the trick. You might need to tweak a few things on the Postgres side, but the documentation for the official Postgres Docker image has all kinds of details on environment variables you can set to configure things differently.

  • You might be wondering where your Postgres container is storing all its data. That's a very good question, and the answer is out of scope of this little tutorial. This time I'm not even going to post a link for further information because I have to go to a meeting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment