Automated alpine based erlang docker image

by Gregor Uhlenheuer on October 12, 2017

Over the last couple of weeks I have been developing an erlang application called statser that is basically a drop-in replacement of the graphite metrics server. Now that I have established a stable version that supports most of the features I initially aimed for, I want to build a docker image for statser.

Docker for statser

My first goal was to get something working first and iterate on proper automation and reasonable size of the resulting image after that.

CentOS

So my first attempt was based on CentOS 6:

The Dockerfile described above first installs the latest erlang distribution, fetches the master version of “statser” from github and starts the build by using rebar3 (which is downloaded as well) after that.

The resulting image works fine but results in a rather large image:

$ docker images
REPOSITORY                   TAG        IMAGE ID        CREATED        SIZE
kongo2002/centos-statser     latest     fd9de4025ce3    2 days ago     818 MB

Alpine linux to the rescue

My next attempt is using a minimalistic base image using alpine linux. I have used that base image already before and it serves really well to produce small docker images.

Erlang base image

At first we will need a base image that contains a working erlang build and runtime environment:

This docker image will serve as our stable erlang base image (in this case using erlang OTP 20.1). Additionally we installed ONBUILD triggers (see docker documentation) that allow us to create new build images containing the “statser” sources we actually want to create a docker image of.

Statser build image

Next we will create another docker image that actually contains the “statser” sources to package a release of:

Pretty simple, right? The trick is actually the building of the image that looks somewhat like the following:

$ docker build -t kongo2002/alpine-statser-build --file alpine-statser-build-base/Dockerfile src

Now that we built a new image based on the kongo2002/alpine-statser-build-base we just created before the build procedure will execute the ONBUILD triggers and copy the src files into the working directory of the new image.

Starting a new container of that image will build the actual “statser” release and put the build results in a mounted volume:

$ docker run --rm -v "$PWD/build:/build" kongo2002/alpine-statser-build

As soon as the container terminated with success we should have an erlang release of “statser” in the /build folder.

Statser docker image

Now all we have to do is to build a new “runtime” container that just contains the release files from the previous step:

That’s it! The reward for those few steps is a docker image that is much smaller with a total size of 37 MB:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
kongo2002/statser   latest    6931953373f6        56 minutes ago      36.7 MB

And it even works as well :D

$ docker run -it kongo2002/statser
Exec: /statser/erts-9.1/bin/erlexec -noshell -noinput +Bd -boot /statser/releases/1.0.0/statser -mode embedded -boot_var ERTS_LIB_DIR /statser/lib -config /statser/releases/1.0.0/sys.config -args_file /statser/releases/1.0.0/vm.args -pa -- foreground
Root: /statser
/statser
21:28:03.004 [info] Application lager started on node statser@325f4ca71a44
21:28:03.004 [info] initial load of configuration
21:28:03.005 [info] start listening for API requests on port 8080
21:28:03.005 [info] start listening for metrics on port 2003
21:28:03.005 [info] starting health service with update interval of 60 sec
21:28:03.005 [info] starting instrumentation service at <0.489.0>
21:28:03.005 [info] starting rate limiter [create_limiter] with limit 10/sec
21:28:03.005 [info] preparing instrumentation service timer with interval of 60000 ms
21:28:03.005 [info] starting rate limiter [update_limiter] with limit 500/sec
21:28:03.005 [info] Application statser started on node statser@325f4ca71a44
...

Conclusion

The steps described above might seem intimidating at first but after you understood the basic mechanism of ONBUILD triggers it is actually pretty straightforward. In fact the whole process can be “automated” in a simple script file in about 6 lines (without comments).

Please keep in mind I just finished this process and there may still be some flaws or ways to improve these steps for sure. Anyways, I am pretty satisfied so far - the next step will be to integrate this (or a similar mechanism) into my github build/publish.

References

This post is tagged with docker, erlang, linux, programming, alpine and statser