Help us understand the problem. What is going on with this article?

すごいE本 第21章 on Elixir (アンブレラ&リリース)

環境

sh
$ lsb_release -d
Description:    Ubuntu 18.04.2 LTS

$ elixir -v
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.8.1 (compiled with Erlang/OTP 20)

21.1 水漏れパイプを直す

アプリケーションファイルを更新する

E本では ppool.app erlcount.app の2つのアプリを持っているが、
私は erlcount.app を作っていない。
辻褄合わせということで、テスト用に使った nagger.ex をライブラリにしてしまおう。

アンブレラプロジェクト · Elixir School

sh
$ mix new pool --umbrella
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs

Your umbrella project was created successfully.
Inside your project, you will find an apps/ directory
where you can create and host many apps:

    cd pool
    cd apps
    mix new my_app

Commands like "mix compile" and "mix test" when executed
in the umbrella project root will automatically run
for each application in the apps/ directory.

$ cd pool
$ tree
.
├── README.md
├── apps
├── config
│   └── config.exs
└── mix.exs

2 directories, 3 files

apps ディレクトリに PPool Nagger を作る。

sh
$ cd apps
$ mix new p_pool --module PPool --sup
...
$ mix new nagger
...
$ tree
.
├── nagger
│   ├── README.md
│   ├── config
│   │   └── config.exs
│   ├── lib
│   │   └── nagger.ex
│   ├── mix.exs
│   └── test
│       ├── nagger_test.exs
│       └── test_helper.exs
└── p_pool
    ├── README.md
    ├── config
    │   └── config.exs
    ├── lib
    │   ├── p_pool
    │   │   └── application.ex
    │   └── p_pool.ex
    ├── mix.exs
    └── test
        ├── p_pool_test.exs
        └── test_helper.exs

9 directories, 13 files

そして、前回までのソースをコピーする。

sh
$ ls -F ../../
p_pool/ pool/

$ cp -r ../../p_pool/lib/* p_pool/lib/
$ cp ../../p_pool/test/nagger.ex nagger/lib/
$ tree
.
├── nagger
│   ├── README.md
│   ├── config
│   │   └── config.exs
│   ├── lib
│   │   └── nagger.ex
│   ├── mix.exs
│   └── test
│       ├── nagger_test.exs
│       └── test_helper.exs
└── p_pool
    ├── README.md
    ├── config
    │   └── config.exs
    ├── lib
    │   ├── p_pool
    │   │   ├── application.ex
    │   │   ├── poolvisor.ex
    │   │   ├── stash.ex
    │   │   ├── supervisor.ex
    │   │   └── workervisor.ex
    │   └── p_pool.ex
    ├── mix.exs
    └── test
        ├── p_pool_test.exs
        └── test_helper.exs

9 directories, 17 files

アプリケーションをコンパイルする

sh
$ cd ..  # アンブレラ pool のルートに戻る
$ ls -F
README.md  apps/  config/  mix.exs

$ iex -S mix

$ mix compile でもいいね。

iex
iex(1)> :application.which_applications()
[
  {:p_pool, 'p_pool', '0.1.0'},
  {:nagger, 'nagger', '0.1.0'},
  {:logger, 'logger', '1.8.1'},
  {:mix, 'mix', '1.8.1'},
  {:iex, 'iex', '1.8.1'},
  {:elixir, 'elixir', '1.8.1'},
  {:compiler, 'ERTS  CXC 138 10', '7.3.2'},
  {:stdlib, 'ERTS  CXC 138 10', '3.8.1'},
  {:kernel, 'ERTS  CXC 138 10', '6.3.1'}
]
iex(2)> PPool.start_pool(Nagger, :start_link, [])
{:ok, #PID<0.151.0>}
iex(3)> PPool.run(Nagger, [self(), :msg, 500, 3])
{:ok, #PID<0.155.0>}
iex(4)> Received down message.
flush()
{#PID<0.155.0>, :msg}
{#PID<0.155.0>, :msg}
{#PID<0.155.0>, :msg}
:ok

おお、いぇー。
PPool Nagger 共に mix.exs が今までとは少し違う。

pool/apps/p_pool/mix.exs
defmodule PPool.MixProject do
  use Mix.Project

  def project do
    [
      app: :p_pool,
      version: "0.1.0",

      # E本の内容に合わせるならば以下を追加
      description: "Run and enqueue different concurrent tasks",

      # Mix によって追加されてる
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",

      elixir: "~> 1.8",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  def application do
    [
      extra_applications: [:logger],
      mod: {PPool.Application, []},
      registered: [PPool.Supervisor]  # お忘れなく
    ]
  end

  defp deps do
    []
  end
end
sh
$ tree _build
_build
└── dev
    ├── consolidated
    │   ├── Elixir.Collectable.beam
    │   ├── Elixir.Enumerable.beam
    │   ├── Elixir.IEx.Info.beam
    │   ├── Elixir.Inspect.beam
    │   ├── Elixir.List.Chars.beam
    │   └── Elixir.String.Chars.beam
    └── lib
        ├── nagger
        │   └── ebin
        │       ├── Elixir.Nagger.beam
        │       └── nagger.app
        └── p_pool
            └── ebin
                ├── Elixir.PPool.Application.beam
                ├── Elixir.PPool.Poolvisor.beam
                ├── Elixir.PPool.Stash.beam
                ├── Elixir.PPool.Supervisor.beam
                ├── Elixir.PPool.Workervisor.beam
                ├── Elixir.PPool.beam
                └── p_pool.app

7 directories, 15 files

なるほどね。(日経電子ry

※ アンブレラ内部の依存関係

今回の場合には関係のない話なのだが、
例えば PPool が起動時に Nagger に依存していたとする。

pool/apps/p_pool/mix.exs
defmodule PPool.MixProject do
  use Mix.Project
  ...
  defp deps do
    [{:nagger, in_umbrella: true}]
  end
end

なるほどね。(にっry

※ Distillery でのリリース

Elixir では Distillery を使う。

pool/apps/p_pool/mix.exs
defmodule Pool.MixProject do
  use Mix.Project

  def project do
    [
      apps_path: "apps",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  defp deps do
    [
      distillery: "~> 2.0"  # 追加
    ]
  end
end
sh
$ mix do deps.get, deps.compile
...
$ mix release.init

An example config file has been placed in rel/config.exs, review it,
make edits as needed/desired, and then run `mix release` to build the release

rel/config.exs が生成されたらしいので見てみよう。

pool/rel/config.exs
# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
~w(rel plugins *.exs)
|> Path.join()
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))

use Mix.Releases.Config,
    # This sets the default release built by `mix release`
    default_release: :default,
    # This sets the default environment used by `mix release`
    default_environment: Mix.env()

# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/config/distillery.html


# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile

environment :dev do
  # If you are running Phoenix, you should make sure that
  # server: true is set and the code reloader is disabled,
  # even in dev mode.
  # It is recommended that you build with MIX_ENV=prod and pass
  # the --env flag to Distillery explicitly if you want to use
  # dev mode.
  set dev_mode: true
  set include_erts: false
  set cookie: :"Qt.(TQmtgz<olih:/ihSj$V2{`NFP&m3~CL>?eyRz9}Au1T|m|{dF|mrCeC&9`r<"
end

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :",Xf:!buFeY`>~kG1yw)kHjD2AYRqF61lBiu,XFB)mD^{TgbdWeL8sugf&hONdAx5"
  set vm_args: "rel/vm.args"
end

# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default

release :pool do
  set version: "0.1.0"
  set applications: [
    :runtime_tools,
    nagger: :permanent,
    p_pool: :permanent
  ]
end

アンブレラ・プロジェクトだと問題あるかもと思ったけど、いけてるみたい。
この設定だと本番環境でリリースした場合に Erlang ランタイムが含まれそうだね。
それでは開発版のリリースといこう。

sh
$ mix release
==> Assembling release..
==> Building release pool:0.1.0 using environment dev
==> You have set dev_mode to true, skipping archival phase
Release successfully built!
To start the release you have built, you can use one of the following tasks:

    # start a shell, like 'iex -S mix'
    > _build/dev/rel/pool/bin/pool console

    # start in the foreground, like 'mix run --no-halt'
    > _build/dev/rel/pool/bin/pool foreground

    # start in the background, must be stopped with the 'stop' command
    > _build/dev/rel/pool/bin/pool start

If you started a release elsewhere, and wish to connect to it:

    # connects a local shell to the running node
    > _build/dev/rel/pool/bin/pool remote_console

    # connects directly to the running node's console
    > _build/dev/rel/pool/bin/pool attach

For a complete listing of commands and their use:

    > _build/dev/rel/pool/bin/pool help

iex -S mix ライクに使うなら console だよって書いてあるね。

sh
$ _build/dev/rel/pool/bin/pool console
iex
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(pool@127.0.0.1)1> PPool.start_pool(Nagger, :start_link, [])
{:ok, #PID<0.790.0>}
iex(pool@127.0.0.1)2> PPool.run(Nagger, [self(), make_ref(), 500, 3])
{:ok, #PID<0.794.0>}
iex(pool@127.0.0.1)3> Received down message.
flush()
{#PID<0.794.0>, #Reference<0.1485725171.1652031495.47565>}
{#PID<0.794.0>, #Reference<0.1485725171.1652031495.47565>}
{#PID<0.794.0>, #Reference<0.1485725171.1652031495.47565>}
:ok

おお、やったぁ。

デプロイ

本番環境でリリース。

sh
$ MIX_ENV=prod mix release
...
$ ll _build/prod/rel/pool/releases/0.1.0/合計 12892
drwxrwxr-x  5 feelinguy feelinguy     4096  5月 10 04:11 ./
drwxrwxr-x  3 feelinguy feelinguy     4096  5月 10 04:11 ../
drwxrwxr-x  2 feelinguy feelinguy     4096  5月 10 04:11 commands/
-rw-rw-r--  1 feelinguy feelinguy    34974  5月 10 04:11 config.boot
-rw-rw-r--  1 feelinguy feelinguy    47582  5月 10 04:11 config.script
drwxrwxr-x 10 feelinguy feelinguy     4096  5月 10 04:11 hooks/
drwxrwxr-x  4 feelinguy feelinguy     4096  5月 10 04:11 libexec/
-rw-rw-r--  1 feelinguy feelinguy    34910  5月 10 04:11 no_dot_erlang.boot
-rw-rw-r--  1 feelinguy feelinguy    47518  5月 10 04:11 no_dot_erlang.script
-rw-rw-r--  1 feelinguy feelinguy    35616  5月 10 04:11 pool.boot
-rwxrwxrwx  1 feelinguy feelinguy     5768  5月 10 04:11 pool.ps1*
-rw-rw-r--  1 feelinguy feelinguy      448  5月 10 04:11 pool.rel
-rw-rw-r--  1 feelinguy feelinguy    48166  5月 10 04:11 pool.script
-rwxrwxrwx  1 feelinguy feelinguy     4195  5月 10 04:11 pool.sh*
-rw-rw-r--  1 feelinguy feelinguy 12719279  5月 10 04:11 pool.tar.gz  # これだよー
-rw-rw-r--  1 feelinguy feelinguy    35616  5月 10 04:11 start.boot
-rw-rw-r--  1 feelinguy feelinguy    48166  5月 10 04:11 start.script
-rw-rw-r--  1 feelinguy feelinguy    34910  5月 10 04:11 start_clean.boot
-rw-rw-r--  1 feelinguy feelinguy    47518  5月 10 04:11 start_clean.script
-rw-rw-r--  1 feelinguy feelinguy       97  5月 10 04:11 sys.config
-rw-rw-r--  1 feelinguy feelinguy      849  5月 10 04:11 vm.args

一番大きなサイズの tarball がある、12MBぐらい。
この pool.tar.gz を本番環境に展開すればデプロイ完了。

sh
$ mkdir ~/deploy
$ cp _build/prod/rel/pool/releases/0.1.0/pool.tar.gz ~/deploy/
$ cd ~/deploy
$ tar -zxf pool.tar.gz
$ ls -F
bin/  erts-10.3.4/  lib/  pool.tar.gz  releases/

$ cd bin
$ ./pool start
$ ./pool ping
pong

$ ./pool remote_console
...
# iex セッション Ctrl+C
...
$ ./pool stop
ok

できたねぇ。
例えばこれを別アーキテクチャの環境に持っていっても動かない。
今時なんかだと Docker とかでやるんでしょうね。覚えること多すぎ。

おわりに

次の第22章はやりません、たぶん。
ホット・コード・ロードの話になるんだけど、これまたきつい。
これについては、書籍 プログラミング Elixir の第18章が参考になる。
ちょっと古いので Distillery ではなく EXRM というのを使ってるんだけど、
どっちのツールも開発者は同じなのでなんとかなる。

英語も読めなければ、メンターもいない私。がんばったよ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした