前回の記事 で、アプリケーションが作れそうな実感は得られたので、
今度はデプロイできる実感を得たいと思い、デプロイの手順を試してみました。
結構紆余曲折しましたので、急いで手順だけ知りたい方は、一番最後のまとめだけ読んでください。
あとですね...Elixir/ErlangにはMacでビルドしたやつはLinuxで動かないという問題があると噂に聞いていますので、手元で動くようになったとしても本番サーバーで動かせるようになるまではまだまだ遠いかもしれません。
公式ドキュメントを読んでみる
デプロイするには3つのステップがある。
- Application secrets の処理
- Assets のコンパイル
- プロダクションモードでサーバーの起動
そして、これらがどのように行われるかは、インフラによって異なり、
- Heroku を使う場合
- Heroku を使わないなら Distillery を使うのをオススメ
の 2 つの方法が主にオススメされているようです。
まずは上記の 3 つのステップについて、もう少しドキュメントを
読んで手元で試してみましょう。
Application secrets の扱い
これは、DBのユーザー名やパスワードなどのことですね。
Phoenix ではこれらは config/prod.secret.exs
というファイルに
書くことになっていて、git には入れないみたいです。
ちょっと中身を見てみましょう。
use Mix.Config
# In this file, we keep production configuration that
# you'll likely want to automate and keep away from
# your version control system.
#
# You should document the content of this
# file or create a script for recreating it, since it's
# kept out of version control and might be hard to recover
# or recreate for your teammates (or yourself later on).
config :blog, BlogWeb.Endpoint,
secret_key_base: "hogehogefugafuga+hogehogefugafuga+jugemjugemgokounosurikire"
# Configure your database
config :blog, Blog.Repo,
adapter: Ecto.Adapters.Postgres,
username: "hogehoge",
password: "fugafuga",
database: "blog_prod",
pool_size: 15
Rails の database.yml とか secrets.yml 的なやつですね。
で、この情報をどうにかして production 環境に配置する必要があるが、
- Heroku でやるように環境変数経由で渡す方法
- デプロイで上書きされない領域 (
/var/config.prod.exs
など)に配置して、このパスから読み込むようにする方法(Capistrano の shared みたいなイメージですかね?)
などが考えられる、と。
Assets のコンパイル
Phoenix ではデフォルトでは brunch を使うよ、と。
なんでしょう?初耳なんですけど。 サイトを見ると、gulp と比較してるようなので、
gulp みたいなやつなんでしょう、とわかったつもりになったことにして、次へ。
で、ビルドはこうする、と。
# 必要なライブラリを入れて
$ mix deps.get --only prod
# コンパイルして
$ MIX_ENV=prod mix compile
# assets のコンパイルして
$ brunch build --production
# assets のファイル名にダイジェストを付与する
$ MIX_ENV=prod mix phoenix.digest
やってみましょう。
$ mix deps.get --only prod
Running dependency resolution...
Dependency resolution completed:
connection 1.0.4
cowboy 1.1.2
cowlib 1.0.2
db_connection 1.1.2
decimal 1.4.0
ecto 2.1.6
gettext 0.13.1
mime 1.1.0
phoenix 1.3.0
phoenix_ecto 3.2.3
phoenix_html 2.10.4
phoenix_pubsub 1.0.2
plug 1.4.3
poison 3.1.0
poolboy 1.5.1
postgrex 0.13.3
ranch 1.3.2
All dependencies up to date
ふむ。
$ MIX_ENV=prod mix compile
==> connection
Compiling 1 file (.ex)
Generated connection app
==> gettext
Compiling 1 file (.erl)
Compiling 20 files (.ex)
warning: String.strip/1 is deprecated, use String.trim/1
lib/gettext/po/parser.ex:47
warning: String.strip/1 is deprecated, use String.trim/1
lib/gettext/po/parser.ex:55
warning: String.lstrip/1 is deprecated, use String.trim_leading/1
lib/gettext/po/parser.ex:65
warning: String.to_char_list/1 is deprecated, use String.to_charlist/1
lib/gettext/po/tokenizer.ex:147
Generated gettext app
===> Compiling ranch
===> Compiling poolboy
==> decimal
Compiling 1 file (.ex)
warning: Integer.to_char_list/1 is deprecated, use Integer.to_charlist/1
lib/decimal.ex:944
warning: Integer.to_char_list/1 is deprecated, use Integer.to_charlist/1
lib/decimal.ex:963
warning: Integer.to_char_list/1 is deprecated, use Integer.to_charlist/1
lib/decimal.ex:986
Generated decimal app
warning: String.strip/1 is deprecated, use String.trim/1
/Users/tmaeda/src/github.com/tmaeda/phenix-tut/blog/deps/poison/mix.exs:4
==> poison
Compiling 4 files (.ex)
warning: Integer.to_char_list/2 is deprecated, use Integer.to_charlist/2
lib/poison/encoder.ex:173
Generated poison app
==> db_connection
Compiling 23 files (.ex)
Generated db_connection app
==> phoenix_pubsub
Compiling 12 files (.ex)
Generated phoenix_pubsub app
===> Compiling cowlib
src/cow_multipart.erl:392: Warning: crypto:rand_bytes/1 is deprecated and will be removed in a future release; use crypto:strong_rand_bytes/1
===> Compiling cowboy
==> mime
Compiling 1 file (.ex)
warning: String.strip/1 is deprecated, use String.trim/1
lib/mime.ex:28
Generated mime app
==> plug
Compiling 1 file (.erl)
Compiling 44 files (.ex)
Generated plug app
==> phoenix_html
Compiling 8 files (.ex)
Generated phoenix_html app
==> phoenix
Compiling 74 files (.ex)
Generated phoenix app
==> postgrex
Compiling 62 files (.ex)
Generated postgrex app
==> ecto
Compiling 69 files (.ex)
Generated ecto app
==> phoenix_ecto
Compiling 4 files (.ex)
Generated phoenix_ecto app
==> blog
Compiling 17 files (.ex)
Generated blog app
Generated ほにゃらら app
ってのがいっぱい出てますね。
いわゆるライブラリがひとつの小さなアプリケーションという考え方なんですかね?
$ brunch build --production
-bash: brunch: コマンドが見つかりません
なるほどw brew でもダメだったんで、仕方なくサイトを読んで、
npm で入れるらしいことがわかりました。が、yarn 推しなので yarn で入れますよ。
$ yarn global add brunch
yarn global v0.24.6
warning No license field
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 📃 Building fresh packages...
warning Your current version of Yarn is out of date. The latest version is "0.27.5" while you're on "0.24.6".
success Installed "brunch@2.10.10" with binaries:
- brunch
warning No license field
✨ Done in 11.45s.
気を取り直して。
$ brunch build --production
15:34:07 - error: The directory does not seem to be a Brunch project. Create brunch-config.js or run brunch from the correct directory. Cannot find module '/blog/brunch-config'
15:34:07 - error: Here's a minimal brunch-config.js to get you started:
module.exports = {
files: {
javascripts: {
joinTo: 'app.js'
}
}
};
はいいい???
どこかに config あるんですかねぇ?
$ find . -name brunch-config.js
./assets/brunch-config.js
./deps/phoenix/brunch-config.js
ほほーう。じゃ、assets の下でやってみますか。
$ cd assets
$ brunch build --production
15:36:59 - info: compiled 6 files into 2 files, copied 3 in 2.6 sec
お、なんかできたっぽいですね。次。
$ MIX_ENV=prod mix phoenix.digest
** (Mix) The task "phoenix.digest" could not be found. Did you mean "phoenix.new"?
はいいいい???ディレクトリが違うんですかね?
$ cd ..
$ MIX_ENV=prod mix phoenix.digest
mix phoenix.digest is deprecated. Use phx.digest instead.
Check your digested files at "priv/static"
できたけど、deprecated だと。
$ MIX_ENV=prod mix phx.digest
Check your digested files at "priv/static"
ふぅ、やっと辿り着いた。 Check しろっていうんで、Check しますか。
$ ls -1 priv/static/
cache_manifest.json
css
favicon-a8ca4e3a2bb8fea46a9ee9e102e7d3eb.ico
favicon.ico
images
js
robots-067185ba27a5d9139b10a759679045bf.txt
robots-067185ba27a5d9139b10a759679045bf.txt.gz
robots.txt
robots.txt.gz
ふむふむ、なるほど。digest つける作業って、 brunch でやりそうな感じがしますけど、
mix でやるんですねぇ。
サーバーの起動
$ PORT=4001 MIX_ENV=prod mix phoenix.server
ですが、必要に応じて、この前に
$ MIX_ENV=prod mix ecto.migrate
もやらないといけない、と。
やってみましょう。
$ MIX_ENV=prod mix ecto.create
The database for Blog.Repo has been created
$ MIX_ENV=prod mix ecto.migrate
15:45:23.165 [info] == Running Blog.Repo.Migrations.CreateArticles.change/0 forward
15:45:23.165 [info] create table articles
15:45:23.171 [info] == Migrated in 0.0s
で、
$ PORT=4001 MIX_ENV=prod mix phoenix.server
mix phoenix.server is deprecated. Use phx.server instead.
15:46:06.683 [info] Running BlogWeb.Endpoint with Cowboy using http://:::4001
ん?デーモンモードじゃないの???しかも、また deprecated って出てるし...
$ PORT=4001 MIX_ENV=prod mix phx.server
15:47:55.711 [info] Running BlogWeb.Endpoint with Cowboy using http://:::4001
んー、おんなじですねぇ...
一応、この状態で、ブラウザからアクセスしてみますか。
おー、出た出た。まぁ、当然ですけどね。心なしか、development モードより、
キビキビ動くような気がします。
ログはこんな感じ。
15:48:27.978 request_id=4jddi0sv4o928pnv33dq813r88cu1eht [info] GET /
15:48:28.023 request_id=4jddi0sv4o928pnv33dq813r88cu1eht [info] Sent 200 in 44ms
15:48:34.577 request_id=3drdgeaa3udj8uglvkhskb1sf0tfcrp8 [info] GET /articles
15:48:34.626 request_id=3drdgeaa3udj8uglvkhskb1sf0tfcrp8 [info] Sent 200 in 48ms
15:48:36.313 request_id=nohsc54o07271kmabhoppupuoj8sketn [info] GET /articles/new
15:48:36.361 request_id=nohsc54o07271kmabhoppupuoj8sketn [info] Sent 200 in 48ms
15:49:17.992 request_id=7i7esq0kfsn75us520gs62of0rkk3s4q [info] GET /articles
15:49:18.007 request_id=7i7esq0kfsn75us520gs62of0rkk3s4q [info] Sent 200 in 14ms
15:49:40.925 request_id=3ljqp8ke710rkj0hvc0ejk7k766k1lhi [info] GET /articles/new
15:49:40.926 request_id=3ljqp8ke710rkj0hvc0ejk7k766k1lhi [info] Sent 200 in 548µs
15:49:44.194 request_id=bslu70c0k8o8tp3ia96gu9iqru4ja59f [info] POST /articles
15:49:44.276 request_id=bslu70c0k8o8tp3ia96gu9iqru4ja59f [info] Sent 302 in 81ms
15:49:44.302 request_id=voludfcpgkl3u1f7gvntgqpf0509m6b7 [info] GET /articles/1
15:49:44.359 request_id=voludfcpgkl3u1f7gvntgqpf0509m6b7 [info] Sent 200 in 56ms
SQLとか全くでないんですね。噂に聞く「マイクロ秒」ってのがチラホラ出てますね。
エラーページはこんな感じ。当然、スタックトレースなどは出なくなってます。
開発者コンソールのNetworkタブはこんな感じ。
websocket が居ないですね。やはりあれは livereload 専用だったのか...
さて、デーモンモードの話に戻ります。
公式ドキュメントには別のコマンドも載っているので、そっちも試してみましょう。
$ PORT=4001 MIX_ENV=prod iex -S mix phx.server
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
17:17:44.362 [info] Running BlogWeb.Endpoint with Cowboy using http://:::4001
Interactive Elixir (1.5.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 17:18:40.549 request_id=1rbg8400df6nrk1jnokvoqq7hhua54sn [info] GET /
17:18:40.601 request_id=1rbg8400df6nrk1jnokvoqq7hhua54sn [info] Sent 200 in 52ms
nil
iex(2)> 17:18:56.882 request_id=cg6384a3pdm9gkn1vthouvfh2vd3nc0l [info] GET /articles
17:18:56.941 request_id=cg6384a3pdm9gkn1vthouvfh2vd3nc0l [info] Sent 200 in 59ms
nil
iex(3)>
おやぁ???何ですか、これはぁ? rails s と rails c を合わせたような...使い途がよくわかりません。
次、
Or run it detached from the iex console. This effectively daemonizes the process so it can run independently in the background
MIX_ENV=prod PORT=4001 elixir --detached -S mix do compile, phoenix.server
って書いてるので、これがデーモンなんでしょうね。しかし、このコマンド、絶対に違うような気がするので、気を利かせて以下のように実行してみます。
$ PORT=4001 MIX_ENV=prod elixir --detached -S mix phx.server
あー、動きました。動きましたけど、ロ、、ログファイルは、、、
findしてもそれっぽいものが見つからないので、ググると、
logger_file_backend
を使え、と。なるほどー。うーん...
--detached
というのは VM を console から切り離した状態で動かすよ、ということらしいです。
$ elixir --help
Usage: elixir [options] [.exs file] [data]
-e COMMAND Evaluates the given command (*)
-r FILE Requires the given files/patterns (*)
-S SCRIPT Finds and executes the given script in PATH
-pr FILE Requires the given files/patterns in parallel (*)
-pa PATH Prepends the given path to Erlang code path (*)
-pz PATH Appends the given path to Erlang code path (*)
--app APP Starts the given app and its dependencies (*)
--cookie COOKIE Sets a cookie for this distributed node
--detached Starts the Erlang VM detached from console # 👈
--erl SWITCHES Switches to be passed down to Erlang (*)
--help, -h Prints this message and exits
--hidden Makes a hidden node
--logger-otp-reports BOOL Enables or disables OTP reporting
--logger-sasl-reports BOOL Enables or disables SASL reporting
--name NAME Makes and assigns a name to the distributed node
--no-halt Does not halt the Erlang VM after execution
--sname NAME Makes and assigns a short name to the distributed node
--version, -v Prints Elixir version and exits
--werl Uses Erlang's Windows shell GUI (Windows only)
ログについては以下のような方法が思い浮かびますが...
まとめ
だいぶ紆余曲折したので、もう一度手順まとめますね。
# 事前準備
$ yarn global add brunch
$ mix deps.get --only prod
$ MIX_ENV=prod mix compile
$ cd assets
$ brunch build --production
$ cd ..
$ MIX_ENV=prod mix phx.digest
$ MIX_ENV=prod mix ecto.create
$ MIX_ENV=prod mix ecto.migrate
$ PORT=4001 MIX_ENV=prod mix phx.server
- プロダクションで動かす手順がわかりました。
- 公式ドキュメントにはだいぶ地雷があります。あとで時間ができたらプルリクエストの状況確認しておきます。
- ログの扱いなど、いろいろ考えることがありそう...
- この辺、Heroku は結構うまくやってくれそうな気がしているので、 次回は Heroku でお会いしましょう