{"id":5457,"date":"2016-06-07T17:32:27","date_gmt":"2016-06-07T20:32:27","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=5457"},"modified":"2016-06-07T17:37:37","modified_gmt":"2016-06-07T20:37:37","slug":"deploying-elixir-applications-with-edeliver","status":"publish","type":"post","link":"http:\/\/blog.plataformatec.com.br\/2016\/06\/deploying-elixir-applications-with-edeliver\/","title":{"rendered":"Deploying Elixir applications with Edeliver"},"content":{"rendered":"

We’ve been talking about deploy and releases with Elixir lately, like how to run migrations on top of a release<\/a> or how to deal with environment variables<\/a>. Now it’s time to discover another tool that can help us release our Elixir application.<\/p>\n

After practicing deploy and tracing through nodes<\/a> with Exrm<\/a>, we got more comfortable knowing that there is a tool we can count on for managing production releases. Our next biggest concern was how could we make the deploy process more manageable. We couldn’t stop thinking about Capistrano, which we normally use for our Rails projects, then we found Edeliver<\/a>. From Edeliver’s README description:<\/p>\n

edeliver is based on deliver and provides a bash script to build and deploy Elixir and Erlang applications and perform hot-code upgrades.<\/p><\/blockquote>\n

Trying the whole deploy process manually was a bit harsh with some repetitive tasks. Using Edeliver for our first script\/deploy<\/code> was awkwardly easy! In the end, the whole manual process was simplified to:<\/p>\n

    #!\/bin\/bash -ex\n\n    BRANCH=${1:-master};\n\n    mix edeliver build release --branch=BRANCH --verbose\n    mix edeliver deploy release to production --verbose\n    mix edeliver start production --verbose\n    mix edeliver migrate production up --verbose\n<\/code><\/pre>\n

You’re probably going to need to customize this script, adapting it for your needs. In this case, we’re using this script only for production deploys, but you can customize it for staging servers pretty easily. We’ll explain how environments work further along.<\/p>\n

How it works<\/h2>\n

As we saw before in the README quote, Edeliver makes pretty much everything with bash scripts. The Mix tasks we saw above will be executed with Elixir, but they’ll result in bash script instructions. Part of the instructions are executed in the scripts locally, which will build new instructions that will run remotely via RPC (Remote procedure call).<\/p>\n

Let’s go deeper in some aspects of the lib.<\/p>\n

Environments<\/h2>\n

Edeliver is a cool option for launching and distributing releases in multiple environments. It has a concept of three environments: build, staging and production. Among these, only the build environment should get a bit more of attention.<\/p>\n

For a release to work in a server, it must have been built in a machine with the same architecture where the release will run. That’s because Edeliver uses Exrm for building its releases. Exrm will internally use its local NIFs<\/a> (C functions used by Erlang) which may vary in a different architecture, thus causing, for example, an OSX release not working on Linux. You can read more about it in this Phoenix issue<\/a> where people are discussing cross-compiling issues and there are some other issues in Exrm<\/a> as well.<\/p>\n

In order to use the build environment in our own development machine, it needs to use the same architecture of our staging and production servers, otherwise it won’t work.<\/p>\n

To configure our environments, we’ll need to create a .deliver<\/code> directory in our project and add a config<\/code> file. Let’s see the suggested configs from Edeliver’s README for this file:<\/p>\n

#!\/usr\/bin\/env bash\n\nAPP=\"your-erlang-app\" # name of your release\n\nBUILD_HOST=\"build-system.acme.org\" # host where to build the release\nBUILD_USER=\"build\" # local user at build host\nBUILD_AT=\"\/tmp\/erlang\/my-app\/builds\" # build directory on build host\n\nSTAGING_HOSTS=\"test1.acme.org test2.acme.org\" # staging \/ test hosts separated by space\nSTAGING_USER=\"test\" # local user at staging hosts\nTEST_AT=\"\/test\/my-erlang-app\" # deploy directory on staging hosts. default is DELIVER_TO\n\nPRODUCTION_HOSTS=\"deploy1.acme.org deploy2.acme.org\" # deploy \/ production hosts separated by space\nPRODUCTION_USER=\"production\" # local user at deploy hosts\nDELIVER_TO=\"\/opt\/my-erlang-app\" # deploy directory on production hosts\n<\/code><\/pre>\n

It’s pretty easy to configure our environments, we only need to make sure we have ssh<\/code> permission for these servers specified. A cool thing about this whole configuration, as mentioned before, is that it’s possible to distribute the releases through several servers.<\/p>\n

How can I include extra tasks to my deploy process?<\/h2>\n

What Edeliver does is generic for Elixir and Erlang applications. When we’re using Phoenix, for example, we need to run some tasks before generating the release. The most important tasks are brunch build --production<\/code> and mix phoenix.digest<\/code> so we can have our assets working on our release.<\/p>\n

To make these work, we’ll need to define a hook in our .deliver\/config<\/code> file:<\/p>\n

pre_erlang_clean_compile() {\n  status \"Preparing assets with: brunch build and phoenix.digest\"\n  __sync_remote \"\n    # runs the commands on the build host\n    [ -f ~\/.profile ] && source ~\/.profile # load profile (optional)\n\n    # fail if any command fails (recommended)\n    set -e\n\n    # enter the build directory on the build host (required)\n    cd '$BUILD_AT'\n\n    mkdir -p priv\/static # required by the phoenix.digest task\n\n    # installing npm dependencies\n    npm install\n\n    # building brunch\n    brunch build --production\n\n    # run your custom task\n    APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD phoenix.digest $SILENCE\n  \"\n} \n<\/code><\/pre>\n

This was extracted from an Edeliver doc sample, which explains all the possibilities of hooks.<\/p>\n

What about my environment variables?<\/h2>\n

We shared a tip on dealing with environment variables with Exrm<\/a> in order to avoid exporting them in our build environment and it’s still up! Although, there’s an important detail we’ll need to pay attention.<\/p>\n

In order to make the environments replaceable we needed to set RELX_REPLACE_OS_VARS=true<\/code> before our start command. But that’s not possible with Edeliver because the start task runs locally.<\/p>\n

mix edeliver start production<\/code><\/p>\n

Then a possible solution is to export the RELX_REPLACE_OS_VARS<\/code> in your production environment.<\/p>\n

Considerations<\/h2>\n

Edeliver seems like a cool option for dealing with our releases and deploy process, I found it really easy to use. I didn’t enter in implementation details in this post, so make sure to read its README<\/a> and docs, they’re very useful and well-explained.<\/p>\n

This was a solution we found to ease our deploy process. How have you been managing your process? Did this post help you?<\/p>\n


\n
\n

If you are into Elixir-Phoenix, you may also like…<\/p>\n

\"What's<\/a>\n<\/div>\n","protected":false},"excerpt":{"rendered":"

We’ve been talking about deploy and releases with Elixir lately, like how to run migrations on top of a release or how to deal with environment variables. Now it’s time to discover another tool that can help us release our Elixir application. After practicing deploy and tracing through nodes with Exrm, we got more comfortable … \u00bb<\/a><\/p>\n","protected":false},"author":38,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[249,230,143,245],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5457"}],"collection":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/38"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=5457"}],"version-history":[{"count":16,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5457\/revisions"}],"predecessor-version":[{"id":5460,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5457\/revisions\/5460"}],"wp:attachment":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=5457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=5457"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=5457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}