You’ve already got a first target system installed, and now you’ve written some new code and want to deploy it. This article will show you how to setup make and fab commands that use reltools to build & install new releases.
Your code should be part of an OTP application structure. Additionally, you will need an appup file in the
ebin/ directory for each application you want to upgrade. There’s a lot you can do in an appup file:
- reload a module
- add or delete a module
- update a running process
- and lots more. Refer to the Appup Cookbook and appup reference manual for more details.
To create a new release, you’ll need a new rel file, which I’ll refer to as
VSN should be greater than your previous release version. My usual technique is to copy my latest rel file to
NAME-VSN.rel, then update the release
VSN and all the application versions.
Note: reltools assumes that the rel file will be in
$ROOTDIR defaults to
code:root_dir(). This path is also used below in the make and fab commands. You can pass a different value for
releases/ is hard coded. This may change in the future, but for now your rel files must be in
$ROOTDIR/releases/ if you want to use reltools.
- Make a copy of reltools and add it to your application.
- Clone elib and add it to your code path with
But I’ll assume you want option 2 because it provides cleaner code separation and easier release handling. Keeping elib external means you can easily pull new code, and only need to add the elib application to your rel file with the latest vsn.
Now that you have a new release defined, and elib is in your code path, you’re ready to build release upgrade packages. Below is the make command I use to call
reltools:make_upgrade("NAME-VSN"). Be sure to update
PATH/TO/ to your particular code paths.
ERL=erl # use default erl command, but can override path on command line src: FORCE @$(ERL) -pa lib/*/ebin -make # requires an Emakefile upgrade: src @$(ERL) -noshell \ # run erlang with no shell -pa lib/*/ebin \ # include your local code repo -pa PATH/TO/elib/ebin \ # include elib -pa PATH/TO/erlang/lib/*/ebin \ # include local erlang libs -run reltools make_upgrade $(RELEASE) \ # run reltools:make_upgrade -s init stop # stop the emulator when finished FORCE: # empty rule to force run of erl -make
Using the above make rules, you can do
RELEASE=PATH/TO/releases/NAME-VSN to build a release upgrade package. Once you can do this locally, you can use fab to do remote release builds and installs. But in order to build a release remotely, you need to get the code onto the server. There are various ways to do this, the simplest being to clone your repo on the remote server(s), and push your updates to each one.
fab release build install
PATH/TO/TARGET should be the path to your first target system.
release is a separate command so that it you are only asked for
NAME-VSN once, no matter how many hosts you
build will run
RELEASE=releases/NAME-VSN on the remote system, using the target system’s copy of erl. Theoretically, you could build a release package once, then distribute it to each target system’s
releases/ directory. But that requires each target system being exactly the same, with all the same releases and applications installed. If that’s the case, modify the above recipe to run
build on a single build server, have it put the release package into all the other node’s
releases/ directory, then run
install on each node.
_rpcall to run
["NAME-VSN"]). I’ve kept
_rpcall separate so you can see how to define your own fab commands by setting
from fabric.api import env, prompt, require, run env.erl = 'PATH/TO/TARGET/bin/erl' def release(): '''Prompt for release NAME-VSN. rel file must be in releases/.''' prompt('Specify release as NAME-VERSION:', 'release', validate=r'^\w+-\d+(\.\d+)*$') def build(): '''Build upgrade release package.''' require('release') run('cd PATH/TO/REPO && hg up && make upgrade ERL=%s RELEASE=releases/%s' % (env.erl, env.release)) def install(): '''Install release to running node.''' require('release') env.mfa = 'reltools,install_release,["%s"]' % env.release _rpccall() def _rpccall(): require('mfa') evalstr = 'io:format(\"~p~n\", [rpc:call(NODE@%s, %s)])' % (env.host, env.mfa) # NOTE: local user must have same ~/.erlang.cookie as running nodes run("%s -noshell -sname fab -eval '%s' -s init stop" % (env.erl, evalstr))
Once you’ve updated your
Makefile and created
fabfile.py, your workflow can be something like this:
- Write new application code.
- Update the app and appup files for each application to upgrade.
- Create a new rel file as
- Commit and push your changes.
NAME-VSNfor your new release.
- Watch your system hot upgrade in real-time 🙂
reltools:install_release(NAME-VSN) can fail, usually when the release_handler can’t find an older version of your code. In this case, your new release will be
unpacked but not
installed. You can see the state of all the known releases using
release_handler:which_releases().. This can usually be fixed by removing old releases and trying again. Shell into your target system and do something like this (where
OLDVSN is the
VSN of a release marked as
See the release_handler manual for more information.
release_handler:remove_release("OLDVSN"). % repeat as necessary release_handler:install_release("VSN"). release_handler:make_permanent("VSN").