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) がノード名 ↔ ポートの解決を担う。
Cookie による認証
全ノードが同一の 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.podIPとphase == "Running"を収集する(Pod に一覧取得権限の ServiceAccount を付与)。
- 例: Kubernetes の Pod 内から
Elixir では libcluster がこの発見戦略(gossip / k8s / DNS など)をプラガブルに提供する。
OTP 上の分散プリミティブ
- ノードをまたいだ
GenServer.call、グローバルプロセス登録 (:global)、pgによるプロセスグループ。 - RaftFleet (Raft 実装)のように、クラスタ全体で合意を取る分散プロセス群。
RaftFleet.active_nodesで参加ノードを確認できる。
環境抽象化 (EAL パターン)
Antikythera は EAL (Environment Abstraction Layer) でクラスタ構成を ClusterConfiguration.Behaviour という振る舞い(インターフェース)に抽象化する。デフォルト実装はスタンドアロン(Node.self() のみ返す)で、実行環境ごとに running_hosts/0 を差し替えることでローカル/compose/k8s に対応する。「発見ロジックをインターフェースで切り離す」という分散システムの定石。
関連
- antikythera-k8s — k8s 上での具体的なクラスタ構築事例
- kubernetes / homelab-kubernetes
- distributed-consensus-raft / microservices-architecture