この記事は、Elixir Advent Calendar 2024 シリーズ3 の21日目です
昨日は、@RyoWakabayashi さんで 「Advent of code 2015 Day 10 Part 1 & Part 2 を Livebook で楽しむ」 でした
piacere です、ご覧いただいてありがとございます
前回 は、クラウドを使わずにリアルタイム性を担保するElixirならではの分散コンピューティングの基礎を示しました
今回は、その基礎を土台にモダンなクラスタリング機能群を備える libcluster というライブラリを使って、Elixirにおける「クラスタリング」に入門していきます
なお今回の内容は、libcluster無しでも実現できますが、次回以降に必要なオートスケールやダウン時自動復帰も簡単に実現したいため、今回からlibclusterを使っていきます
本シリーズの全体像は、下記になります
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を作成し、分散接続可能な起動をします
--sup
を付けると、libclusterを使うために必要なスーパーバイザー設定が標準で入ります
mkdir apps
cd apps
mix new a --sup
cd a
次に、2台目のマシンでもPJを作成します
mkdir apps
cd apps
mix new b
cd b
ここで、1台目の方に、libclusterのインストールを行います
defmodule A.MixProject do
use Mix.Project
…
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
+ {:libcluster, "~> 3.4"}
]
end
…
「EPMD」というElixirノードの位置共有/解決を行うメカニズムをクラスター化の戦略として使います
このEPMDは、前回行った Node.connect
や Node.list
を支えるメカニズムでもあります
defmodule A.Application do
# See https://hexdocs.pm/elixir/Application.html
…
def start(_type, _args) do
+ topologies = [
+ clusters1: [
+ strategy: Cluster.Strategy.Epmd,
+ config: [hosts: [:"node1@172.24.176.1", :"node2@172.24.176.2"]]
+ ]
+ ]
children = [
# Starts a worker by calling: A.Worker.start_link(arg)
# {A.Worker, arg}
+ {Cluster.Supervisor, [topologies, [name: A.ClusterSupervisor]]}
]
…
これで1台目のマシンが、2台目のマシンを自動認識/接続し、クラスターを構成してくれるようになりました
なお、下記のように config.ex
で設定することも可能です
では、クラスター化をするので、まず2台目のマシンを起動します
iex --name node2@172.24.176.2 --cookie edge-servers -S mix
その後、libclusterが動く1台目のマシンを起動します
mix deps.get
iex --name node1@172.24.176.1 --cookie edge-servers -S mix
その際、下記のようなメッセージが出れば、クラスター化に成功しています
[info] [libcluster:clusters1] connected to :"node1@172.24.176.1
ノードを見ると、自動接続していることが確認できます
iex> Node.list
[:"node1@172.24.176.1"]
なお、2台目のiexが起動していなかったり、ポートが閉じていたりすると、warningが出て、Node.listしても空のリストのままです
[warning] [libcluster:clusters1] unable to connect to :"node1@172.24.176.1"
3台以上のノードも、下記のように追記することで追加していけます
defmodule A.Application do
# See https://hexdocs.pm/elixir/Application.html
…
def start(_type, _args) do
topologies = [
clusters1: [
strategy: Cluster.Strategy.Epmd,
+ config: [hosts: [:"node1@172.24.176.1", :"node2@172.24.176.2", :"node3@172.24.176.3"]]
…
3台以上のノードがあると、下記のようになります
iex> Node.list
[:"node2@172.24.176.2", :"node3@172.24.176.3"]
クラスターのノード間アクセスの確認
さて、クラスター内の別ノードにアクセスしてみましょう
2台目のマシンのiexのプロセスIDを取得します
iex> self
#PID<0.138.0>
1台目のマシンから、この2台目のマシンのiexプロセス情報を取得できれば、クラスター化された別ノードへのアクセスが可能であることになります
iex> :erlang.list_to_pid('<0.138.0>') |> Process.info
[
registered_name: :ssl_connection_sup,
current_function: {:gen_server, :loop, 7},
initial_call: {:proc_lib, :init_p, 5},
status: :waiting,
message_queue_len: 0,
links: [#PID<0.139.0>, #PID<0.146.0>, #PID<0.133.0>],
dictionary: [
"$initial_call": {:supervisor, :ssl_connection_sup, 1},
"$ancestors": [:ssl_sup, #PID<0.129.0>]
],
…
1台目のマシン上で2台目のマシンのiexプロセス情報が取得できました
ここまでで何が可能となったのか?
複数Elixirノードを1つのクラスターとして扱えるようになったことで、下記のようなことが可能となっています
- 複数ノードでクラスターを組める
- 前回実施した
nl()
のでノード複製ができる
これらにより、クラウドを使わずともクラスタリングが実現できるので、複数ノードで構成されるエッジサーバを簡単に実現できます
ただし、デフォルトのEPMD戦略だけでは、下記のようなモダンなクラスタリング制御は残念ながら出来ません
- クラスターをオートスケールできない
- ノードダウン後のアップ時にクラスターに自動復帰できない
これは、libclusterの限界では無く、あくまでEPMD戦略の限界なため、他の戦略を使う以降のコラムで解決していきます
終わりに
Elixirのクラスタリングが、思った以上に簡単に構築できることが共有できたら幸いです
一方で、現段階では実用的なクラスタリングのようにも見えないため、次回はそこを解決すべく、モダンなクラスタリング制御であるオートスケール/ダウン後自動復帰を実現 してきます
p.s.このコラムが、面白かったり、役に立ったら…
明日は、 @mokichi さんで 「Stripeで従量課金を実現する」 です