Target systems and release handling
Categories: erlang, release_handler
Creating the first target system
Before any hot code swapping can occur there must be an initial system running. Packaging the first target system is slightly different than packaging subsequent application releases.
Here is the documentation that I followed to get this working: http://www.erlang.org/doc/system_principles/create_target.html
Start with foo
I'm going to create a foo application and deploy the initial version of the target system locally. To facilitate comparing app versions I'll initialize a git repository in the foo directory.
$ mkdir /src/foo
$ cd /src/foo
$ git init
Initialized empty Git repository in /src/foo/.git/
The foo application will have an app file, an application module and a gen_server.
$ mkdir ebin src
$ touch ebin/foo.app src/foo.erl src/foo_app.erl
- ebin/
- foo.app
- src/
- foo_app.erl
- foo.erl
foo.app:
foo_app.erl
foo.erl:
The foo gen_server does nothing special. It stores an integer as its state and provides a function, get_state/0, that returns the current state.
Rel file
Create a rel file for version 0.1 of the foo app:
$ touch foo-0.1.rel
foo-0.1.rel:
Add an empty sys.config file:
$ echo "[]." > sys.config
At this point you should have the following app structure
- ebin/
- foo.app
- src/
- foo_app.erl
- foo.erl
- foo-0.1.rel
- sys.config
Add the files to git and commit
$ git add .
$ git commit -m "commit of 0.1"
Packaging the target system
Compile the modules and create the target system package:
$ erlc -o ebin src/*.erl
$ mkdir bin releases
$ erl -pa /src/foo/ebin
1> systools:make_script("foo-0.1", [no_module_tests]).
ok
2> file:copy(filename:join([code:root_dir(), "bin", "start_clean.boot"]), "bin/start.boot").
{ok,5354}
3> file:write_file("releases/start_erl.data", iolist_to_binary([erlang:system_info(version), " ", "0.1", "\n"])).
ok
4> systools:make_tar("foo-0.1", [{erts, code:root_dir()}, {dirs, ['bin', 'releases']}]).
ok
Installing the target system
Now we have a target system that can be deployed. I'm going to deploy and run the target system locally in the /usr/local/erl-target directory:
$ sudo mkdir /usr/local/erl-target
$ sudo chown -R jacobvorreuter:jacobvorreuter /usr/local/erl-target/
$ cp foo-0.1.tar.gz /usr/local/erl-target/
$ cd /usr/local/erl-target
$ tar xvf foo-0.1.tar.gz
$ mv lib/foo-0.1/bin ./
$ mv lib/foo-0.1/releases/start_erl.data releases/
$ sed "s|%FINAL_ROOTDIR%|`pwd`|" <erts-5.7.2/bin/erl.src >bin/erl
$ sed "s|%FINAL_ROOTDIR%|`pwd`|" <erts-5.7.2/bin/start.src >bin/start
$ sed "s|%FINAL_ROOTDIR%|`pwd`|" <erts-5.7.2/bin/start_erl.src >bin/start_erl
$ cp erts-5.7.2/bin/epmd erts-5.7.2/bin/run_erl erts-5.7.2/bin/to_erl bin/
$ chmod +x bin/*
$ cd releases
$ ../bin/erl
1> release_handler:create_RELEASES("/usr/local/erl-target", "foo-0.1.rel").
We're going to edit the startup script that was generated for us at /usr/local/erl-target/bin/start_erl. Add a node name so that the last line looks like this:
exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys -name foo@`hostname` ${1+"$@"}
Fire up the foo application:
$ cd /usr/local/erl-target
$ bin/start
$ bin/erl -name jake -remsh foo@`hostname`
1> application:which_applications().
[{foo,"foo app","0.1"},
{sasl,"SASL CXC 138 11","2.1.6"},
{stdlib,"ERTS CXC 138 10","1.16.2"},
{kernel,"ERTS CXC 138 10","2.13.2"}]
2> foo:get_state().
0
3> foo:get_state().
1
The foo application is now running...
Version 0.2 of foo
The documentation I followed for the steps below can be found here: http://www.erlang.org/doc/design_principles/release_handling.html
Create another directory called foo1 and clone the repo from foo:
$ cd /src
$ git clone -l /src/foo foo1
$ cd foo1
Change the foo app version in foo.app:
Remove foo-0.1.rel and replace with foo-0.2.rel:
Update the foo.erl gen_server to store a float in its state:
Create an appup file:
$ touch ebin/foo.appup
foo.appup:
Packaging release 0.2
Compile the code
$ erlc -o ebin src/*.erl
Generate a relup file, boot script and tarball:
$ erl -pa /src/foo /src/foo/ebin /src/foo1/ebin
1> systools:make_relup("foo-0.2", ["foo-0.1"], ["foo-0.1"]).
ok
2> systools:make_script("foo-0.2").
ok
3> systools:make_tar("foo-0.2").
ok
Deploying release 0.2
Copy the new release package into the releases directory:
$ cp foo-0.2.tar.gz /usr/local/erl-target/releases/
Connect to our running node, upack the release and install it:
$ /usr/local/erl-target/bin/erl -name rel -remsh foo@`hostname`
1> release_handler:unpack_release("foo-0.2").
{ok,"0.2"}
2> release_handler:install_release("0.2").
Test that it worked:
3> foo:get_state().
2.0
4> foo:get_state().
3.0