Skip to content

Latest commit

 

History

History
607 lines (483 loc) · 24.1 KB

18.6.md

File metadata and controls

607 lines (483 loc) · 24.1 KB

18.6 EXRM - Elixirのリリースマネージャ

  • exrm
    リリースの作業を簡単にしてくれるElixirのパッケージ

始める前に

Elixirにはバージョンが2つあり、それぞれ独立している

  • コード

  • データ

  • コードのバージョン
    mix.exsproject

  • データのバージョン
    OTPアプリケーションの状態はサーバーによって維持されている
    各サーバーの状態は独立
    =>
    データは各サーバーのモジュール内でバージョンをつける
    @vsn

初めてのリリース

  1. プロジェクトの依存関係に exrm を追加 mix.exs のdeps関数に追加
  2. exrmのインストール
    $ mix do deps.get, deps.compile
  3. リリース
    $ 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

デプロイと実行

  1. アプリケーション名.tar.gz を配備する
  2. 展開する
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

2回目のリリース

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`
  1. リリースディレクトリの作成: ~/elixir/programming_elixir/deploy/releases/0.0.2
  2. sequence2.tar.gz を配備する
  3. ホットアップグレードする
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"