Building releases in Docker¶
There are many times where you want to build a release which targets an environment other than your local machine. In this situation, it is recommended to build your release in a Docker container which matches your target machine’s OS, kernel version, architecture, and system libraries. Docker makes this trivial to accomplish with fairly minimal effort.
Creating a build container¶
The general approach is to write a
Dockerfile which sets up the image that
your release builds will take place in. This can be refreshed when system
libraries need to be updated, or for OS upgrades. An example of such an image
might look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
FROM ubuntu:16.04 ENV REFRESHED_AT=2018-08-16 \ LANG=en_US.UTF-8 \ HOME=/opt/build \ TERM=xterm WORKDIR /opt/build RUN \ apt-get update -y && \ apt-get install -y git wget vim locales && \ locale-gen en_US.UTF-8 && \ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && \ dpkg -i erlang-solutions_1.0_all.deb && \ rm erlang-solutions_1.0_all.deb && \ apt-get update -y && \ apt-get install -y erlang elixir CMD ["/bin/bash"]
We can build our image with the following command in the same directory as the
$ docker build -t elixir-ubuntu:latest .
To actually build a release for our app, we need to mount the source code for our app, mount a directory for the release tarball to be output to, then execute a script which will build the release and copy the release tarball to that directory.
First, our build script, in
bin/build, would look something like this:
bin/build is marked executable, with
chmod +x bin/build
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/usr/bin/env bash set -e cd /opt/build APP_NAME="$(grep 'app:' mix.exs | sed -e 's/\[//g' -e 's/ //g' -e 's/app://' -e 's/[:,]//g')" APP_VSN="$(grep 'version:' mix.exs | cut -d '"' -f2)" mkdir -p /opt/build/rel/artifacts # Install updated versions of hex/rebar mix local.rebar --force mix local.hex --if-missing --force export MIX_ENV=prod # Fetch deps and compile mix deps.get # Run an explicit clean to remove any build artifacts from the host mix do clean, compile --force # Build the release mix release # Copy tarball to output cp "_build/prod/rel/$APP_NAME/releases/$APP_VSN/$APP_NAME.tar.gz" rel/artifacts/"$APP_NAME-$APP_VSN.tar.gz" exit 0
To build our release and output it to
rel/artifacts, we need to run the
$ docker run -v $(pwd):/opt/build --rm -it elixir-ubuntu:latest /opt/build/bin/build
This command will start our build container, execute the build script, then
exit. Once we are back at the command prompt, you should be able to see the
produced release tarball in the output of
If you add dependencies that require system packages, you will need to update
Dockerfile for the build container, and rerun the
docker build command
to update it.
The tarball produced with this method can then be deployed to the target, without needing to install Erlang on the target. The only requirement is that any system packages the release depends on have been installed.