Writing Dockerfiles

Trial and error—but faster!

Why use Docker?

At my last job, I developed on Windows and deployed on Linux. There were multiple production VMs running different operating systems with different versions. This made deployment annoying and tedious as certain apps would work on some machines but not others. Additionally, there was a large application that would not install on fresh machines.

Docker fixes a lot of these issues. To deploy a dockerized application, just copy it to the machine and run something like docker-compose up. Easy!

How?

Just write a Dockerfile ;-)

But really, the way I started was by copying and pasting an example Dockerfile given in Docker’s tutorial.

Here is the modified example:

# Use an official Python runtime as a parent image.
# I've had problems with Alpine (failing to build node-canvas 
# because of musl), but Debian hasn't failed me yet.
FROM python:3.6.8-jessie

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Run main.py when the container launches
CMD ["python", "main.py"]

This Dockerfile can be built into an image with:

docker build -t names-are-hard .

Docker will create an image with the creative tag “names-are-hard”. Of course, for the build to work you will need a couple other files. For a full, working example checkout this repo.

To run the image:

docker run names-are-hard

For your first attempt at writing a Dockerfile, trial and error is fine. However, the cycle (make a change, rebuild, run…) can be tedious. If you’re not sure about what commands Docker should run, use -it to run an interactive session.

docker run -it names-are-hard bash

This command runs the image, but instead of executing main.py you will be dropped into a bash prompt inside the container. This is very useful for testing commands before committing them to a Dockerfile. For instance, if you forgot the pip install line in the Dockerfile then this mistake will be very obvious when you try to run main.py from the bash prompt.

A screenshot of the command line. I tried to run main.py from inside the container but received a traceback “ModuleNotFoundError”

This also works with base images–very handy if you want to quickly try something on a specific OS.

docker run -it ubuntu /bin/bash

Docker compose

One of the nice things about JavaScript projects is the widespread use of npm and the standardization that brings. Installing dependencies is almost universally npm i and often a development server can be started with npm run dev. Different projects can use different tools but a small set of npm run commands is usually sufficient. This is in contrast to Python projects I’ve worked with where starting a development server requires reading the docs.

docker-compose up reminds me of npm run dev, it can be used as a universal command for starting a Dockerized application. Also faster to type (vs building then running the image).

To get docker-compose up working add a basic .docker-compose.yml file:

version: "3"
services:
  scraper:
    build: .