BEAM/Erlang 分散クラスタリング

Erlang VM (BEAM) が標準で備える分散機能。複数のノード(ノード=1つの BEAM インスタンス)を透過的に接続し、プロセス間メッセージをノード境界を越えて送れる。Elixir/OTP もこの仕組みをそのまま利用する。_moc-web-infra

ノードと接続の基礎

  • ノード名name@host 形式。Node.self() で自分の名前を取得。
    • iex --name antikythera@host.local --cookie <secret> のように起動する。
    • FQDN 必須: ドットを含むホスト名(*.local 等)を使わないと「System running to use fully qualified hostnames」「Hostname ... is illegal」で起動に失敗する。コンテナのデフォルト hostname はドット無しなので明示設定が要る。
  • 接続: Node.ping(:"node@other.local"):pong を返せば疎通。Node.list() で接続済みノード一覧。接続は推移的(A-B, B-C をつなぐと A-C も自動接続)で、フルメッシュを形成する。
  • epmd (Erlang Port Mapper Daemon) がノード名 ↔ ポートの解決を担う。

全ノードが同一の magic cookie を共有していないと接続を拒否する。共有秘密による素朴な認証で、信頼ネットワーク前提の設計。RELEASE_COOKIE 環境変数や --cookie で指定する。

クラスタ形成の課題: ノード発見

BEAM 自体はノード名さえ分かれば繋げるが、「どのノードが存在するか」を知る仕組みは持たない。実運用では各環境に応じた ノード発見 (node discovery) が必要:

  • 静的: /etc/hosts や設定ファイルにホスト一覧を固定で持つ(docker-compose では extra_hosts + ipv4_address で固定)。
  • 動的: クラウド/オーケストレータの API を叩いて生存ノードを列挙する。
    • 例: Kubernetes の Pod 内から kubectl get pods -o json を叩き、ラベルで絞って status.podIPphase == "Running" を収集する(Pod に一覧取得権限の ServiceAccount を付与)。

Elixir では libcluster がこの発見戦略(gossip / k8s / DNS など)をプラガブルに提供する。

OTP 上の分散プリミティブ

  • ノードをまたいだ GenServer.call、グローバルプロセス登録 (:global)、pg によるプロセスグループ。
  • RaftFleet (Raft 実装)のように、クラスタ全体で合意を取る分散プロセス群。RaftFleet.active_nodes で参加ノードを確認できる。

環境抽象化 (EAL パターン)

AntikytheraEAL (Environment Abstraction Layer) でクラスタ構成を ClusterConfiguration.Behaviour という振る舞い(インターフェース)に抽象化する。デフォルト実装はスタンドアロン(Node.self() のみ返す)で、実行環境ごとに running_hosts/0 を差し替えることでローカル/compose/k8s に対応する。「発見ロジックをインターフェースで切り離す」という分散システムの定石。

関連