分散Erlangシステムを楽しむには複数のPCがあるといいのですが、それだと準備が大変で敷居を高く感じるかもしれません。
Dockerを使えば一つのPC上に複数の仮想ホストを簡単に立ち上げられます。それらを別々のPCに見立てれば、気軽に遊べるのではないでしょうか。
やりたいこと
ノードとは
分散Erlangのドキュメントによると
A distributed Erlang system consists of a number of Erlang runtime systems communicating with each other. Each such runtime system is called a node.
- 分散Erlangシステムは複数のErlangランタイムシステムがお互いに通信することで構成される。
- 分散Erlangシステム上の各ランタイムシステムはノードと呼ばれる。
論よりRun
<
Dockerでノードを起動する方法
Dockerで仮想マシンを起動
まずはhexpm/elixirのDockerイメージを使用してElixirが使える仮想Linuxマシンを立ち上げます。
docker run --rm -it hexpm/elixir:1.14.2-erlang-25.1.2-alpine-3.16.2 /bin/sh
仮想マシンのIPアドレスを探す
他の仮想マシンと接続するにはIPアドレスが必要になります。
LinuxにはいくつかIPアドレスを探す方法があるようです。
hostname
、ifconfig
、ip
が挙げられます。
hostname -i
ifconfig eth0
ip -o address
ip addr show eth0
cat /etc/hosts
余談ですが、ついでにLinuxコマンドで文字列を加工する技を学ぶよい機会でした。
ifconfig eth0 | awk '/inet/ {print $2}' | sed -r 's/addr://g'
ip -o address | awk '/eth0/ {print $4}' | cut -d '/' -f 1
ip addr show eth0 | awk '/inet/ {print $2}' | cut -d '/' -f 1
cat /etc/hosts | grep $(hostname) | cut -f 1
ノードを起動
IExを起動するときに--name
オプションにノードの場所を含む完全修飾名を指定します。IPアドレスはご自身のものに読み替えてください。
iex --name hoge@172.17.0.4
以上の作業を別々のシェルから実行し、別々の仮想マシンを立ち上げ、ノードを起動します。
分散Erlangシステムを構築する
Erlangクッキーを設定
ノードが接続する際に必要になります。あるクラスターに属するすべてのノードが同じクッキーを共有することになります。
Node.set_cookie(:mycookie)
ちなみにiex
コマンドの--cookie
オプションまたは--erl
オプションを用いることにより、Erlangランタイムシステム起動時にクッキーを設定することも可能です。
iex --name hoge@172.17.0.4 --cookie mycookie
iex --name hoge@172.17.0.4 --erl "-setcookie mycookie"
bin/iex
実行ファイルの一番下の行でelixir
コマンドがiex
モードで実行されています。
erl
のオプションはErlangのドキュメントに列挙されています。
Node.ping/1でノードを接続
hoge
ノードのIExから他のノード(fuga
ノードとpiyo
ノード)に接続します。
:pong = Node.ping(:"fuga@172.17.0.6")
:pong = Node.ping(:"piyo@172.17.0.7")
Node.ping/1は、成功した場合は :pong
、失敗した場合は:pang
を返します。IPアドレスはご自身のものに置き換えてください。
Node.list/0で現在接続されているノードのリストが確認できます。この時点で全てのノードが他の全てのノードに接続されているはずです。
後は自由に遊ぶ
別ノードでコードを実行
fuga
ノードのIExにToukon
モジュールを貼り付けて定義します。
defmodule Toukon do
def aisatsu, do: "元氣ですかーーーーッ!"
end
Node.spawn/2を使って、piyo
ノードのIExからfuga
ノードで定義されたToukon
モジュールを実行します。
Node.spawn(
:"fuga@localhost",
fn ->
Toukon.aisatsu() |> IO.puts()
end
)
グループリーダーという概念があるようです。piyo
ノードがNode.spawn
を実行した場合はpiyo
ノードがグループリーダーなので結果はpiyo
ノードの標準出力に印字されます。
Node.spawn/2以外にも別ノードでコードを実行する方法はいくつか挙げられます。
今回つかったDockerコンテナを実際のPCやIoTデバイスに置き換えても同様のことができるはずです。
トラブルシューティング
Node.spawn/2でエラーが出る
これは、ローカルのElixirとリモートのElixirのバージョンが異なるためと思われます。同じバージョンであることをご確認ください。
** (BadFunctionError) function #Function<43.3316493/0 in :erl_eval> is invalid, likely because it points to an old version of the code
:erlang.apply/2
Erlangのドキュメントに分散Erlangの互換性について以下のように記述されています。
Erlang nodes can communicate across at least two preceding and two subsequent releases.
前後の2リリースのノード間の通信の互換性は保証されているようです。
Node.start/3でエラーが出る
本記事では使用していませんが、Node.start/3を使用してエラーが出る場合は@zacky1972さんの記事が参考になります。
また、Livebookの実装から着想が得られるかもしれません。
もっとDockerでElixirを楽しみたい
ローカルのファイルを使ってDockerコンテナの中のElixirに計算させたい
開発PCにあるファイルを使って、Dockerコンテナの中のElixirで計算をしたい場合はDockerボリュームの設定(docker run
の-v
オプション)が必要となります。
@torifukukaiouさんの記事が参考になります。
docker-composeを使ってしっかりElixir学習用環境を構築したい
@RyoWakabayashiさんのサンプルコードが参考になります。
docker-composeを使ってPhoenixのウエブアプリを開発環境を構築したい
@koyo-miyamuraさんがサンプルアプリを公開されています。
ご参考までに