exrm
リリースの作業を簡単にしてくれるElixirのパッケージ
Elixirにはバージョンが2つあり、それぞれ独立している
-
コード
-
データ
-
コードのバージョン
mix.exs
のproject
-
データのバージョン
OTPアプリケーションの状態はサーバーによって維持されている
各サーバーの状態は独立
=>
データは各サーバーのモジュール内でバージョンをつける
@vsn
- プロジェクトの依存関係に
exrm
を追加mix.exs
のdeps関数に追加 - exrmのインストール
$ mix do deps.get, deps.compile
- リリース
$ mix release
ディレクトリ構成
rel/sequence2/release/0.1.0/sequence2.tar.gz
: 実行するために必要なものが全て入っている
goh@goh% tree rel/
rel/
└── sequence2
├── bin
│ ├── install_upgrade.escript
│ :
├── erts-10.1
│ ├── bin
│ │ ├── beam.smp
│ │ :
│ ├── include
│ │ ├── driver_int.h
│ │ :
│ └── lib
│ ├── internal
│ :
├── lib
│ ├── bbmustache-1.6.1
│ │ └── ebin
│ │ ├── bbmustache.app
│ │ └── bbmustache.beam
│ ├── cf-0.3.1
│ │ └── ebin
│ │ ├── cf.app
│ │ :
│ ├── compiler-7.2.5
│ │ └── ebin
│ │ ├── beam_a.beam
│ │ :
│ ├── elixir-1.7.3
│ │ └── ebin
│ │ ├── Elixir.Access.beam
│ │ :
│ ├── erlware_commons-1.2.0
│ │ ├── ebin
│ │ :
│ ├── exrm-1.0.8
│ │ ├── ebin
│ │ :
│ ├── getopt-1.0.1
│ │ └── ebin
│ │ ├── getopt.app
│ │ └── getopt.beam
│ ├── iex-1.7.3
│ │ └── ebin
│ │ ├── Elixir.IEx.App.beam
│ │ :
│ ├── kernel-6.1
│ │ ├── ebin
│ │ :
│ ├── logger-1.7.3
│ │ └── ebin
│ │ ├── Elixir.Logger.App.beam
│ │ :
│ ├── providers-1.7.0
│ │ ├── ebin
│ │ :
│ ├── relx-3.27.0
│ │ ├── ebin
│ │ :
│ ├── sasl-3.2.1
│ │ └── ebin
│ │ ├── alarm_handler.beam
│ │ :
│ ├── sequence2-0.1.0
│ │ └── ebin
│ │ ├── Elixir.Sequence2.Application.beam
│ │ :
│ └── stdlib-3.6
│ ├── ebin
│ :
└── releases
├── 0.1.0
│ ├── no_dot_erlang.boot
│ ├── sequence2.bat
│ ├── sequence2.boot
│ ├── sequence2.rel
│ ├── sequence2.script
│ ├── sequence2.sh
│ ├── sequence2.tar.gz
│ ├── start.boot
│ ├── start_clean.boot
│ ├── sys.config
│ └── vm.args
├── RELEASES
└── start_erl.data
62 directories, 735 files
mix do deps.get, deps.compile
goh@goh% mix do deps.get, deps.compile
Resolving Hex dependencies...
Dependency resolution completed:
New:
bbmustache 1.6.1
cf 0.3.1
erlware_commons 1.2.0
exrm 1.0.8
getopt 1.0.1
providers 1.7.0
relx 3.27.0
* Getting exrm (Hex package)
* Getting relx (Hex package)
* Getting bbmustache (Hex package)
* Getting cf (Hex package)
* Getting erlware_commons (Hex package)
* Getting getopt (Hex package)
* Getting providers (Hex package)
===> Compiling cf
===> Compiling erlware_commons
===> Compiling bbmustache
===> Compiling getopt
===> Compiling providers
===> Compiling relx
warning: variable "description" does not exist and is being expanded to "description()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:8
warning: variable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:9
warning: variable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:10
warning: variable "docs" does not exist and is being expanded to "docs()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:11
==> exrm
Compiling 11 files (.ex)
warning: "not expr1 in expr2" is deprecated. Instead use "expr1 not in expr2" if you require Elixir v1.5+, or "not(expr1 in expr2)" if you have to support earlier Elixir versions
lib/exrm/deps.ex:21
warning: the Behaviour module is deprecated. Instead of using this module, use the @callback and @macrocallback module attributes. See the documentation for Module for more information on these attributes
lib/exrm/plugin.ex:46: (module)
(elixir) src/elixir_compiler.erl:71: :elixir_compiler.dispatch/4
(elixir) src/elixir_compiler.erl:68: :elixir_compiler.compile/3
(elixir) src/elixir_module.erl:281: :elixir_module.eval_form/6
(elixir) src/elixir_module.erl:79: :elixir_module.compile/5
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:266
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:269
warning: variable "priv_path" does not exist and is being expanded to "priv_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:341
warning: variable "rel_source_path" does not exist and is being expanded to "rel_source_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:343
warning: variable "priv_path" does not exist and is being expanded to "priv_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:345
warning: variable "rel_file_source_path" does not exist and is being expanded to "rel_file_source_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:347
warning: variable "rel_dest_path" does not exist and is being expanded to "rel_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:359
warning: variable "rel_dest_path" does not exist and is being expanded to "rel_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:360
warning: variable "rel_file_dest_path" does not exist and is being expanded to "rel_file_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:374
warning: variable "rel_file_dest_path" does not exist and is being expanded to "rel_file_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/exrm/utils/utils.ex:375
warning: variable "get_plugins" does not exist and is being expanded to "get_plugins()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.plugins.ex:25
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.plugins.ex:55
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.plugins.ex:94
warning: variable "rel_file_dest_path" does not exist and is being expanded to "rel_file_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.clean.ex:77
warning: variable "rel_dest_path" does not exist and is being expanded to "rel_dest_path()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.clean.ex:86
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:105
warning: variable "get_elixir_lib_paths" does not exist and is being expanded to "get_elixir_lib_paths()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:129
warning: variable "relx_config_path" does not exist and is being expanded to "relx_config_path()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:243
warning: variable "relx_config_path" does not exist and is being expanded to "relx_config_path()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:252
warning: Kernel.to_char_list/1 is deprecated. Use Kernel.to_charlist/1 instead
lib/exrm/appups.ex:55
warning: Kernel.to_char_list/1 is deprecated. Use Kernel.to_charlist/1 instead
lib/exrm/appups.ex:55
warning: variable "relx_config_path" does not exist and is being expanded to "relx_config_path()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:302
warning: variable "v2_file" is unused
lib/exrm/appups.ex:84
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:322
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:331
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:354
warning: variable "abort!" does not exist and is being expanded to "abort!()", please use parentheses to remove the ambiguity or change the variable name
lib/mix/tasks/release.ex:360
warning: function beam_imports/1 is unused
lib/exrm/appups.ex:98
warning: Kernel.to_char_list/1 is deprecated. Use Kernel.to_charlist/1 instead
lib/exrm/appups.ex:55
warning: Mix.Config.read!/1 is deprecated. Use eval!/2 instead
lib/exrm/utils/utils.ex:34
warning: Mix.Dep.loaded/1 is deprecated. Mix.Dep.loaded/1 was private API and you should not use it
Found at 4 locations:
lib/exrm/deps.ex:10
lib/exrm/deps.ex:47
lib/exrm/deps.ex:138
lib/exrm/plugins/appups.ex:15
warning: String.ljust/2 is deprecated. Use String.pad_trailing/2 instead
lib/mix/tasks/release.plugins.ex:31
warning: String.rstrip/2 is deprecated. Use String.trim_trailing/2 with a binary as second argument instead
Found at 2 locations:
lib/mix/tasks/release.clean.ex:110
lib/mix/tasks/release.ex:101
warning: String.strip/1 is deprecated. Use String.trim/1 instead
lib/exrm/utils/utils.ex:281
warning: String.strip/2 is deprecated. Use String.trim/2 with a binary second argument instead
lib/exrm/utils/utils.ex:280
warning: String.to_char_list/1 is deprecated. Use String.to_charlist/1 instead
Found at 9 locations:
lib/exrm/appups.ex:23
lib/exrm/appups.ex:28
lib/exrm/appups.ex:58
lib/exrm/appups.ex:59
lib/exrm/appups.ex:65
lib/exrm/plugin.ex:96
lib/exrm/utils/utils.ex:253
lib/mix/tasks/release.ex:129
lib/mix/tasks/release.ex:204
Generated exrm app
mix release
goh@goh% mix release
Compiling 6 files (.ex)
warning: function init/1 required by behaviour GenServer is not implemented (in module Sequence2.Stash).
We will inject a default implementation for now:
def init(args) do
{:ok, args}
end
You can copy the implementation above or define your own that converts the arguments given to GenServer.start_link/3 to the server state.
lib/sequence2/stash.ex:1
Generated sequence2 app
warning: not passing the :switches or :strict option to OptionParser is deprecated
(elixir) lib/option_parser.ex:562: OptionParser.build_config/1
(elixir) lib/option_parser.ex:197: OptionParser.parse/2
lib/mix/tasks/release.ex:434: Mix.Tasks.Release.parse_args/1
lib/mix/tasks/release.ex:68: Mix.Tasks.Release.do_run/1
(mix) lib/mix/task.ex:316: Mix.Task.run_task/3
Building release with MIX_ENV=dev.
warning: variable "description" does not exist and is being expanded to "description()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:8
warning: variable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:9
warning: variable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:10
warning: variable "docs" does not exist and is being expanded to "docs()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:11
==> The release for sequence2-0.1.0 is ready!
==> You can boot a console running your release with `$ rel/sequence2/bin/sequence2 console`
sshを使ってローカルマシンにデプロイする
- リリース格納先:
~/elixir/programming_elixir/deploy
ディレクトリの作成、ファイルのコピーは手作業で行うが、Capistrano、Ansibleで自動化するべき
事前準備
- リモートログインをONにする
システム環境設定 > 共有 - 自分の公開鍵を
~/.ssh/authorized_keys
に登録する
goh@goh% ssh localhost mkdir ~/elixir/programming_elixir/deploy
アプリケーション名.tar.gz
を配備する- 展開する
goh@goh% scp rel/sequence2/releases/0.1.0/sequence2.tar.gz localhost:~/elixir/programming_elixir/deploy
sequence2.tar.gz 100% 11MB 140.7MB/s 00:00
goh@goh% ssh localhost tar xf ~/elixir/programming_elixir/deploy/sequence2.tar.gz -C ~/elixir/programming_elixir/deploy
実行
-t
オプションを付けることで ^C
を実行できる
goh@goh% ssh -t localhost ~/elixir/programming_elixir/deploy/bin/sequence2 console
Exec: /Users/goh/elixir/programming_elixir/deploy/erts-10.1/bin/erlexec -boot /Users/goh/elixir/programming_elixir/deploy/releases/0.1.0/sequence2 -config /Users/goh/elixir/programming_elixir/deploy/running-config/sys.config -boot_var ERTS_LIB_DIR /Users/goh/elixir/programming_elixir/deploy/erts-10.1/../lib -env ERL_LIBS /Users/goh/elixir/programming_elixir/deploy/lib -pa /Users/goh/elixir/programming_elixir/deploy/lib/sequence2-0.1.0/consolidated -args_file /Users/goh/elixir/programming_elixir/deploy/running-config/vm.args -mode embedded -user Elixir.IEx.CLI -extra --no-halt +iex -- console
Root: /Users/goh/elixir/programming_elixir/deploy
/Users/goh/elixir/programming_elixir/deploy
Erlang/OTP 21 [erts-10.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex([email protected])1> Sequence2.Server.next_number
456
iex([email protected])2> Sequence2.Server.next_number
457
next_number関数が返すメッセージを変更する
- before: 458
- after: The next number is 458
goh@goh% mix release
warning: not passing the :switches or :strict option to OptionParser is deprecated
(elixir) lib/option_parser.ex:562: OptionParser.build_config/1
(elixir) lib/option_parser.ex:197: OptionParser.parse/2
lib/mix/tasks/release.ex:434: Mix.Tasks.Release.parse_args/1
lib/mix/tasks/release.ex:68: Mix.Tasks.Release.do_run/1
(mix) lib/mix/task.ex:316: Mix.Task.run_task/3
Building release with MIX_ENV=dev.
warning: variable "description" does not exist and is being expanded to "description()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:8
warning: variable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:9
warning: variable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:10
warning: variable "docs" does not exist and is being expanded to "docs()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:11
This is an upgrade, verifying appups exist for updated dependencies..
==> All dependencies have appups ready for release!
==> Generated .appup for sequence2 0.1.0 -> 0.2.0
==> The release for sequence2-0.2.0 is ready!
==> You can boot a console running your release with `$ rel/sequence2/bin/sequence2 console`
- リリースディレクトリの作成:
~/elixir/programming_elixir/deploy/releases/0.0.2
sequence2.tar.gz
を配備する- ホットアップグレードする
goh@goh% ssh localhost mkdir ~/elixir/programming_elixir/deploy/releases/0.2.0
goh@goh% scp rel/sequence2/releases/0.2.0/sequence2.tar.gz localhost:~/elixir/programming_elixir/deploy/releases/0.2.0
sequence2.tar.gz 100% 11MB 125.3MB/s 00:00
goh@goh% ssh localhost ~/elixir/programming_elixir/deploy/bin/sequence2 upgrade 0.2.0
Release 0.2.0 not found, attempting to unpack releases/0.2.0/sequence2.tar.gz
Unpacked successfully: "0.2.0"
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.2.0 is already unpacked, now installing.
Installed Release: 0.2.0
Made release permanent: "0.2.0"
ホットアップグレードの確認
iex([email protected])2> Sequence2.Server.next_number
457
iex([email protected])3> Sequence2.Server.next_number
"The next number is 458"
Erlangは同時に2つのバージョンのモジュールを実行できる
明示的に呼び出されたときに新しいバージョンにスワップされる
downgrade
コマンドでダウングレードできる
goh@goh% ssh localhost ~/elixir/programming_elixir/deploy/bin/sequence2 downgrade 0.1.0
Release 0.1.0 is marked old, switching to it.
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.1.0 is marked old, switching to it.
Installed Release: 0.1.0
Made release permanent: "0.1.0"
iex([email protected])3> Sequence2.Server.next_number
"The next number is 458"
iex([email protected])4> Warning: "/Users/goh/elixir/programming_elixir/deploy/releases/0.1.0/relup" missing (optional)
nil
iex([email protected])5> Sequence2.Server.next_number
459
元に戻す
goh@goh% ssh localhost ~/elixir/programming_elixir/deploy/bin/sequence2 upgrade 0.2.0
Release 0.2.0 is marked old, switching to it.
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.2.0 is marked old, switching to it.
Installed Release: 0.2.0
Made release permanent: "0.2.0"
iex([email protected])5> Sequence2.Server.next_number
459
iex([email protected])6> Sequence2.Server.next_number
"The next number is 460"
increment_number関数で増分を変更できるようにする
- 状態を構造体で保持する
- code_change関数を実装する
データ構造が「1つの整数」から構造体に変わるので、サーバーの再起動では、古い状態が失われる
code_change関数でそれを補う
GenServer.code_chage(old_vsn, state, extra)
- old_vsn: 前のバージョン
@vsn
で指定しているバージョン - state: 現在の状態
- extra: 状態を変えるのに必要な追加パラメーター
{:ok, new_state}
を返す
- new_state: 新しい状態
State
%{current_number: number, stash_pid: pid, delta: delta}
goh@goh% mix release
Compiling 6 files (.ex)
warning: function init/1 required by behaviour GenServer is not implemented (in module Sequence2.Stash).
We will inject a default implementation for now:
def init(args) do
{:ok, args}
end
You can copy the implementation above or define your own that converts the arguments given to GenServer.start_link/3 to the server state.
lib/sequence2/stash.ex:1
Generated sequence2 app
warning: not passing the :switches or :strict option to OptionParser is deprecated
(elixir) lib/option_parser.ex:562: OptionParser.build_config/1
(elixir) lib/option_parser.ex:197: OptionParser.parse/2
lib/mix/tasks/release.ex:434: Mix.Tasks.Release.parse_args/1
lib/mix/tasks/release.ex:68: Mix.Tasks.Release.do_run/1
(mix) lib/mix/task.ex:316: Mix.Task.run_task/3
Building release with MIX_ENV=dev.
warning: variable "description" does not exist and is being expanded to "description()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:8
warning: variable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:9
warning: variable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:10
warning: variable "docs" does not exist and is being expanded to "docs()", please use parentheses to remove the ambiguity or change the variable name
/Users/goh/elixir/sequence2/deps/exrm/mix.exs:11
This is an upgrade, verifying appups exist for updated dependencies..
==> All dependencies have appups ready for release!
==> Generated .appup for sequence2 0.2.0 -> 0.3.0
==> The release for sequence2-0.3.0 is ready!
==> You can boot a console running your release with `$ rel/sequence2/bin/sequence2 console`
アップグレード
goh@goh% ssh localhost ~/elixir/programming_elixir/deploy/bin/sequence2 upgrade 0.3.0
Release 0.3.0 not found, attempting to unpack releases/0.3.0/sequence2.tar.gz
Unpacked successfully: "0.3.0"
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.3.0 is already unpacked, now installing.
Installed Release: 0.3.0
Made release permanent: "0.3.0"
起動中のコンソール
iex([email protected])1> Sequence2.Server.next_number
"The next number is 456"
iex([email protected])2> Sequence2.Server.next_number
"The next number is 457"
iex([email protected])3>
23:36:58.234 [info] Changing code from 0 to 1
23:36:58.234 [info] {458, #PID<0.666.0>}
23:36:58.235 [info] %Sequence2.Server.State{current_number: 458, delta: 1, stash_pid: #PID<0.666.0>}
Sequence2.Server.next_number
"The next number is 458"
iex([email protected])4> Sequence2.Server.increment_number 10
:ok
iex([email protected])5> Sequence2.Server.next_number
"The next number is 469"
iex([email protected])6> Sequence2.Server.next_number
"The next number is 479"