この記事は、Elixir Advent Calendar 2024 シリーズ3 の21日目です
昨日は、@RyoWakabayashi さんで 「KinoLink で Livebook に OGP 対応のリッチなリンクを表示する」 でした
piacere です、ご覧いただいてありがとございます
前回 は、エッジコンピューティングで最も重要なのは「リアルタイム性」であり、それを阻害するのは一般的なWeb/AI開発の「常識」や「当たり前」であるクラウドにある点を言及した後、クラウド無しでも分散コンピューティングを叶えるElixirのイディオムを紹介しました
今回は、クラウドを使わずにリアルタイム性を担保するために、複数台のエッジサーバをElixirノード群で構成する基礎について解説します
本シリーズの全体像は、下記になります
There are 13 Elixir Advent Calendar, making for a hot winter!
Over the past 7 years, Elixir has consistently been a top-ranking language in the programming category of the Advent Calendar, claiming the #1 spot last year. This year, too, Elixir is sure to stay at the top.
We’re feeling the excitement again this year… Please support or subscribe!
https://qiita.com/advent-calendar/2024/elixir
https://qiita.com/advent-calendar/2024/ranking/feedbacks/categories/programming_languages
クラウドを使わずに分散コンピューティングを叶える
上記①のクラウド利用を避けつつ、②の分散コンピューティングを叶えるという「いいとこどり」をElixirは割とカンタンに叶えられます
まず、1台目のマシンでPJを作成し、分散接続可能な起動をします
ここで、--cookie edge-servers
に設定されているのは、同じ分散ノードに属することを示す名前で分散接続可能にしています
mkdir apps
cd apps
mix new a
cd a
iex --name node1@172.24.176.1 --cookie edge-servers -S mix
次に、2台目のマシンでもPJを作成し、やはり分散接続可能な起動をします
mkdir apps
cd apps
mix new b
cd b
iex --name node2@172.24.176.2 --cookie edge-servers -S mix
2台目のマシンから、1台目のマシンに接続し、接続ノードリストを見ると、1台目のマシンが見れます
ただし、ポート番号4369をファイアウォール等で閉じていると使えないので、あらかじめ空けておいてください
iex> Node.connect(:"node1@172.24.176.1")
iex> Node.list
[:"node1@172.24.176.1"]
1台目のマシンからも2台目のマシンが見れます
iex> Node.list
[:"node2@172.24.176.2"]
ノード間のプロセス連携
2台が接続した状態で、プロセス連携をしてみましょう
1台目のマシンから2台目のマシンのiexにメッセージを送ってみたいので、まず2台目のマシンのiexのプロセスIDを取得します
iex> self
#PID<0.138.0>
1台目のマシンから、この2台目のマシンのiexプロセスに、2台目上のモジュールを呼び出すメッセージを送ります
iex> :erlang.list_to_pid('<0.138.0>') |> send(&B.hello/1)
2台目のマシンのiexプロセスで受信し、受信した関数を実行できます
iex> receive do
...> f -> &f
...> end
:world
このように、ノード間を跨いだプロセス連携による分散処理が可能です
名前付きプロセスでノードを意識しない連携
先程は、特定ノード上のプロセスIDをわざわざ調べて連携しましたが、どのノードでどのプロセスが動いているかを意識せず、これと同じことが出来ます
そのためには、あらかじめプロセスに名前を付けて、メッセージを待ち受けます
iex> register(self, :anywhere_iex)
iex> receive do
...> f -> &f
...> end
名前付きプロセスで呼び出せます
iex> :erlang.list_to_pid('<0.138.0>') |> send(&B.hello/1)
ノード間のモジュール複製
2台が接続した状態から、1台目のマシンに定義したコードを、2台目のマシンでも利用可能にしてみます
defmodule A do
def func(count) do
for i <- 1..count, do: i
end
end
まず、1台目のマシン上で、これを実行してみます
iex> A.func(5)
[1, 2, 3, 4, 5]
この段階では、2台目のマシン上にこの定義が無いため、エラーになります
iex> A.func(5)
** (UndefinedFunctionError) function A.func/1 is undefined (module A is not available)
A.func(5)
iex:4: (file)
ここで、1台目のマシンにあるコードを、下記コードで2台目に複製できます
iex> nl(A)
{:ok, [{:"node2@172.24.176.2", :loaded, A}]}
これで、2台目のマシン上でも実行できるようになります
iex> A.func(5)
[1, 2, 3, 4, 5]
3台以上の接続がある場合でも、同様に複製できます
iex> nl(A)
{:ok,
[{:"node2@172.24.176.2", :loaded, A}, {:"node3@172.24.176.3", :loaded, A}]
このようにElixirは、クラウドやコンテナを使うことも無く、分散コンピューティングをいとも容易く叶えます
終わりに
今回は、エッジコンピューティングのリアルタイム性担保のために、最も重要な「クラウド無しでの分散コンピューティング」をElixirのみで叶えました
しかも、サードパーティのライブラリやFWも特に導入しておらず、デフォルト状態のElixirのみで実現できています
この土台を中心に、上記②のマルチマスタかつ冗長化された分散DBの実現や、上記⑥の耐障害性とコンピュートエンジンの冗長化など、エッジコンピューティングに求められる「リアルタイム性」を叶える様々な事柄が、やはりカンタンに実現できます
次回は、上記①の基本的な要素である「クラスタリング」を構築していきます
p.s.このコラムが、面白かったり、役に立ったら…
明日は、 @RyoWakabayashi さんで 「Advent of code 2015 Day 11 Part 1 & Part 2 を Livebook で楽しむ」 です