Erlang で、relx と sync を使ったお手軽自動リロード開発環境コトハジメ

こんにちは。 VOYAGE GROUP の @ajiyoshi です。adingoという会社でFluctという広告サービスの開発をしています。

Fluctのサブシステムで、Erlang言語を使ったりしています。今回は Erlang のリリースツール relx と、 sync を使って、開発をちょっと楽にするような TIPS をご紹介します。

relx コトハジメ の内容を把握している読者を想定しています。(いきなりハードル高くて申し訳ありません。Erlang のコンパイル環境があれば、基本的にそのまま動くようなサンプルコード もありますのでご勘弁ください)

Dockerイメージなどを開発環境にしていると、なるべく実際に動く環境に近いような動作環境でテストしたくなります。

一方で、開発環境というのはなるべく手間なく勝手にコンパイルやテストが走ったり、変更点を勝手にリロードしてほしいものです。

relx を使ってリリースイメージに近いような環境でソフトウェアを起動しつつ、 sync を使って自動コンパイル&リロードする環境を作ろうというのがこの記事の趣旨です。

サンプルプロジェクトを用意

rebar のテンプレート作成機能を使って、サンプルプロジェクトを用意します。(rebar がない人はコンパイルしてください。rebar は Erlang でほぼデファクトスタンダードなビルドツールです)

$ mkdir myapp
$ cd myapp
$ cp /path/to/rebar .
$ cp /path/to/relx .
$ ./rebar create-app appid=myapp
==> myapp (create-app)
Writing src/myapp.app.src
Writing src/myapp_app.erl
Writing src/myapp_sup.erl

普通です。 rebar のテンプレートでアプリのひな形を作ります。

relx の設定(自動リロードなし)を用意

dev_relx.config

{release, {myapp, "0.0.1"},
    [sasl, myapp]}.
{extended_start_script, true}.
{output_dir, "dev"}.

コンパイルし、relxリリースを作る

$ ./rebar get-deps compile
==> myapp (get-deps)
==> myapp (compile)
Compiled src/myapp_app.erl
Compiled src/myapp_sup.erl

$ ./relx -c dev_relx.config
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /home/y-sudo/project/0316/myapp/ebin
          /home/y-sudo/kerl/R16B03-1/lib
===> Resolved myapp-0.0.1
===> Including Erts from /home/y-sudo/kerl/R16B03-1
===> release successfully created!

私はこういうのは Makefile にしちゃう のが好みです。そうすれば上のコマンドはこのように一発でまとめて実行できますし、シェルによっては tab で make ターゲットを補完したりできます。

$ make rx
./rebar get-deps
(中略)
./rebar compile
(中略)
./relx -c dev_relx.config
(中略)
===> release successfully created!

relx 起動

$ ./dev/myapp/bin/myapp console
(中略)
(myapp@127.0.0.1)1>

無事起動しました。ここまでは voluntas さんの記事の通りです。

(myapp@127.0.0.1)1> q().

終了しときます。

sync を準備 & relxの設定を変更

sync という Erlangアプリがあり、ファイルシステムを監視して、ソースコードが変更されたら自動コンパイル&リロードしてくれる素敵なアプリです。

ただ、sync は開発用途のソフトウェアで sync:go() してやらないと監視を開始してくれません。それはそれでもっともだと思いますが、 開発用Dockerイメージなどではイメージ起動したら勝手にソースの更新チェックとかしてほしいです。結局 console 開くことにはなるけど、毎度 sync:go() 叩くのも面倒です。

そこで sync:go() するだけの auto_syncer というアプリケーションを作ったので、こいつを使ってやります。

rebar.config

{deps, [
    {auto_syncer, ".*", {git, "https://github.com/ajiyoshi/auto_syncer.git"}}
]}.

rebar.config のdepsに追加

dev_relx.config

{release, {myapp, "0.0.1"},
    [sasl, myapp, auto_syncer]}.
{extended_start_script, true}.
{output_dir, "dev"}.

auto_syncer をリリースに含めてやります

リリース作成してアプリケーションを起動

$ ./rebar get-deps
==> myapp (get-deps)
Pulling auto_syncer from {git,"https://github.com/ajiyoshi/auto_syncer.git"}
Cloning into 'auto_syncer'...
==> auto_syncer (get-deps)
Pulling sync from {git,"https://github.com/rustyio/sync.git"}
Cloning into 'sync'...
==> sync (get-deps)

$ ./rebar compile
==> sync (compile)
Compiled src/sync.erl
Compiled src/sync_notify.erl
Compiled src/sync_scanner.erl
Compiled src/sync_options.erl
Compiled src/sync_utils.erl
==> auto_syncer (compile)
Compiled src/auto_syncer_app.erl
Compiled src/auto_syncer_sup.erl
==> myapp (compile)

$ ./relx -c dev_relx.config
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /home/y-sudo/project/0316/myapp/ebin
          /home/y-sudo/project/0316/myapp/deps
          /home/y-sudo/kerl/R16B03-1/lib
===> Resolved myapp-0.0.1
===> Including Erts from /home/y-sudo/kerl/R16B03-1
===> release successfully created!

これで、アプリケーションを起動するだけでソースコードを自動コンパイル&リロードしてくれるリリースビルドができました。起動してみます。

$ ./dev/myapp/bin/myapp console
(中略)
(myapp@127.0.0.1)1>

ソースコードを適当に変更したら、自動リロードされます

src/myapp_app.erl を適当に編集。

-module(myapp_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).
-export([hello/0]).

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
    myapp_sup:start_link().

stop(_State) ->
    ok.

% 追加した
hello() ->
    hello.

勝手にコンパイル&リロード

(myapp@127.0.0.1)1>
=INFO REPORT==== 8-Feb-2015::14:24:41 ===
/home/y-sudo/project/0316/myapp/src/myapp_app.erl:0: Recompiled.

=INFO REPORT==== 8-Feb-2015::14:24:41 ===
myapp_app: Reloaded! (Beam changed.)

(myapp@127.0.0.1)2> myapp_app:hello().
hello

便利。