Edited at

Elixir アプリケーションをパッケージ化しよう

Elixir アプリケーションを本番サーバで動かす時にはパッケージ化しましょう。


パッケージ化とは

Elixir で言うパッケージ化というのは、


  • 依存しているアプリケーションやライブラリの beam ファイル

  • 設定ファイル

  • Elixir 本体の beam ファイル

  • ERTS と呼ばれる Erlang ランタイム

  • アプリケーションを起動するためのスクリプト

などを集めて、ほぼ単体で起動するように1つのディレクトリ内に纏めてしまうことを言います。

Elixir でパッケージ化するツールは、今は多分 Distillery 一択です。

2019/7/2 追記: Elixir 1.9.0 で公式に mix release が追加されました。なのでこちらで済む場合はこれを使うと良いでしょう。


パッケージ化の利点

パッケージ化する利点は そのサーバに Erlang と Elixir を入れる必要が無くなる ということに尽きます。


  • Erlang や Elixir を入れる必要が無いので、サーバを構築するための Ansible だとか AMI を作る作業が楽

  • サーバ内の Erlang や Elixir のバージョンを更新する必要が無いので、インフラの人との調整が不要

  • デプロイするために git からソースを持ってきて起動みたいなことをしなくても、zip ファイル1個転送すれば動かせれる

  • とても単純な仕組みになるので、デプロイシステム全体を理解する手間が減る

Erlang と Elixir が不要になるので、ほぼ何も入っていない Alpine Linux ですら、必要なパッケージは musl, ncurses-libs, zlib, bash だけになります。1

これらのパッケージを入れておけば、パッケージ化した Elixir アプリケーションはそのサーバで動作します。


パッケージ化する方法

基本的には Distillery のドキュメント を読んでその通りにするだけで作れます。

この時重要なのは、その環境に入っている OS や Erlang や Elixir のバージョンでパッケージが作られる ということです。

そのため、本番と同じ OS の上でパッケージ化する必要があります。

このためにパッケージのビルド用サーバを作って、そこでパッケージ化するという手もありますが、個人的には Docker でビルドするのがお手軽で良いと思います。

パッケージをビルドする用のイメージと、それを使ってパッケージ化するスクリプトを書いておけば、誰でも簡単にローカルで パッケージが作れるようになるのが利点です。2


パッケージ化した際の注意点

Distillery でパッケージ化すると、MixExUnit というライブラリが入りません。

そのため実行時に MixExUnit を使うと、それらのモジュールが無いためエラーになります。3

例えば以下のコードは、mix run した場合は動きますが、パッケージ化して起動するとエラーになります。

defmodule Foo do

def f() do
# テスト時は :foo を返す
if Mix.env() == :test do
:foo
else
:bar
end
end
end

これは Mix.env/0 を実行時に使っているからです。

コンパイル時には Mix.env/0 を使えるので、以下のようにするのがいいでしょう。

defmodule Foo do

# テスト時は :foo を返す
if Mix.env() == :test do
def f() do
:foo
end
else
def f() do
:bar
end
end
end

これで、コンパイル時に Mix.env/0 が評価され、どちらかの関数だけが残ることになり、実行時に Mix.env/0 を使わなくなるためエラーが無くなります。

パッケージ化した際に時々やってしまうことがあるので、気を付けましょう。

また、パッケージ化すると mix yacto.migrate のような Mix のコマンドを打てなくなるので、本番データベースのマイグレーションをどうやってやるかというのが問題になってきます。

これは Running migrations にあるように、Mix を使わずにマイグレーションを実行するスクリプトを作り、それをパッケージ化したバイナリ経由で呼び出すのがいいでしょう。

このように少し手間は掛かりますが、デプロイやサーバ管理を手軽にするために、パッケージ化をできるようにしておきましょう。





  1. bash は Distillery が要求しています。それ以外は Erlang VM を動かすために必要なのだけど、これはビルドフラグによっても変わります(例えば --disable-dynamic-ssl-lib を指定しなかった場合、OpenSSL あたりのライブラリが必要になってくる)。 



  2. 自分は prod.secret.exs を使わず環境変数(env パッケージ)を使う人なので、シークレットなデータを知らないせいでちゃんとパッケージ化できないみたいなことは無い。 



  3. これで問題が出た時に、ローカルでパッケージ化できるようにしてると割と簡単にデバッグできるので良い