今回はマインクラフトサーバー運営の中でも、プロキシサーバーの運用についてお話ししたいと思います。
名前は聞いたことがあるけれど仕組みはよくわからない、そもそもなぜプロキシを挟むのか、といった疑問を持っている方に向けて、プロキシサーバーへの理解を深めてもらえる内容になっているはずです。
プロキシサーバーとは
通常、マインクラフトサーバーはJavaプロセスを起動してネットワークのポートを開放すれば、外からそのサーバーに直接接続できます。プロキシサーバーは、このプレイヤーとサーバーの間に挟まるサーバーです。つまり、プレイヤーが最初に接続するのはプロキシサーバーであり、その背後に待ち構えている各サーバーへ橋渡しをするのがプロキシサーバーの役目です。
プロキシサーバーを使う理由はいくつかありますが、まず挙がるのは負荷分散という考え方でしょう。
マインクラフトサーバーは、ワールドの生成やチャンクの処理、エンティティの動作、プレイヤーの操作への応答など、そのほとんどをメインスレッドという一本の処理の流れで捌いています。
これはゲームサーバーに同期的な処理、つまり処理の順番を保証するやり方が求められるからです。同期の対義語として非同期がありますが、こちらは処理の完了を待たずに次へ進む方式で、どの順番で終わるかは保証されません。ブロックを壊した結果を待たずに次の処理へ進んでしまうと、ゲームとして成り立たなくなるわけです。
そのため、マインクラフトサーバーはプレイヤー数やエンティティ数が増えて負荷が高まると、TPS(1秒あたりの処理回数、通常は20)が下がり、動作がもたついてしまいます。
では、大規模なサーバーはこの処理をどうやって捌いているのでしょうか。
負荷分散
ここで負荷分散というキーワードが出てきます。一つのサーバーですべてを処理するのではなく、複数のサーバーに分けて負荷を分散させよう、という考え方です。
海外の大規模なミニゲームサーバーはこの考え方に基づいて、ロビーとゲームサーバーが分離されていたり、ロビー自体が複数用意されていたりします。そして、接続してきたプレイヤーを複数のロビーに振り分けて、一箇所に集中しないようにしています。そうしなければ処理を捌ききれないのです。
サーバーの行き来はどうするか
ここでまた問題が出てきます。ロビーにログインしたプレイヤーを、別のサーバー(例えばミニゲームサーバー)に移動させる方法はあるのか、という問題です。
マインクラフトの通信規約(プロトコル)には、サーバー間を移動する機能はありません。この部分は別途、仕組みを整えてあげる必要があります。そこで登場するのがプロキシサーバーです。
プロキシサーバーの役割
プロキシサーバーは、クライアントからの接続を直接受け付けます。そして、その通信を接続先のサーバーへそのまま流します。プレイヤーから見ればプロキシサーバーと通信しているように見えますが、実際には背後のサーバーに通信が届いている、ということです。逆に、サーバーが送ってきた通信をプレイヤーへ返す橋渡しもしています。
これにより、目的別にサーバーを分け、必要最小限のプレイヤーだけをそれぞれのサーバーに接続させることができます。
サーバー間移動の仕組み
では、プロキシサーバーはどうやってプレイヤーを別のサーバーに移動させているのでしょうか。
先ほど書いた通り、マインクラフトのプロトコルにはサーバー間移動という概念はありません。プロキシサーバーがやっているのは、クライアントとの接続はそのまま維持したまま、裏側で「どのサーバーに通信を流すか」を切り替えることです。プレイヤーの視点では、ワールドが読み込み直されて別の場所に出てきたように見えますが、クライアントとしてはプロキシサーバーに繋ぎっぱなしで、一度も切断していません。
裏側では、プロキシが移動先のサーバーに対して新たにログイン処理を行い、古いサーバーとの接続を閉じ、以降のパケットを新しいサーバーに流すようにしています。クライアントにはリスポーン相当のパケットが送られるので、画面上はディメンション移動のような挙動になります。
この切り替えは、プロキシ側のプラグインからコマンドやイベントで実行できるほか、バックエンド側のプラグインからプラグインメッセージング(Plugin Messaging Channel)を通じてプロキシに「このプレイヤーをあのサーバーへ送ってくれ」と依頼することもできます。ロビーのNPCをクリックしたらミニゲームサーバーへ飛ぶ、といった仕組みはこれで実現されています。
代表的なプロキシソフトウェア
プロキシサーバーとして広く使われているものを紹介します。
BungeeCord
最も古くから存在するプロキシで、Spigotと同じmd_5氏が開発しています。歴史が長い分、対応プラグインも多く、情報も豊富です。Waterfall
BungeeCordをPaperMCがフォークしたもので、パフォーマンスや互換性の改善が加えられていました。
ただし現在は開発が終了しており、PaperMCからは後述のVelocityへの移行が推奨されています。- Velocity
PaperMCが一から設計したプロキシです。BungeeCordとの互換性を意識した設計ではなく、パフォーマンスとセキュリティを優先して作られています。特に「モダンフォワーディング」と呼ばれる、プロキシとバックエンド間の通信を秘密鍵で検証する仕組みが標準で用意されており、これから新しく構築するのであれば第一候補になるでしょう。
導入時に必ず押さえておくこと
プロキシ構成にすると、セキュリティ面で一つ大きな変化があります。それは、バックエンドサーバーのonline-modeをfalseにする必要があるということです。
通常、マインクラフトサーバーはMojangの認証サーバーと通信して、接続してきたプレイヤーが本物かどうかを確認します。プロキシ構成ではこの認証をプロキシサーバーが代わりに行い、バックエンドサーバーはプロキシから受け取った情報を信用する形になります。バックエンドは自分で認証しないので、online-modeはfalseにせざるを得ません。
ここで問題になるのが、バックエンドサーバーに直接接続されてしまった場合です。認証を行わないサーバーなので、名前を偽って誰にでもなりすますことができてしまいます。運営者のアカウント名で接続してOPコマンドを実行する、といった攻撃が成立してしまうわけです。
これを防ぐために、次の二つは必ず行ってください。
一つ目は、IPフォワーディングの設定です。プロキシがプレイヤーの本来のIPアドレスとUUIDをバックエンドに引き渡す設定で、BungeeCord系ならspigot.ymlのbungeecordをtrueに、Velocityならpaper-global.ymlでvelocity-supportを有効にし、プロキシ側と同じシークレットを設定します。Velocityのモダンフォワーディングでは、このシークレットを知らない接続はバックエンド側で拒否されるため、なりすましの対策にもなります。
二つ目は、ファイアウォールによる制限です。バックエンドサーバーのポートは、プロキシサーバーからの接続だけを許可し、それ以外からの接続はすべて遮断してください。同じマシン上で動かしているならlocalhostにバインドするだけで済みますし、別マシンならプライベートネットワークの経路だけを通すようにします。設定ファイルの対策だけに頼らず、ネットワークの段階で閉じておくのが基本です。
プレイヤーデータはどうなるか
プロキシを挟んだからといって、サーバー同士が自動的にデータを共有するわけではありません。各バックエンドサーバーは独立したマインクラフトサーバーなので、インベントリ、経験値、実績、統計などはそれぞれのサーバーに別々に保存されます。ロビーで持っていたアイテムをミニゲームサーバーに持ち込むことは、何もしなければできません。
これは欠点にも見えますが、ミニゲームサーバーのように「サーバーごとにインベントリが分かれている方が都合がいい」ケースも多いです。一方で、サバイバルの複数ワールドをサーバー単位で分けたい場合など、データを共有したいケースもあります。その場合は、MySQLやRedisといった外部のデータベースを用意し、サーバー間でデータを同期するプラグインを導入することになります。経済プラグインの所持金や、権限プラグインのランク情報も同様で、多くのプラグインはデータベースを使った共有に対応しています。
チャットも同じです。プロキシは通信を横流ししているだけなので、サーバーAでのチャットはサーバーBには届きません。サーバーをまたいだ全体チャットが必要な場合は、プロキシ側のプラグインで実装するか、上記のような外部データベースやメッセージングを介して連携する必要があります。
プラグインはどこに置くか
プロキシ構成では、プラグインを置く場所が二つになります。プロキシ側とバックエンド側です。
プロキシ側のプラグインは、サーバー間移動、全体チャット、ログイン制御、メンテナンスモードなど、複数のサーバーにまたがる処理を担当します。ただし、プロキシはワールドを持たないので、ブロックやエンティティに触れることはできません。
バックエンド側のプラグインは、ゲームの中身に関わる処理です。ワールド保護、ミニゲームのロジック、ショップなど、これまで通りのプラグインはこちらに置きます。
どちらに置くべきか迷ったら、「ワールドの情報が必要か」を基準に考えると判断しやすいです。
プロキシは万能ではない
ここまで読むと、プロキシを入れれば負荷の問題はすべて解決するように思えるかもしれませんが、そうではありません。
プロキシによる負荷分散は、あくまで「プレイヤーを複数のサーバーに分ける」ことで実現されるものです。一つのサーバーに100人が集まって同じ場所で遊んでいる状況は、プロキシを挟んでも何も変わりません。分散できるのは、分ける理由がある場合だけです。ロビーとゲームを分ける、ワールドごとに分ける、といった設計上の分割が先にあって、それを繋ぐ道具としてプロキシがあると考えてください。
また、プロキシサーバー自体にも負荷はかかります。すべてのプレイヤーの通信が一箇所を通るため、プレイヤー数が非常に多くなればプロキシがボトルネックになることもあります。大規模サーバーでは、プロキシ自体を複数台用意してDNSやロードバランサーで振り分ける構成も取られています。
よくある質問
BungeeCordとVelocity、どちらを選べばいい?
これから新規に構築するならVelocityがおすすめです。パフォーマンスとセキュリティの面で優れており、PaperMCが現在も積極的に開発しています。すでにBungeeCord向けプラグインに依存した環境がある場合は、移行コストと相談してください。
プロキシサーバーを使えばラグは減る?
プロキシ自体がラグを減らすわけではありません。減るのは、プレイヤーを複数のバックエンドサーバーに分散させた結果として、各サーバーの負荷が下がった場合だけです。一つのサーバーに人が集まる構成のままでは効果はありません。
バックエンドをonline-mode=falseにして安全なの?
IPフォワーディングの設定と、ファイアウォールでバックエンドへの直接接続を遮断する対策を両方行っていれば問題ありません。逆に、どちらかを怠るとなりすましによる乗っ取りが簡単に成立するので、必ず両方セットで設定してください。
まとめ
プロキシサーバーは、プレイヤーとバックエンドサーバーの間に立ち、通信を橋渡しすることでサーバー間移動を可能にする存在です。負荷分散のための「サーバーを分ける」という設計を成立させるために必要な道具であり、それ自体が負荷を減らすものではありません。
導入する際は、バックエンドがonline-mode=falseになることを理解した上で、IPフォワーディングの設定とファイアウォールによる直接接続の遮断を必ず行ってください。ここを怠ると、なりすましによる乗っ取りが簡単に成立してしまいます。
これからマイクラのプロキシサーバーを新しく構築するなら、Velocityを選ぶのが無難でしょう。次回は実際にVelocityを立てて、ロビーとサバイバルサーバーを繋ぐところまでを手順付きで紹介したいと思います。