[043] Releases with Relex

Making your Elixir application redistributable using Erlang releases.

Subscribe now

Releases with Relex [05.13.2016]

In Erlang, the unit of deployment is a release. These consist of an Erlang runtime system, as well as all of the required OTP applications in order to boot the system.

The point of a release is that you should be able to distribute it, and someone without erlang or elixir on their machine should be able to use it to launch your application.

The standard way to prepare a release is to build a .rel file, but there's a nice third party tool called Relex for Elixir that allows you to declare your release more easily. There's also a tool called Pogo that adds a few more niceties as well as a shell script to launch the release more easily. We'll be using both of these.

Project

You need to have a proper OTP application in order to build a release, and our SupervisedListServer fits the bill. There's a copy of it in this episode's resources. We'll just extract it to get started:

tar zxvf 037_supervised_list_server_with_exlager.tar.gz
cd supervised_list_server

This app was built specifying an earlier version of elixir, so open up mix.exs and remove the line that specifies the elixir version.

Now, the first thing we need to do is add our two tools as dependencies. Open up mix.exs and add the following:

  # Returns the list of dependencies in the format:
  # { :foobar, "~> 0.1", git: "https://github.com/elixir-lang/foobar.git" }
  defp deps do
    [
      {:exlager, github: "khia/exlager"},
      {:relex, github: "interline/relex", branch: "start_clean"},
      {:pogo, github: "onkel-dirtus/pogo"}
    ]
  end

Now we're using this fork of Relex to get the start_clean behaviour. If it ever gets merged in, you won't need to use this branch.

Go ahead and install the dependencies with mix deps.get.

Next, we need to define the release. Open up mix.exs. The first thing we have to is load the dependencies into our application manually, since we'll be using them inside the mix file itself. At the top of the file, add the following:

Code.append_path "_build/shared/lib/relex/ebin/"
Code.append_path "_build/shared/lib/pogo/ebin/"

Next, we'll define the release. This is done in a module, called SupervisedListServer.Release. Right below the two lines we just added, add the following:

# This keeps this from breaking the mixfile before you've installed the dependencies...
if Code.ensure_loaded?(Relex.Release) do
  defmodule SupervisedListServer.Release do
    use Relex.Release
    use Pogo.Release

    def name, do: "supervised_list_server"
    def applications, do: [:pogo, :supervised_list_server]
  end
end

That's almost enough to assemble the release. We still have to tell the project what its release is called, so that Relex can reflect on that to build the release. Add an option to the project function in the mixfile for that:

  def project do
    [ app: :supervised_list_server,
      version: "0.0.1",
      elixirc_options: options(Mix.env),
      deps: deps,
      release: SupervisedListServer.Release
    ]
  end

That's it - that's all we have to do to get the release to build. However, we're going to make a couple of changes. Right now, starting our application doesn't start the ListSupervisor at the top of our ListServer's supervision tree. Let's do that so that when you boot our app, the ListServer's running and ready. Open up lib/supervised_list_server/supervisor.ex and add the ListSupervisor as a child:

  def init([]) do
    children = [
      # Define workers and child supervisors to be supervised
      worker(ListSupervisor, [])
    ]

    # See http://elixir-lang.org/docs/stable/Supervisor.Behaviour.html
    # for other strategies and supported options
    supervise(children, strategy: :one_for_one)
  end

That's it. Now when we start the application, the ListSupervisor is started as well. Let's go ahead and assemble the release. This is a mix task provided by Relex. It doesn't compile the application before it assembles the release - I feel like it should. So you need to compile it before assembling the release. Run:

mix compile
mix relex.assemble

Alright, now your release has been assembled. You can find it in the directory supervised_list_server. To run it, just run ./supervised_list_server/bin/supervised_list_server. That will give you a list of the commands available for this shell script. If this were a web server or something, you could just run it with start and it would be running. We don't have an easy way of making sure our app's working without an iex session, so you can launch the release with a console using ./supervised_list_server/bin/supervised_list_server console.

You're given an iex shell. It's important to point out that this is not using your machine's erlang or elixir runtimes - this is all provided in the release, standalone. That's the whole point.

Now you can verify that our app's running properly in the release. Try it out:

ListServer.add :foo
ListServer.items
ListServer.crash
ListServer.items

Summary

That's releases in a nutshell. I had a lot of fun figuring them out, and it brings us one step closer to building proper deployments of our software. See you soon!

Resources

Attachments