Erlangアプリケーションを node_package で簡単パッケージング

この記事はErlang Advent Calendar 2014の9日目の記事です。

Erlang/OTP でサーバー作ったあとのデプロイはわりと大変だ。リリースをわりと念入りに作っておいても、ディレクトリ構造がややこしかったりいろいろと面倒らしい。ぼく自身は運良くそういう問題はこれから紹介する node_package を使っているのであまり苦労をしたことがないのだが、リリースを作る大変さはすごいE本を読めば書いてある。

すごいErlangゆかいに学ぼう!

すごいErlangゆかいに学ぼう!

これを読んで、やっぱり自分がラッキーなことを再確認した。実際、うまくツールチェインを作りこんでも、 NIF があったりしてリリースを環境ごとにコンパイルしなければいけなかったりとかなり面倒だ。で、サービスを運用している会社でもない場合、たとえばCentOSやUbuntu, Redhatなど複数の環境にインストールできるようにしなければならない。

しかしながら、ディストリビューションやOSによってパッケージシステムの設計や思想が全く異なっていて、それぞれの作法ややり方をいちから覚えて作りなおさないといけない。せっかくどんなOSでも動作するErlang VMを同梱したはずのリリースを作ったのにこれでは本末転倒だ。ほら、やっぱり設定ファイルは /etc に置きたいし、各種ツール類は /usr/sbin にインストールしたいじゃない。

その問題を解決してくれるのが node_package だ。これは Riak や Riak CS のリリースパッケージを作るために作られた便利ツールチェインだ。前提条件としては、 rebar でリリースが作れるようになっていれば、あとはこれを rebar.config に追加しておいて、 Makefile かどこかにパッケージ作成のコマンドを覚えておけば、あとは make package でどんなパッケージでも作れるようになっている。 node_package がサポートする環境の一覧は Riak のダウンロードページを見るのが正しい確認方法だ。

使い方

使い方はまあ、 Riak の Makefile の様子を見てそれなりに汲み取ってもらえれば…というのはいささか大変過ぎるだろうと思うので、まずはEUC 2013での解説スライドを貼っておこう。

簡単な手順をかいておこう。

パッケージ作成用のツールを予め入れておく

CentOSだと rpm-build だし、 Ubuntu だと dchroot devscripts debhelper あたりが必要だろう*1

rebar で release を作れるようにしておく

"rebar generate" でローカルのリリースがきちんとできるように準備しておこう。この辺りはいろいろ面倒なので省略。reltools.config とか vars.overlay とかまあいつものアレをちゃんと書いて、パッケージがなくてもこのリリースがちゃんと動くようにしておこう。 node_package も含めて正しい手順を詳しく知りたい人はこちらをどうぞ→Erlang/OTP パッケージングコトハジメ

rebar.config に依存を追加

まずはモノを持ってこないと話にならない。他にも当然、 lager, eper, cluster_info くらいはリリースに入れると思うのでちょっとここでも書いておく。最新版は 2.0.0 だ。

{deps,[
       {node_package, ".*", {git, "git://github.com/basho/node_package", {tag, "2.0.0"}}},
       {lager, ".*", {git, "git://github.com/basho/lager", {tag, "2.0.3"}}},
       {eper, ".*", {git, "git://github.com/basho/eper.git", {tag, "0.78"}}},
       {cluster_info, ".*", {git, "git://github.com/basho/cluster_info.git", {tag, "2.0.0rc1"}}}
      ]}.

Makefile を書く

お作法としては、 package/ というディレクトリを作って、そこに改めて指定のタグなりブランチなりをクリーンな状態にして持ってきた方が余計なものが入らなくてよい。Makefileは deps/node_package/ に入っているので、これを持ってくる。

package: package/$(PKG_ID).tar.gz
	$(MAKE) -C package -f $(PKG_ID)/deps/node_package/Makefile

package/$(PKG_ID).tar.gz: package.src

package.src: deps
	mkdir -p package
	rm -rf package/$(PKG_ID)
	git archive --format=tar --prefix=$(PKG_ID)/ $(PKG_REVISION)| (cd package && tar -xf -)
	make -C package/$(PKG_ID) deps
	for dep in package/$(PKG_ID)/deps/*; do \
		echo "Processing dep: $${dep}"; \
	    mkdir -p $${dep}/priv; \
        git --git-dir=$${dep}/.git describe --tags >$${dep}/priv/vsn.git; \
	done
	find package/$(PKG_ID) -depth -name ".git" -exec rm -rf {} \;
	tar -C package -czf package/$(PKG_ID).tar.gz $(PKG_ID)

pkg.vars.config 作成

node_package が見るための設定ファイルのようなものだ。パッケージのベースネームやライセンスの指定、インストールすべきスクリプト類、作成すべきユーザー名などいくつかの項目を指定することがでくいる。 Riak だとこんな感じになる。そこに載ってない豆知識をひとつ披露しておくと、

{deb_depends, ["nkf"]}.

みたいな感じで、依存パッケージもここで指定できたりする。

パッケージ作成

ここまでできたら、あとは簡単。

$ make package

あとは、 node_package がディストリビューションや環境を自分で判定してリリースから、その環境用のパッケージを作ってくれる。リモートのレポジトリにきちんとタグがついていれば、そのタグを使ってバージョン番号をよしなにパッケージにつけてくれるし、そうでなければ git のコミットハッシュを使ってユニークな番号をつけてくれる。

コツは rebar.config でちゃんとバージョンを固定しておくことだ。依存ライブラリが多すぎて毎度タグを打つのが面倒な場合は rebar_lock_deps_plugin を使うとよいだろう。

ためしにソースからパッケージを作ってみよう

1番簡単に作れるものとしては、やはり Riak CS を推したい。もしも Riak CS がサポートするLinuxやFreeBSD、MacOSなどの環境を持っているようであれば、Erlang (R16B03-1) をインストールした
あとにコレを試していただきたい。(CentOSやUbuntuだとパッケージ作成系のパッケージも必要)

$ git clone git://github.com/basho/riak_cs -b release/1.5
$ cd riak_cs && make package
...(snip)
make[2]: Leaving directory '/tmp/riak_cs/package'
make[1]: Leaving directory '/tmp/riak_cs/package'
$ ls package/packages/
riak-cs_1.5.2.8.g09aa0a4-1_amd64.deb  riak-cs_1.5.2.8.g09aa0a4-1_amd64.deb.sha

できた!
簡単ですね。

応用例

ぼくはこれとDockerを組み合わせて、いつでもどこでも CentOS 用のRPMパッケージを作れるようにしてある。ブランチ名を指定して make rpm とやると、それだけで修正済みのパッケージができるようになっている。せっかくなのでDockerfileを公開しておこう。こうしておけば、大規模なビルド環境がなくても Drone.io とか?ローカルでちょこっとパッケージを作ってあちこちの環境で試すことができる。 Docker Image を作っておいとけばいいではないかという話もあるが、ネットワークとIOまわりのことを気にして下回りになるべくレイヤーを挟みたくないのでやらない。

これをちょっと応用して、わりとパッケージを作りにくい、 escriptize されたツールもRPMなどのパッケージを作れるようにもしてある。最近だと、 basho_bench のRPMが急遽必要になったのでこれでRPMパッケージをサクッと作ってリリースした。他にも、特定のユーザー向けの簡単なErlangツールもこれでパッケージにしておくと、Erlangで書かれたとか、使うためにErlangをインストールしなきゃいけないとか面倒なことを考えなくてよいし、こちらも説明の手間が省けている。本当に便利すぎる。

おまけ

この node_package すこぶる便利なのだが、いくつかイケてないところもある。 /usr/share に入れるようなドキュメント類(リリースノートやライセンス、や manpages なんか)も一緒にうまく入ればよいのだが、そのあたりは全く機能がない(Riakでは docs.basho.com に全てドキュメントを公開してしまっているので困ってない)。あと、 release upgrade には対応していない。というか、この手のパッケージ管理と release upgrade ってすごく相性が悪いので…というわけで、この辺りをうまくこなしてくれる猛者がいたら、こちらか、こちらまでどうぞ!

*1:よくわからないのでとりあえず入れている