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.