Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Erlang リリース コトハジメ

Erlang リリース コトハジメ

更新:2014-04-10
バージョン:0.2.1
作者:@voluntas
URL:http://voluntas.github.io/

reltool 周りについて勉強がてらまとめてみました

Erlang の準備

バージョン:17.0

Mac OS X

./configure --prefix=/opt/erlang/17.0 --enable-smp-support --enable-darwin-64bit --enable-vm-probes --enable-kernel-poll --enable-hipe --with-dynamic-trace=dtrace --without-javac --enable-dirty-schedulers --disable-native-libs --disable-sctp

rebar の準備

GitHub からどうぞ

$ ./rebar --version
rebar 2.2.0 17 20140409_170003 git 2.2.0-47-g0c7fe5f

アプリケーションの準備

まずはアプリを作ります

$ ./rebar create-app appid=snowflake

次に rebar.config を作ります。

以下の設定は自分がよく使う設定です。

rebar.config:

$ vim rebar.config

{require_otp_vsn, "17"}.
{erl_opts, [warnings_as_errors,
            warn_export_all,
            warn_unused_import,
            warn_untyped_record]}.

{xref_checks, [fail_on_warning, undefined_function_calls]}.
{clean_files, [".test/*.beam", ".eunit/*", "ebin/*.beam"]}.
{cover_enabled, true}.

{validate_app_modules, true}.

{sub_dirs, ["rel"]}.

ポイントは sub_dirs です。"rel" を登録しましょう。

rel/の中身を生成して reltool.config を設定します。

変更する前の部分をコメントで書いてあります

reltool.config:

$ cd rel
$ ../rebar create-node nodeid=snowflake

$ vim reltool.config

{sys, [
       %% 変更前 {lib_dirs, []},
       {lib_dirs, ["../deps"]},
       {erts, [{mod_cond, derived}, {app_file, strip}]},
       {app_file, strip},
       {rel, "snowflake", "1",
        [
         kernel,
         stdlib,
         sasl,
         snowflake
        ]},
       {rel, "start_clean", "",
        [
         kernel,
         stdlib
        ]},
       {boot_rel, "snowflake"},
       {profile, embedded},
       {incl_cond, derived},
       {mod_cond, derived},
       {excl_archive_filters, [".*"]}, %% Do not archive built libs
       {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
                           "^erts.*/(doc|info|include|lib|man|src)"]},
       {excl_app_filters, ["\.gitignore"]},
       {app, hipe, [{incl_cond, exclude}]},
       %% 変更前 {app, snowflake, [{mod_cond, app}, {incl_cond, include}]}
       {app, snowflake, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}
      ]}.

{target_dir, "snowflake"}.

{overlay, [
           {mkdir, "log/sasl"},
           {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
           {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
           {copy, "files/snowflake", "bin/snowflake"},
           {copy, "files/snowflake.cmd", "bin/snowflake.cmd"},
           {copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
           {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
           {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
           {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
          ]}.

hipe

コンパイル時に hipe を無効にしている場合でも reltool では hipe を要求してきます。 この辺は根が深い問題らしいので、そっと「あぁそうなんだ」で許してください。

そのため以下の内容を追加する必要があります。

{app, hipe, [{incl_cond, exclude}]}

リリースしてみる

rebar generate を打てば生成されます

$ cd rel
$ ../rebar generate

ポイントは rel の中から rebar generate を実行することです

rel の下に snowflake というディレクトリが出来ているはずです。さっそく実行してみましょう。

$ ./rel/snowflake/bin/snowflake console

Erlang/OTP 17 [erts-6.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V6.0  (abort with ^G)
(snowflake@127.0.0.1)1>

起動しました。start で起動すればデーモンとして上がりますし、stop すれば終了します。

開発者向けリリース

リリースしたのはいいのですが、このままでは ebin や deps に変更をするたび ./rebar generate する必要があります。そんな面倒なことは嫌なので、色々置き換える事で効率よく開発出来るようにします。

やることはシンプルで rel のライブラリを全てシンボリックリンクで開発の deps, priv, ebin に置き換えてしまう仕組みです。

ただし、少し問題がありまして、rebar の create-node で作ったファイルそのままでは動かない事がわかっています。

これは -mode emmbedded で動かした場合はライブラリは全てバージョンが指定されている必要があります。今回の方法ではメインの snowflake のバージョンを指定していないため、このままでは動きません。

そのため files/snowflake に少しだけ手を入れる必要があります。手を入れるのは以下の一行から -mode embedded を削除してください

-mode embedded 削除前:

$ vim rel/files/snowflake

CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH"

-mode embedded 削除後:

$ vim rel/files/snowflake

CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH"

後は、deps や ebin 、さらに priv を書き換える仕組みを Makefile に追加します

Makefile 一部抜粋:

devrel: rel
        $(foreach dep,$(wildcard deps/*), rm -rf dev/$(APP_NAME)/lib/$(shell basename $(dep))-* && ln -sf $(abspath $(dep)) dev/$(APP_NAME)/lib;)
        rm -rf dev/$(APP_NAME)/lib/$(APP_NAME)-*
        rm -rf dev/$(APP_NAME)/lib/$(APP_NAME)
        mkdir dev/$(APP_NAME)/lib/$(APP_NAME)
        ln -sf $(abspath ebin) dev/$(APP_NAME)/lib/$(APP_NAME)/ebin
        ln -sf $(abspath priv) dev/$(APP_NAME)/lib/$(APP_NAME)/priv

rel: compile
        mkdir -p dev
        mkdir -p deps
        (cd rel && rm -rf ../dev/$(APP_NAME) && ../rebar generate && ../rebar generate target_dir=../dev/$(APP_NAME))

rel は ./rebar generate する際に dev というフォルダ以下に出力するのを明示的にしています。

devrel は deps と ebn と priv をシンボリックリンクで書き換えています。こうすることで毎回リリースする事無く、コードを書いてコンパイルするだけで反映されるようになります。

reloader の導入

mochiweb の reloader を上手く使うと、コンパイルすると自動で反映してくれるという仕組みが使えます。また watchdog あたりを使って変更を見てコンパイルする仕組みを入れることでさらに効率が上がります。

rebar.config の deps に reloader を追加します。

rebar.config:

%% debug
{reloader,
 ".*", {git, "git@github.com:oinksoft/reloader.git", {branch, "master"}}}

さらに rel/files/vm.args の中に -s reloader を追加します。

vm.args:

## Name of the node
-name <nodename>@127.0.0.1

## reloader を読み込むようにする
-s reloader

## Cookie for distributed erlang
-setcookie spam

## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
## (Disabled by default..use with caution!)
##-heart

## Enable kernel poll and a few async threads
##+K true
##+A 5

## Increase number of concurrent ports/sockets
##-env ERL_MAX_PORTS 4096

## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10

スリムリリース

開発者リリースをさらに効率化させます。通常リリース、開発者リリースまでは Erlang/OTP を含んでいましたが、これすらも省略する事が出来ます。

ポイントは R15B02 から reltool に導入された {excl_lib, otp_root} を使います。

reltool.config に {excl_lib, otp_root} を追加してください。それ以外の変更は不要です。

reltool.config:

$ cd rel
$ ../rebar create-node nodeid=snowflake

$ vim reltool.config

{sys, [
       {lib_dirs, ["../deps"]},
       %% この一行を追加する
       {excl_lib, otp_root},
       {erts, [{mod_cond, derived}, {app_file, strip}]},
       {app_file, strip},
       {rel, "snowflake", "1",
        [
         kernel,
         stdlib,
         sasl,
         snowflake
        ]},
       {rel, "start_clean", "",
        [
         kernel,
         stdlib
        ]},
       {boot_rel, "snowflake"},
       {profile, embedded},
       {incl_cond, derived},
       {mod_cond, derived},
       {excl_archive_filters, [".*"]}, %% Do not archive built libs
       {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
                           "^erts.*/(doc|info|include|lib|man|src)"]},
       {excl_app_filters, ["\.gitignore"]},
       {app, hipe, [{incl_cond, exclude}]},
       {app, snowflake, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}
      ]}.

{target_dir, "snowflake"}.

{overlay, [
           {mkdir, "log/sasl"},
           {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
           {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
           {copy, "files/snowflake", "bin/snowflake"},
           {copy, "files/snowflake.cmd", "bin/snowflake.cmd"},
           {copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
           {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
           {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
           {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
          ]}.

スリムリリースでは起動スクリプトを変更する必要があります。

erl -boot_var RELTOOL_EXT_LIB $RUNNER_BASE_DIR/lib -boot \
    $RUNNER_BASE_DIR/releases/1/<nodeid> -sasl releases_dir \
    \"$RUNNER_BASE_DIR/releases\"

つづく ...

おまけ

以下は参考までに良く自分がベースにする Makefile です

.PHONY: all compile deps clean test devrel rel

REBAR_CONFIG = rebar.config

APP_NAME = snowflake

all: clean deps test

deps: get-deps update-deps
        @./rebar -C $(REBAR_CONFIG) compile

update-deps:
        @./rebar -C $(REBAR_CONFIG) update-deps

get-deps:
        @./rebar -C $(REBAR_CONFIG) get-deps

compile:
        @./rebar -C $(REBAR_CONFIG) compile skip_deps=true
        @./rebar -C $(REBAR_CONFIG) xref skip_deps=true

devrel: rel
        $(foreach dep,$(wildcard deps/*), rm -rf dev/$(APP_NAME)/lib/$(shell basename $(dep))-* && ln -sf $(abspath $(dep)) dev/$(APP_NAME)/lib;)
        rm -rf dev/$(APP_NAME)/lib/$(APP_NAME)-*
        rm -rf dev/$(APP_NAME)/lib/$(APP_NAME)
        mkdir dev/$(APP_NAME)/lib/$(APP_NAME)
        ln -sf $(abspath ebin) dev/$(APP_NAME)/lib/$(APP_NAME)/ebin
        ln -sf $(abspath priv) dev/$(APP_NAME)/lib/$(APP_NAME)/priv

rel: compile
        mkdir -p dev
        mkdir -p deps
        (cd rel && rm -rf ../dev/$(APP_NAME) && ../rebar generate target_dir=../dev/$(APP_NAME))

test:
        rm -rf .eunit
        @./rebar -C $(REBAR_CONFIG) eunit skip_deps=true

clean:
        @./rebar -C $(REBAR_CONFIG) clean skip_deps=true

distclean: clean
        @./rebar -C $(REBAR_CONFIG) clean
        @./rebar -C $(REBAR_CONFIG) delete-deps
        rm -rf dev

dialyze-init:
        dialyzer --build_plt --apps erts kernel stdlib mnesia crypto public_key snmp reltool
        dialyzer --add_to_plt --plt ~/.dialyzer_plt --output_plt $(APP_NAME).plt -c .
        dialyzer -c ebin -Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs

dialyze: compile
        dialyzer --check_plt --plt $(APP_NAME).plt -c .
        dialyzer -c ebin

謝辞

この記事を書くに当たって @shino (@itawasa) に色々教えていただきました。 おそらく日本で一番 rebar と reltool 周りに詳しいと思います。本当にありがとうございます。

参照

rebar generateでErlangアプリをパッケージ化したときのメモ - ごろねこ日記
http://d.hatena.ne.jp/hiroe_orz17/20120617/1339934952
Change to slim release · 95ec813 · shino/slim_sample
https://github.com/shino/slim_sample/commit/95ec813b74360e19b522c806292cdd582a2d5c46
rebar/priv/templates/simplenode.runner at slim · tuncer/rebar
https://github.com/tuncer/rebar/blob/slim/priv/templates/simplenode.runner
Comparing aa0b0b44a5...0426492c5a · voluntas/snowflake
https://github.com/voluntas/snowflake/compare/aa0b0b44a5...0426492c5a
Add slim release support · Issue #7 · rebar/rebar
https://github.com/rebar/rebar/issues/7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment