Developing with Docker and Make

Kyle Martineau-McFarlane
4 March 2019


Introduction

Software developers often find themselves needing to work in assorted different languages. Installing the language runtimes locally can lead to a bloated, unmaintainable development machine full of odd versions of Node, unnecessary web servers you installed a while ago to test some PHP or a redundant database running because you needed to remind yourself of the syntax MySQL uses to create a new table index.

Recently, I needed to learn some Lua to develop some extensions for an nginx server. I've not worked with Lua before and wanted a clean sandbox in which to work through some Lua tutorials. Rather than installing the Lua runtime for something I will probably not need to do again, I took a leaf out of John Mackenzie's book and use Docker and a Makefile to isolate my Lua deployment.

The goal

My goal is to have a Makefile which allows me to do the following:

  • run a source code file in Lua and see the resulting output
  • run the Lua command-line entry environment
  • modify and maintain my code locally on my development machine

Doing this is easy enough to do. We need a source Docker image from which to start, some source code, and a simple Makefile with a few commands.

The code

src/

We need a simple piece of code to test, and in the traditional way, this is src/main.lua which contains a single line:

print("Hello, world")

Docker image

The first step was a quick check of Docker Hub for a suitable source image. There are several similar images which have Lua installed; I chose jimho/lua.

Makefile

The core of the work is done in a simple Makefile. Its contents are largely self-explanatory but will be explained afterwards:

CONTAINER = "learnlua"
DOCKER_SOURCE = "jimho/lua"

run: stop
  docker run --volume ./src:/code  --name $(CONTAINER) $(DOCKER_SOURCE) lua /code/main.lua
repl: stop
  docker run --volume ./src:/code -it --name $(CONTAINER) $(DOCKER_SOURCE) lua
stop:
    docker stop $(CONTAINER) || true
    docker rm $(CONTAINER) || true

At the top of the Makefile, we define a variable to store our container's name and the name of the source image. The first two rules, run and repl, will run a Docker container mounting src/ to /code/; in the case of run, the main.lua file will be executed and the container will close afterwards. In the case of repl, a REPL prompt will appear.

Both of these run stop before executing; this allows me to ensure that pre-existing docker containers are removed before we try to start a new one. The || true part ensures that, if there is no pre-existing container, make doesn't report an error.

Result

The resulting - simple - Makefile allows me to modify my code, run make run, and see my code running. Similarly, I can run make repl and have an interactive command line at my disposal. All of this works without me needing to pollute my local machine with all the extra bits and pieces needed to run a language runtime I'm only going to need once.

A similar Makefile can easily be created for almost any other programming language - as long as there's a Docker image out there, you can use this approach to modularise and segment your environment to allow for the experimentation and mucking about that we geeks so love to do.