ノードから見る分散システムの概要
現実のシステムは、大規模になるほど一部のノードに障害が発生する確率は高くなり、システム全体を分散システムとして扱う必要が出てきます。分散システムは古くからコンピュータ・サイエンスの世界で研究されている分野です。分散システムをどう定義するかは研究対象によって異なりますが、大まかには各ノードが自律的に動作し、互いに通信して構成するネットワーク型のシステムといえます。
大規模システムでは一つひとつノードを手動でクラスターに参加・除去させることは現実的ではありません。可能な限りクラスターの運用を自動化し、ノードが自律的にクラスターへ参加・除去されるためには、分散システムの理論を適用することが必須になります。
分散システムの根本的な問題のひとつは、以下の3つが区別できないことです。
- ノードの動作遅延(例: CPUを長時間占有する処理、ガベージ・コレクションのポーズなど)
- ノードのダウン(例: サーバー停止など)
- ネットワーク分断(例: ルーター障害など)
区別できないにもかかわらず、これらは対処法が異なります。
Akkaクラスターではそれぞれ
- ノードの動作遅延の場合 → ノードが正常動作するまで待つ
- ノードのダウンの場合 → 当該ノードをクラスターから取り除き、再度クラスターに参加させるには、参加プロセスを最初から踏ませる
- ネットワーク分断の場合 → 一定時間待ち、分断が修復されなければ分断された片方のクラスターで処理を続ける
という対処になります。ダウンしたと思ったノードから相当遅れてレスポンスが返って来ることもあるので、正しく対応するのは非常に困難です。
分散システムの研究は、こうしたネットワーク型のシステムの厄介な事象が起こることを前提として、システム全体で動作を保つためのアルゴリズムや、そもそもシステム全体で保つべき動作とはなんなのか、といったことを研究対象としています。
文献は膨大で難解なものも多く、私も部分的にしか理解していないので、ここではAkkaクラスターの仕組みに関わる部分の中でも概要の解説にとどめます。しかし概要を理解すれば、Akkaクラスターのソースコードを読み解く際にも大きな手掛かりになるでしょう。
Akkaクラスターのメンバーシップ
先にクラスターシャーディングの話をしましたが、それが依存するクラスターのモジュールのコアな部分がメンバーシップです。公式ドキュメントに沿ってメンバーシップに関する用語を定義しましょう。ここまでに繰り返し使ってきた用語もありますが、あらためてAkkaクラスターの文脈での定義を確認します。
ノード: クラスタを構成する論理的なメンバー。物理ノード1台の上に複数のノードが存在できます。ノードは hostname:port:uid の3つの値の組で定義されます。
クラスター: 複数のノードを束ねた一群で、クラスター・メンバーシップサービスによって構成されます。
リーダー: クラスター内で、クラスターの収束とメンバーシップの状態遷移を管理する役割を持つ、単一のノード。
シードノード: 新しいノードがクラスターに参加するときの、接点となるノード。参加する側のノードは、全てのシードノードにメッセージを送り、最初に応答したシードノードにJoinコマンド(後述)を送ります。

新しくクラスターに参加する1つのノードに着目すると、その状態は以下のように推移します。これはAkka公式ドキュメントから抜粋した図です。Joinコマンドを送った後、Up状態になれば正常に動作しています。Downや自発的なLeaveなどによってもノードの状態は変化します。

ここで重要なのは、各ノードはクラスターに属する他のノードの観測状態を持っていて、遅延や障害によって各ノードが観測するクラスター全体の状態は異なる可能性がある、ということです。クラスターに属する各ノードが観測する状態は、時間とともに収束して同じ観測状態になるよう設計されていますが、ネットワーク分断などが起こると、それが続く限り収束しません。
Akkaクラスターを使う上での注意点
Akkaクラスターは分散システムの実装なので、内部的にはどうしても複雑なシステムになっています。よって、実際に運用するときに注意すべきことは多岐にわたります。この記事で紹介するのはクラスターの概要のみにとどめているため、注意点も以下の2点のみにとどめます。いずれも重要な内容です。
まず1つめは、本番環境では必ずスプリット・ブレイン・リゾルバを利用するということです。スプリット・ブレイン・リゾルバは分散システムの解説で触れたネットワーク分断に対処するためのモジュールで、これを本番環境で利用しないのは明確なアンチパターンだと宣言されています。幸運なことに、Lightbend社が、それまで有償提供していたスプリット・ブレイン・リゾルバをオープンソース化しました。Lightbend社のものをはじめ、いくつかの実装が提供されているので、必ず本番環境では導入してください。
もう1点はAkkaをマイクロサービスに用いる場合についてです。Akkaの公式ドキュメント内で、マイクロサービス間の通信にアクターのメッセージングを使わないよう注意喚起しています。その理由は密結合を生み出してしまうからです。マイクロサービスをまたいでバイナリ依存を持ち、それぞれを自由なタイミングでデプロイできなくなるので、マイクロサービスの最大のメリットを失ってしまいます。これは「分散モノリス」と呼ばれるアンチ・パターンなので陥らないように気をつけてください。
学習と運用のコスト
Akkaクラスターは学習コストが高く、運用も非常に難しいので本番環境への導入は慎重に判断してください。開発チームの1人だけではなく、複数人がAkkaに精通している必要があり、アプリケーションの設計も、根本からAkkaに合わせて変えなくてはなりません。
本番環境導入後に予想される、運用上のトラブルに関しては、事前にプロトタイプのシステムを作ってテストするなど、慎重に検証を重ねてください。ノードのダウンやネットワーク分断、デプロイの失敗など、対処法を事前に確認するとともに、間違った設計によってトラブルを悪化させないかを確認してください。
Akkaクラスターは分散システムの理論と実践に触れるきっかけとなる、技術者にとっては挑戦しがいのある対象です。この連載でAkkaに興味を持った方は、ぜひ学習と検証を続けてみてください。
Akkaはすでに10年の歴史があり、多数の資料がオンライン上で利用できます。Akkaの開発をサポートしているLightbend社も、ビデオやブログ、ホワイトペーパーなどで事例紹介や技術解説に力を入れています。
Akkaクラスターで学んだ分散システムの知識は、他の分散システム、例えばKafkaやCassandraなどを学ぶときにも通用する知識です。学んだことは無駄になりづらいので、ぜひこの連載をきっかけにAkkaクラスターの知識を深めていただければと思います。
まとめ
クラスターとこれまで紹介してきたアクターの機能を組み合わせると、Akkaは最大限の価値を発揮します。これは、言い換えると最大限の価値を発揮するためには、関連する全てのモジュールを学ぶ必要がある、ということです。
次回は連載最後の記事として、これらのモジュールをどう組み合わせるか、そもそもAkka自体を導入するかどうかの判断基準を紹介します。
