リリース

この記事は英語の原文を日本語に翻訳したものです。原文: https://adoptingerlang.org/docs/production/releases/

翻訳元: adoptingerlang/adoptingerlang 2025-12-18(コミット 899008f

OTPを俯瞰するでは、アプリケーションがどのようにリリースへと組み合わされるかを大まかに見ました。そして開発の各章では、OTPアプリケーションを実際に作りテストしてきました。ここまでで、Erlangのシステムが他の多くの言語と違うセットアップになっていること、たとえば起動時に呼び出されるmain()関数のような単一のエントリポイントを持たないことを説明してきました。とはいえ、最終的にあなたのコードを動かすErlangノードを起動するという行為は他の言語やランタイムとさほど変わらず、Dockerコンテナとして他のコンテナと同じように動かすことができ、コンテナを起動するだけで動き出す点では特に変わったものには見えなくなります。しかし、使いやすいシェルスクリプトの付いたリリース一式や、すぐ動かせるDockerイメージができあがるまでの道のりは、最初は戸惑うことが多く、Erlangは自分たちの環境で動かすには違いすぎる、あるいは難しすぎるという印象を与えてしまいがちです。

この章ではリリースがどのように構築されるかについて低レベルの詳細にも触れますが、主な目的はservice_discoveryプロジェクトを使って、rebar3でローカル開発向けのリリースを構築し、続いて本番向けのリリースを構築し、最後にrebar3が提供するツールでリリースを設定・操作する方法を示すことです。並行して動くアプリケーション群を起動するという仕組みがもたらす柔軟性は、運用を秘教的にする代償を伴わなくてもよいのだということを示していきます。

泥臭い部分

オペレーティングシステムとの対比に戻ると、Erlangのリリースを起動することは、OSのブートシーケンスに続いてinitシステムが各種サービスを起動する流れとよく似ています。Erlangノードはブートファイルに書かれた命令を実行することで起動します。この命令はモジュールをロードし、アプリケーションを起動します。Erlangには、必要なアプリケーション一覧からブートスクリプトを生成する関数が用意されており、その一覧は拡張子.relのリリースリソースファイルと、各アプリケーションが持つ.appアプリケーションリソースファイルに定義されます。アプリケーションリソースファイルは、ブートスクリプトがロードすべきモジュールと各アプリケーションの依存関係を定義しており、それによってブートスクリプトは正しい順序でアプリケーションを起動できます。あるリリースで使うアプリケーションだけがブートスクリプトとともにひとまとめにされ、ターゲットへコピー・インストールできる状態になったものをターゲットシステムと呼びます。

Erlang/OTPの初期には、リリースリソースファイルからブートスクリプトを生成する手段はsystoolsとその関数群しかありませんでした。当時、リリースの取り扱いは手作業であり、利用者は自分たちでツールを組み立てるしかありませんでした。その後登場したreltoolはErlang/OTPに同梱されるリリース管理ツールで、リリース作成を容易にすることを狙ったものであり、GUIまで備えていました。ただし、ターゲットシステムの作成とインストールについては、saslアプリケーションに含まれるサンプルモジュールsasl/examples/src/target_system.erl以外にまとまった手段が提供されたことは一度もありません。

リリースの構築は多くの利用者にとって謎めいた難物であり続けました。relxは、リリースの作成と管理を、もう誰も避けて通りたい重荷だとは感じないほど単純にすることを目指して作られました。これは、最小限の設定だけで使い始められることと、生成されるリリースにランタイム管理のためのツールを同梱することによって実現されています。Rebar3が作られたとき、そのリリース構築機能を提供するためにrelxが組み込まれました。

構築とパッケージングに加えて、relxにはリリースを起動し、実行中のリリースと対話するためのシェルスクリプトも付属します。リリースを実行するだけならerl(これ自体、Erlangのインストール先のbin/ディレクトリにあるstart.scriptから構築されたブートスクリプトを実行するものです)だけでも十分ですが、relxが提供するスクリプトは、設定ファイルを指すための適切な引数の設定、稼働中のノードへのリモートコンソールの接続、稼働中のノードでの関数実行など、より多くのことを面倒みてくれます。

以降の節では、service_discoveryプロジェクトを題材にリリース構築を掘り下げていきます。焦点はツールの実運用での使い方であり、リリースの構築・実行にまつわる低レベルの詳細ではありません。

開発用リリースの構築

service_discoveryでまず見るべきは、rebar.configrelxセクションです。

{relx, [{release, {service_discovery, {git, long}},
         [service_discovery_postgres,
          service_discovery,
          service_discovery_http,
          service_discovery_grpc,
          recon]},

        {sys_config, "./config/dev_sys.config"},
        {vm_args, "./config/dev_vm.args"},

        {dev_mode, true},
        {include_erts, false},

        {extended_start_script, true},

        {overlay, [{copy, "apps/service_discovery_postgres/priv/migrations/*", "sql/"}]}]}.

relx設定リストのreleaseタプルは、リリースの名前・バージョン・そのリリースに含めるアプリケーションを定義します。ここでのバージョンは{git, long}であり、これは現在のコミットのgit shaのフルレファレンスをリリースのバージョンとして使うようrelxに指示しています。リリースが起動するアプリケーションには、Postgresストレージバックエンド、サービスとDNS設定への主要なインターフェースとなるアプリケーション、HTTPとgRPCのフロントエンド、本番ノードの調査に便利なツールであるreconが含まれます。

ここでアプリケーションを並べる順序は重要です。ブートスクリプトを構築する際には、各アプリケーションの依存関係に基づいた安定ソートが行われて起動順が決まります。しかし、依存関係の順序だけでは決定しきれない場合は、リスト内で定義された順序が保たれます。service_discovery_postgresservice_discovery_httpservice_discovery_grpcはいずれもservice_discoveryに依存しているため、service_discoveryが最初に起動します。次に起動するのはservice_discovery_postgresで、これはリストの中で先に書かれているためです。HTTPとgRPCのサービスが使えるようになる前にストレージバックエンドを利用可能にしておく必要があるため、この順序は重要です。

rebar3 releaseは、rebar.configrelxセクションとRebar3のプロジェクト構造にもとづいた設定でrelxを実行し、relxがリリース構築に必要なアプリケーションを見つけられるようにします。

$ rebar3 release
===> Verifying dependencies...
===> Compiling service_discovery_storage
===> Compiling service_discovery
===> Compiling service_discovery_http
===> Compiling service_discovery_grpc
===> Compiling service_discovery_postgres
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /app/src/_build/default/lib
          /app/src/apps
          /root/.cache/erls/otps/OTP-22.1/dist/lib/erlang/lib
          /app/src/_build/default/rel
===> Resolved service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70
===> Dev mode enabled, release will be symlinked
===> release successfully created!

出力の中で最初に現れるrelxのステップは「Resolving OTP Applications」であり、続いてそれが検索するディレクトリの一覧が表示されます。relxは、リリース設定にある各アプリケーション(この場合service_discovery_postgresservice_discoveryservice_discovery_httpservice_discovery_grpcrecon)についてビルド済みアプリケーションのディレクトリを見つけ、さらにそのアプリケーションの.appファイルに記載された各アプリケーションについても同じことを行います。

relxの設定リストのアプリケーション一覧にsaslが含まれていない点に注意してください。Rebar3のテンプレートでは標準でこの一覧に含まれています。これは、一部のリリース操作にsaslが必要になるためです。saslアプリケーションにはリリースのアップグレードとダウングレードの機能を提供するrelease_handlerが含まれます。ここではライブアップグレードではなく置き換えを前提としたコンテナの構築に焦点を当てているため、saslを含める必要はありません。

このリリースは{dev_mode, true}で構築されているため、リリースのlibディレクトリには各アプリケーションをコピーする代わりにシンボリックリンクが作られます。

$ ls -l _build/default/rel/service_discovery/lib
lrwxrwxrwx ... service_discovery-c9e1c80 -> .../_build/default/lib/service_discovery

これはランタイム設定ファイルのdev_sys.configdev_vm.argsについても同様です。

$ ls -l _build/default/rel/service_discovery/releases/c9e1c805d57a78d9eb18af1124962960abe38e70
lrwxrwxrwx [...] sys.config -> [...]/config/dev_sys.config
lrwxrwxrwx [...] vm.args -> [...]/config/dev_vm.args

これにより、ローカルでのテスト用にリリースを実行する際のフィードバックループが速くなります。単にリリースを停止・再起動するだけでbeamファイルや設定への変更が反映され、rebar3 releaseを再実行する必要はありません。

Windowsではrelxdev_modeは必ずしも機能せず、代わりにコピーへフォールバックします。

開発用リリースのファイルシステムツリー全体は次のようになります。

_build/default/rel/service_discovery/
├── bin/
│   ├── install_upgrade.escript
│   ├── nodetool
│   ├── no_dot_erlang.boot
│   ├── service_discovery
│   ├── service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70
│   └── start_clean.boot
├── lib/
│   ├── acceptor_pool-1.0.0 -> /app/src/_build/default/lib/acceptor_pool
│   ├── base32-0.1.0 -> /app/src/_build/default/lib/base32
│   ├── chatterbox-0.9.1 -> /app/src/_build/default/lib/chatterbox
│   ├── dns-0.1.0 -> /app/src/_build/default/lib/dns
│   ├── elli-3.2.0 -> /app/src/_build/default/lib/elli
│   ├── erldns-1.0.0 -> /app/src/_build/default/lib/erldns
│   ├── gproc-0.8.0 -> /app/src/_build/default/lib/gproc
│   ├── grpcbox-0.11.0 -> /app/src/_build/default/lib/grpcbox
│   ├── hpack-0.2.3 -> /app/src/_build/default/lib/hpack
│   ├── iso8601-1.3.1 -> /app/src/_build/default/lib/iso8601
│   ├── jsx-2.10.0 -> /app/src/_build/default/lib/jsx
│   ├── recon-2.4.0 -> /app/src/_build/default/lib/recon
│   ├── service_discovery-c9e1c80 -> /app/src/_build/default/lib/service_discovery
│   ├── service_discovery_grpc-c9e1c80 -> /app/src/_build/default/lib/service_discovery_grpc
│   ├── service_discovery_http-c9e1c80 -> /app/src/_build/default/lib/service_discovery_http
│   └── service_discovery_storage-c9e1c80 -> /app/src/_build/default/lib/service_discovery_storage
├── releases/
│   ├── c9e1c805d57a78d9eb18af1124962960abe38e70
│   │   ├── no_dot_erlang.boot
│   │   ├── service_discovery.boot
│   │   ├── service_discovery.rel
│   │   ├── service_discovery.script
│   │   ├── start_clean.boot
│   │   ├── sys.config.src -> /app/src/config/sys.config.src
│   │   └── vm.args.src -> /app/src/config/vm.args.src
│   ├── RELEASES
│   └── start_erl.data
└── sql
    ├── V1__Create_services_endpoints_table.sql
    └── V2__Add_updated_at_trigger.sql

最後のsqlディレクトリは、overlay({copy, "apps/service_discovery_postgres/priv/migrations/*", "sql/"})によって作られます。overlayは、アプリケーションやブートファイルという通常のリリース構造の外側にあるファイルをrelxに伝える仕組みで、ディレクトリの作成、mustacheによる簡易的なテンプレート処理、ファイルのコピーをサポートします。ここではservice_discovery_postgresアプリケーションのマイグレーションをリリースのトップレベルディレクトリsqlにコピーするだけでよいのです。これらのマイグレーションはリリースに含まれるアプリケーションのprivにあるため元々含まれてはいるのですが、後述するように同時に有効なリリースは1つだけにする想定なので、既知のトップレベルディレクトリにまとめておくことで後で見るマイグレーションスクリプトを単純化できます。

OTPのリリース構造は同じリリースの複数バージョンを持てるようになっています。それらは同じlibディレクトリを共有しつつ、アプリケーションごとに異なるバージョン、さらには異なるertsを持つこともできます。だからこそ、バージョン番号のディレクトリを内包するreleasesディレクトリとRELEASESファイルが存在します。すべてのバージョンのリリースで共有されるディレクトリにSQLファイルを置くのは、こうした環境では問題になりえます。しかし、ここではコンテナの中で他から独立して動く自己完結型のリリースを構築することに焦点を当てているため、常にバージョンは1つだけだと仮定して構いません。

このリリースはPostgresストレージバックエンドを使っているため、リリースを起動する前にデータベースが稼働してアクセス可能になっている必要があります。プロジェクトのトップレベルでDocker Composeを実行すると、データベースが立ち上がりマイグレーションが実行されます。

$ docker-compose up

リリースからErlangシェルを得るために、すべてを起動する必要はありません。Rebar3で構築されたすべてのリリースにはstart_cleanというブートスクリプトが付属しており、これはkernelstdlibアプリケーションだけを起動します。これはconsole_cleanコマンドで実行でき、デバッグ用途で便利です。

開発用リリースを対話的なErlangシェルとして起動するには、consoleコマンドで拡張起動スクリプトを実行します。

$ _build/default/rel/service_discovery/bin/service_discovery console
Erlang/OTP 22 [erts-10.5] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:30] [hipe]

(service_discovery@localhost)1>

service_discoveryが動いている状態で、curlを使ってHTTPインターフェースにアクセスできます。以下のコマンドはサービスservice1を作成し、一覧表示することで作成されたことを確認し、IP127.0.0.3でこのサービスにエンドポイントを登録し、ポート8000に名前付きポートhttpを追加します。

$ curl -v -XPUT http://localhost:3000/service \
    -d '{"name": "service1", "attributes": {"attr-1": "value-1"}}'
$ curl -v -XGET http://localhost:3000/services
[{"attributes":{"attr-1":"value-1"},"name":"service1"}]
$ curl -v -XPUT http://localhost:3000/service/service1/register \
    -d '{"ip": "127.0.0.3", "port": 8000, "port_name": "http", "tags": []}'
$ curl -v -XPUT http://localhost:3000/service/service1/ports \
    -d '{"http": {"protocol": "tcp", "port": 8000}}'

service_discoveryのDNSサーバはポート8053で動いています。digを使うと、service1が正しいIPで登録済みのエンドポイントになっていること、そしてサービス(SRV)のDNSクエリがそのサービスのポートとDNS名を返すことが確認できます。

$ dig -p8053 @127.0.0.1 A service1
;; ANSWER SECTION:
service1.		3600	IN	A	127.0.0.3
$ dig -p8053 @127.0.0.1 SRV _http._tcp.service1.svc.cluster.local
;; ANSWER SECTION:
_http._tcp.service1.svc.cluster.local. 3600 IN SRV 1 1 8000 service1.svc.cluster.local.

本番用リリースの構築

本番にデプロイするリリースの準備には、ローカル開発で使うのとは異なるオプションが必要になります。Rebar3のプロファイルを使うとrelxの設定を上書き・追加できます。このプロファイルは一般にprodという名前にします。

{profiles, [{prod, [{relx, [{sys_config_src, "./config/sys.config.src"},
                            {vm_args_src, "./config/vm.args.src"},
                            {dev_mode, false},
                            {include_erts, true},
                            {include_src, false},
                            {debug_info, strip}]}]
            }]}.

prodプロファイルでは2つの設定値を上書きしています。dev_modefalseに設定され、すべての内容がリリースディレクトリにコピーされるようになります。別のマシンから_buildディレクトリへのシンボリックリンクは使えないし、本番用リリースはプロジェクトの不変なスナップショットであるべきだからです。include_ertsは、リリースが依存するErlangランタイムとErlang/OTPアプリケーションをリリースディレクトリにコピーし、ブートスクリプトがこのコピーされたランタイムを指すよう設定します。

追加された項目は、include_srcfalseに、debug_infostripに設定することと、設定ファイルの_src版を使うことです。本番でリリースを実行する際にソースコードは不要なので、最終的なリリースから容量を削減するためにinclude_srcfalseにします。デバッガやxrefcoverのようなツールが使うデバッグ情報も、debug_infostripに設定してbeamファイルから取り除くことでさらに容量を節約します。リリースの中でこれらのツールが使われることはなく、明示的に含めない限り利用すらできません。

sys_config_srcvm_args_srcは、デフォルトプロファイルにあったsys_configvm_argsのエントリより優先されます。この2つについては、この後の節で詳しく述べます。本番用リリースを構築する際には警告が表示されますが、これは意図的に行っていることなので無視して構いません。

本番プロファイルを有効にして構築すると、成果物はプロファイルディレクトリ_build/prod/に書き出されます。

$ rebar3 as prod release
===> Verifying dependencies...
===> Compiling service_discovery_storage
===> Compiling service_discovery
===> Compiling service_discovery_http
===> Compiling service_discovery_grpc
===> Compiling service_discovery_postgres
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /app/src/_build/prod/lib
          /app/src/apps
          /root/.cache/erls/otps/OTP-22.1/dist/lib/erlang/lib
===> Resolved service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70
===> Both vm_args_src and vm_args are set, vm_args will be ignored
===> Both sys_config_src and sys_config are set, sys_config will be ignored
===> Including Erts from /root/.cache/erls/otps/OTP-22.1/dist/lib/erlang
===> release successfully created!

新しいprodプロファイルのリリースディレクトリのツリーを見ると、次のようになっています。

_build/prod/rel/service_discovery
├── bin
│   ├── install_upgrade.escript
│   ├── nodetool
│   ├── no_dot_erlang.boot
│   ├── service_discovery
│   ├── service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70
│   └── start_clean.boot
├── erts-10.5
│   ├── bin
│   ├── doc
│   ├── include
│   ├── lib
│   └── man
├── lib
│   ├── acceptor_pool-1.0.0
│   ├── asn1-5.0.9
│   ├── base32-0.1.0
│   ├── chatterbox-0.9.1
│   ├── crypto-4.6
│   ├── dns-0.1.0
│   ├── elli-3.2.0
│   ├── erldns-1.0.0
│   ├── gproc-0.8.0
│   ├── grpcbox-0.11.0
│   ├── hpack-0.2.3
│   ├── inets-7.0.8
│   ├── iso8601-1.3.1
│   ├── jsx-2.10.0
│   ├── kernel-6.5
│   ├── mnesia-4.16
│   ├── public_key-1.6.7
│   ├── recon-2.4.0
│   ├── service_discovery-c9e1c80
│   ├── service_discovery_grpc-c9e1c80
│   ├── service_discovery_http-c9e1c80
│   ├── service_discovery_storage-c9e1c80
│   ├── ssl-9.3.1
│   └── stdlib-3.10
├── releases
│   ├── c9e1c805d57a78d9eb18af1124962960abe38e70
│   │   ├── no_dot_erlang.boot
│   │   ├── service_discovery.boot
│   │   ├── service_discovery.rel
│   │   ├── service_discovery.script
│   │   ├── start_clean.boot
│   │   ├── sys.config.src
│   │   └── vm.args.src
│   ├── RELEASES
│   └── start_erl.data
└── sql
    ├── V1__Create_services_endpoints_table.sql
    └── V2__Add_updated_at_trigger.sql

lib以下にシンボリックリンクはなく、stdlib-3.10のようなOTPアプリケーションはコピーとして含まれています。ツリーの最上位にはerts-10.5があり、これにはErlangランタイムbin/beam.smpのほか、リリースの実行・操作に必要なerlexecerlescriptといった実行ファイルが含まれます。

リリースのターゲットシステムを構築するには、prodプロファイルでtarコマンドを実行します。

$ rebar3 as prod tar

これで、任意の互換ホストにコピーして展開・実行できるtarball _build/prod/rel/service_discovery/service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70.tar.gzができあがります。ただし、それを行う前に、リリース起動時にsys.config.srcvm.args.srcをどう設定すればよいかを知っておく必要があります。

ランタイムの設定

service_discoveryプロジェクトには、本番用リリースに含まれるconfig/ディレクトリ配下に2つの設定ファイル、vm.args.srcsys.config.srcがあります。これらのファイルは、実行時に環境変数をもとに埋め込まれるテンプレートとして機能します。リリースの実行には2段階の設定が関わってくるため、ファイルは2つに分かれています。1つ目は、Erlang仮想マシン自体の設定です。これらの値はVMが起動する前に設定されている必要があるため、稼働中のVMがなければ読み込みも解釈もできないsys.configのようなErlangタームファイルの一部にすることはできません。代わりに、VMの引数はerl(リリースの起動に使われるコマンド)に直接渡されます。これを簡単にするため、erlコマンドにはコマンドライン引数をプレーンテキストファイルから読み込める-args_fileという引数があり、このファイルは一般にvm.argsという名前にされます。

2段階目は、リリースを構成するErlangアプリケーションの設定です。これは-config引数を通じてerlに渡されるファイルによって行われます。このファイルは2要素タプルのリストであり、1つ目の要素は環境を設定するアプリケーションの名前、2つ目の要素は環境に設定するキーと値のペアのリストです。

もちろん静的なファイルにはかなりの制約があり、OS環境変数を通じて設定できるようにしたいという要望はよくあることです。環境変数による設定に柔軟性とサポートを提供するため、Rebar3を使った際に生成されるリリース起動スクリプトは、${FOO}という形式の変数を現在の環境にある値で置き換えることができます。これはファイルの拡張子が.srcで終わっている場合に自動的に行われます。

relxの設定ではvm_args_srcsys_config_srcを使ってこれらのファイルを含め、テンプレートであることを伝えます。これは、リリース構築の際にsys.configが正しいErlangタームのリストであることを検証しようとしないようにするために必要です。たとえば{port, ${PORT}}はErlangタームとして正しくありません。

{relx, [...
        {sys_config_src, "config/sys.config.src"},
        {vm_args_src, "config/vm.args.src"},
        ...
       ]}.

DockerとKubernetesの章で、なぜsbwtを設定する必要があるのかを説明しますが、ここではvm.args.srcでどう書くかだけを見ておきます。

+sbwt ${SBWT}

sys.config.srcではloggerのレベルも変数にします。こうすることで、たとえばデプロイ済みのサービスを調査する際にdebuginfoレベルのロギングを有効にして詳細を得られるようになります。

{kernel, [{logger_level, ${LOGGER_LEVEL}}]}

こうなると、リリースを実行する際にはこれらの変数を設定しなければならず、設定しなければリリースは起動に失敗します。

$ DB_HOST=localhost LOGGER_LEVEL=debug SBWT=none \
  _build/prod/rel/service_discovery/bin/service_discovery console
Erlang/OTP 22 [erts-10.5] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:30] [hipe]

(service_discovery@localhost)1>

必要な環境変数を設定せずに実行した際に出るエラーは分かりにくいことがあります。現状、設定ファイルで使われているすべての環境変数が設定されているかどうかを検証し、不足しているものを表示するような仕組みはありません。代わりに、不足している変数は空文字列に置き換えられます。もし変数が"${DB_HOST}"のように文字列の中で使われていれば、アプリケーションは起動はするもののデータベースに接続できなくなります。${LOGGER_LEVEL}のように、変数が欠けることでパースできないsys.configができてしまう場合には、構文エラーになります。

$ DB_HOST=localhost SBWT=none \
  _build/prod/rel/service_discovery/bin/service_discovery console
{"could not start kernel pid",application_controller,"error in config file \"/app/src/_build/prod/rel/service_discovery/releases/71e109d8f34ef5e5ccfcd666e0d9e544836044f1/sys.config\" (48): syntax error before: ','"}
could not start kernel pid (application_controller) (error in config file "/home/tristan/Devel/service_discovery/_build/prod/rel/service_discovery/releases/71e109d8f34ef5e5ccfcd666e0d9e544836044f1/sys

Crash dump is being written to: erl_crash.dump...done

vm.argsの値が欠けている場合には、フラグに渡された引数についてのエラーになることが多いです。+sbwt ${SBWT}の場合、次の行を+sbwtの引数として使おうとしてしまい、この例では+C multi_time_warp+Cの値として不正だというブートエラーになります。

$ DB_HOST=localhost LOGGER_LEVEL=debug \
  _build/prod/rel/service_discovery/bin/service_discovery console
bad scheduler busy wait threshold: +C
Usage: service_discovery [flags] [ -- [init_args] ]
The flags are:
...

変数が不足した際の失敗すべてがこれほど単純とは限らず、非常に奇妙なクラッシュにつながることもあれば、起動時にはクラッシュせず、その値に依存するアプリケーション内で失敗することもあります。この点は今後のRebar3のバージョンで、起動前に不足している環境変数の一覧を出して失敗させるチェックを入れることで改善したいと考えています。しかし今のところ、奇妙な挙動や理解できないクラッシュに遭遇したら、まず環境変数を確認するのがよいでしょう。

この先に向けて

次の章で行うことのイメージをつかむために、_build/prod/rel/service_discovery/service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70.tar.gz/tmp/service_discoveryにコピーし、展開してノードを起動してみましょう。

$ export LOGGER_LEVEL=debug
$ export SBWT=none
$ export DB_HOST=localhost
$ mkdir /tmp/service_discovery
$ cp _build/prod/rel/service_discovery/service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70.tar.gz /tmp/service_discovery
$ cd /tmp/service_discovery
$ tar -xvf service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70.tar.gz && rm service_discovery-c9e1c805d57a78d9eb18af1124962960abe38e70.tar.gz
...
$ ls
bin  erts-10.5  lib  releases
$ bin/service_discovery console
...
(service_discovery@localhost)1>

consoleで実行すると対話的なErlangシェルが得られます。対話シェルなしでリリースを実行するにはforegroundを使います。これは、次の章でDockerコンテナの中でリリースを実行する際のやり方でもあります。リリースをforegroundまたはconsoleで実行した状態のまま、別のターミナルを開いて同じスクリプトに引数remote_consoleを渡してみましょう。

$ export LOGGER_LEVEL=debug
$ export SBWT=none
$ export DB_HOST=localhost
$ bin/service_discovery remote_console
...
(service_discovery@localhost)1>

remote_consoleerl-remshフラグを使って対話シェルを稼働中のリリースに接続します。このコマンドがどのErlangノードに接続すべきかは、そのノードが最初に起動されたときと同じvm.args由来の設定、すなわちノード名とcookieの設定にもとづいて判断されます。つまり、ノード起動時にvm.argsを埋めるために使った環境変数は、リモートコンソールを接続する際にも同じ値を設定しておく必要があります。