Elixir v1.9 will ship with releases support and in this blog post we want to show how we have used this exciting new feature on the Hex.pm project.
Installing Elixir master
(Update: This section is no longer relevant since v1.9 is already out!)
Since Elixir v1.9 is not out yet, we need to use the development version. Locally, my preferred approach is to use the Elixir plugin for the asdf-vm version manager.
Here’s a couple of ways we may use asdf to install recent development versions:
# install latest master
$ asdf install elixir master
$ asdf local elixir master
# or, install particular revision:
$ asdf install elixir ref:b8b7e5a
$ asdf local elixir ref:b8b7e5a
Per “Deployment” section of mix release
documentation:
A release is built on a host, a machine which contains Erlang, Elixir, and any other dependencies needed to compile your application. A release is then deployed to a target, potentially the same machine as the host, but usually separate, and often there are many targets (either multiple instances, or the release is deployed to heterogeneous environments).
We deploy Hex.pm using Docker containers and we needed to change our Dockerfile. If you’re deploying using buildpacks (e.g. to Heroku or Gigalixir), it should be as simple as setting elixir_version=master
in your elixir_buildpack.config
.
Setting up releases
Elixir 1.9 ships with two new Mix tasks to work with releases:
-
mix release.init
– generates sample files for releases -
mix release
– builds the release
The sample files generated by mix release.init
are optional, if they are not present in your project then the release will be built with default options.
On Hex.pm, previously we were building releases using Distillery and to work with Elixir releases we needed to make a few small tweaks. Here are the main ones:
- add
:releases
section tomix.exs
– this is an optional step but since we don’t deploy on Windows, we only need to generate executable files for UNIX-like systems - replace
rel/vm.args
withrel/vm.args.eex
- replace
rel/hooks/pre_configure
withrel/env.sh.eex
- add
config/releases.exs
for runtime configuration of the release - remove Distillery dependency (remember to
mix deps.unlock
it!)
See the “Replace Distillery with Elixir releases” PR on Hex.pm repo for more details.
We now have a few files that deal with configuring our app/release, let’s take a step back and see what they can do:
-
config/prod.exs
– provides build-time application configuration -
config/releases.exs
– provides runtime application configuration. We’re using the newConfig
module and theSystem.fetch_env!/1
function, also introduced in Elixir v1.9.0, to conveniently return the environment variable if set, or raise an error. -
rel/vm.args.eex
– provides a static mechanism for configuring the Erlang Virtual Machine and other runtime flags. For now, we use the defaults but if down the line we’d tune the VM, we’d set the options here. -
rel/env.sh.eex
– provides a dynamic mechanism for setting up the VM, runtime flags, and environment variables.
RELEASE_NODE
and RELEASE_COOKIE
variables are used by the release script, see “Environment variables” section in the documentation for all recognized variables. The POD_A_RECORD
variable we have there is specific to our deployment environment on Hex.pm, we deploy it to Google Kubernetes Engine.
See “Application configuration” and “vm.args and env.sh (env.bat)” sections for more information.
Finally, we use the mix release
task to actually assemble the release:
$ mix release
* assembling hexpm-0.0.1 on MIX_ENV=dev
* using config/releases.exs to configure the release at runtime
* creating _build/dev/rel/hexpm/releases/0.0.1/vm.args
* creating _build/dev/rel/hexpm/releases/0.0.1/env.sh
Release created at _build/dev/rel/hexpm!
# To start your system
_build/dev/rel/hexpm/bin/hexpm start
Once the release is running:
# To connect to it remotely
_build/dev/rel/hexpm/bin/hexpm remote
# To stop it gracefully (you may also send SIGINT/SIGTERM)
_build/dev/rel/hexpm/bin/hexpm stop
To list all commands:
_build/dev/rel/hexpm/bin/hexpm
Running the release
The generated release script (bin/hexpm
) has many commands:
$ _build/dev/rel/hexpm/bin/hexpm
Usage: hexpm COMMAND [ARGS]
The known commands are:
start Starts the system
start_iex Starts the system with IEx attached
daemon Starts the system as a daemon
daemon_iex Starts the system as a daemon with IEx attached
eval "EXPR" Executes the given expression on a new, non-booted system
rpc "EXPR" Executes the given expression remotely on the running system
remote Connects to the running system via a remote shell
restart Restarts the running system via a remote command
stop Stops the running system via a remote command
pid Prints the OS PID of the running system via a remote command
version Prints the release name and version to be booted
In our Hex.pm deployment we have used two of these commands for now:
-
bin/hexpm start
– we use it as the start command to be run in our Docker container -
bin/hexpm eval
– we use it to run DB migrations and other maintenance scripts. For migrations, the command is:bin/hexpm eval 'Hexpm.ReleaseTasks.migrate()'
.
Summary
In this blog post we’ve walked through using Elixir releases on an existing project, Hex.pm. We’ve installed the development version of Elixir, configured the release, and adjusted our deployment setup to use it. Hex.pm was previously using Distillery, and with minimal changes we were able to update it to use built-in releases support.
Overall, I’m very happy about this change. We’ve ended up with about the same amount of configuration code, but I think it’s a little bit better structured and more obvious.
I especially like new conventions around configuration. Where previously we used workarounds like config :app, {:system, "ENV_VAR"}
and "${ENV_VAR}"
(and REPLACE_OS_VARS=true
), we now have a clear distinction between build-time and runtime configuration. mix release
documentation does a really good job of explaining configuration aspects in particular but also the whole release process in general.
Building the release is now faster too, on my machine ~2.5s now vs ~5.5s before. Granted, it’s probably the least concern but it’s a nice cherry on top nonetheless.
As of this writing, Hex.pm is already deployed using Elixir releases. Now your turn – try out releases on your project! (And if something goes wrong, submit an issue!)