In our previous two posts, we’ve built releases manually with Distillery, and deployed an upgrade release to an existing Elixir application. As we’ve seen, this was a very hands-on process.
Thankfully, there is another tool that we can use to simplify our release process. In this post, we’ll dive into using edeliver to build and release Elixir applications.
Our Example Application
To help walk ourselves through the process of using edeliver, let’s create a new Phoenix application that we can deploy to a “production server”.
Let’s start by creating a new, basic Phoenix project:
mix phoenix.new hello_edeliver --no-ecto
We’ll be deploying this application as is, so let’s move onto the process of installing and configuring our release tools.
First, we’ll add dependencies on edeliver
, and distillery
, and add edeliver
to our projects list of applications:
def application do
[...,
applications: [..., :edeliver]]
end
defp deps do
[
...
{:edeliver, "~> 1.4.0"},
{:distillery, ">= 0.8.0", warn_missing: false}]
end
Note: edeliver is a high-level tool designed to orchestrate the creation, deployment, and management of Elixir releases. Under the covers, it can use Distillery, exrm, relx, or rebar to build release bundles.
Our application won’t use any “production secrets”, so to simplify this introduction to edeliver, let’s remove our dependency on the config/prod.secrets.exs
file by commenting out its import_config
line in config/prod.exs
:
# import_config "prod.secrets.exs"
Finally, while we’re in config/prod.exs
, let’s add a few vital configuration options to our HelloEdeliver
endpoint:
config :hello_edeliver, HelloEdeliver.Endpoint,
http: [port: {:system, "PORT"}],
url: [host: "...", port: {:system, "PORT"}],
server: true,
root: ".",
version: Mix.Project.config[:version],
cache_static_manifest: "priv/static/manifest.json"
And with that, our example application should be ready for release.
Before we move on, let’s fire up our development server with mix phoenix.server
and make sure that everything looks as we’d expect.
Configuring Distillery and edeliver
Now that our application is ready, we need to spend some time configuring our deployment tools.
First, let’s create our Distillery configuration file (rel/config.exs
) with the release.init
mix task:
mix release.init
The default configuration options provided by release.init
should be fine for our first deployment.
Next, let’s take a look at our edeliver configuration file (.deliver/config
). We’ll update the provided fields and point edeliver to a remote “build host” and “production host”:
APP="hello_edeliver"
BUILD_HOST="ec2-52-87-163-123.compute-1.amazonaws.com"
BUILD_USER="ec2-user"
BUILD_AT="/home/ec2-user/hello_edeliver/builds"
PRODUCTION_HOSTS="ec2-54-172-3-38.compute-1.amazonaws.com"
PRODUCTION_USER="ec2-user"
DELIVER_TO="/home/ec2-user"
At this point, we could add any number of production hosts, or add another environment entirely, such as a staging environment.
Because we’re building a Phoenix application, we’ll need to build and digest all of our static assets before building our release.
We’re building our release bundle on our remote build host, so we’ll need to instruct edeliver to do these things for us. Thankfully, edeliver comes with a host of pre and post hooks that we can use to accomplish this task.
Let’s add a “pre compile” hook to build (npm install
, npm run deploy
) and digest (mix phoenix.digest
) our static assets:
pre_erlang_clean_compile() {
status "Installing NPM dependencies"
__sync_remote "
[ -f ~/.profile ] && source ~/.profile
set -e
cd '$BUILD_AT'
npm install $SILENCE
"
status "Building static files"
__sync_remote "
[ -f ~/.profile ] && source ~/.profile
set -e
cd '$BUILD_AT'
mkdir -p priv/static
npm run deploy $SILENCE
"
status "Running phoenix.digest"
__sync_remote "
[ -f ~/.profile ] && source ~/.profile
set -e
cd '$BUILD_AT'
APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD phoenix.digest $SILENCE
"
}
By default, edeliver will store all release tarballs in the .deliver/releases
folder. Let’s preemptively exclude this folder from revision control:
echo ".deliver/releases/" >> .gitignore
The last thing we need to do to prepare for our first release is to commit all of our changes!
Preparing Our Hosts
When using edeliver, we’ll always have at least two different types of remote environments.
The first is our build host. This is where edeliver actually builds our release. In our case, it’s running a variation of a mix release
command. As we saw in a previous article, it’s important that the build host be nearly identical to the production host in terms or architectures, etc…
Our build host has several dependencies. We’ll need to install Git, Erlang & Elixir, and Node. Thankfully, we’ll only need to provision a single build host.
The second type of host is our deployment target. These machines will be running our application in environments such as staging or production. We can have any number of these hosts living in the wild.
Our deployment machines have no dependencies. We don’t even need to install Erlang - our release will bring it along for us.
However, we do need to set any required environment variables on these hosts. Out of the box, a Phoenix application needs a PORT
value. We can export this from our ~/.profile
:
export PORT=4000
If you’re using Amazon EC2 hosts, you’ll most likely authenticate with your remote hosts by passing along a *.pem
file when you establish your SSH connection.
While edeliver doesn’t explicitly support this kind of authentication, adding a Host
/IndentityFile
entry in your ~/.ssh/config
file for each of your remote hosts will authorize edeliver to communicate with these hosts:
Host ec2-foo.compute-1.amazonaws.com
IdentityFile ~/my_identity_file.pem
Host ec2-bar.compute-1.amazonaws.com
IdentityFile ~/my_identity_file.pem
Making Our Release
Once our application is ready, our deployment tools are configured, and our hosts are provisioned, we can build and deploy our release.
The first step is to instruct edeliver to build our release on our build host:
mix edeliver build release
Success! If everything went well, we should find our newly built release tarball in our release store (.deliver/releases
).
Next, let’s push our initial release up to our production server:
mix edeliver deploy release to production
Another success! We’ve deployed our initial release to our production environment.
Now let’s fire up the Phoenix application in production:
mix edeliver start production
We can check that everything went well by using edeliver to ping our application:
mix edeliver ping production
If our application is up and running, we should receive a pong
reply to our ping.
At this point, we should be able to navigate to our production host and find our application running at the port we’ve specified.
Final Thoughts
From my limited experience, edeliver is a fantastic tool.
While it does require up-front work (more work than using Distillery on its own), that work is purely up-front. Once you’ve provisioned your build host and set up your edeliver configuration file, building and deploying releases to any number of hosts is smooth sailing.
I’m excited to work more with edeliver. Expect an upcoming post on deploying hot upgrade releases to see how the process compares with just using Distillery.