32
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 5

Dockerを使ってElixirの分散システムを気軽に楽しむ

Last updated at Posted at 2022-11-26

分散Erlangシステムを楽しむには複数のPCがあるといいのですが、それだと準備が大変で敷居を高く感じるかもしれません。

Dockerを使えば一つのPC上に複数の仮想ホストを簡単に立ち上げられます。それらを別々のPCに見立てれば、気軽に遊べるのではないでしょうか。

やりたいこと

  • Dockerで仮想Linuxマシンを三つ起動してそれらを別々のPCと見立てる
  • それぞれの仮想Linuxマシンでノードを起動する
  • すべてのノードを接続
  • 後は自由に遊ぶ

ノードとは

分散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.

論よりRun
:speaking_head: < :computer:

Dockerでノードを起動する方法

Dockerで仮想マシンを起動

まずはhexpm/elixirDockerイメージを使用してElixirが使える仮想Linuxマシンを立ち上げます。

CMD
docker run --rm -it hexpm/elixir:1.14.2-erlang-25.1.2-alpine-3.16.2 /bin/sh

仮想マシンのIPアドレスを探す

他の仮想マシンと接続するにはIPアドレスが必要になります。

LinuxにはいくつかIPアドレスを探す方法があるようです。

hostnameifconfigipが挙げられます。

CMD
hostname -i
CMD
ifconfig eth0
CMD
ip -o address
CMD
ip addr show eth0
CMD
cat /etc/hosts

余談ですが、ついでにLinuxコマンドで文字列を加工する技を学ぶよい機会でした。

CMD
ifconfig eth0 | awk '/inet/ {print $2}' | sed -r 's/addr://g'
CMD
ip -o address | awk '/eth0/ {print $4}' | cut -d '/' -f 1
CMD
ip addr show eth0 | awk '/inet/ {print $2}' | cut -d '/' -f 1
CMD
cat /etc/hosts | grep $(hostname) | cut -f 1

ノードを起動

IExを起動するときに--nameオプションにノードの場所を含む完全修飾名を指定します。IPアドレスはご自身のものに読み替えてください。

CMD
iex --name hoge@172.17.0.4

以上の作業を別々のシェルから実行し、別々の仮想マシンを立ち上げ、ノードを起動します。

Dockerでノードを起動.png

分散Erlangシステムを構築する

Erlangクッキーを設定

ノードが接続する際に必要になります。あるクラスターに属するすべてのノードが同じクッキーを共有することになります。

各ノードのIEx
Node.set_cookie(:mycookie)

ちなみにiexコマンドの--cookieオプションまたは--erlオプションを用いることにより、Erlangランタイムシステム起動時にクッキーを設定することも可能です。

CMD
iex --name hoge@172.17.0.4 --cookie mycookie

CMD
iex --name hoge@172.17.0.4 --erl "-setcookie mycookie"

bin/iex実行ファイルの一番下の行でelixirコマンドがiexモードで実行されています。

erlのオプションはErlangのドキュメントに列挙されています。

Node.ping/1でノードを接続

hogeノードのIExから他のノード(fugaノードとpiyoノード)に接続します。

hogeノードのIEx
: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モジュールを貼り付けて定義します。

fugaノードのIExに貼り付け
defmodule Toukon do
  def aisatsu, do: "元氣ですかーーーーッ!"
end

Node.spawn/2を使って、piyoノードのIExからfugaノードで定義されたToukonモジュールを実行します。

piyoノードのIEx
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さんがサンプルアプリを公開されています。

ご参考までに

32
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?