モチベーションは興味本位.
Minecraftがやりたくなったので, サーバーを構築した. もちろんソロでプレイする場合はサーバー必要ないし, マルチだとしても先人の知恵を使えば簡単に構築できる. が独自で構築することにした.
サーバー構築
サーバー用の実行JARはSpigotMC[1] にした. まあこれはModとか入れるなら安定. サーバー本体は Docker image に固めてKubernetes(k8s)上で構築することにした. プラットフォームにはGoogle Cloud Platform(GCP)を利用した. Docker image に固めたのは, 構築しやすくするためで特にサーバーを起動したり落としたりすることが多いので正解. k8s上に構築したのはただの趣味. ふつうはAWSならばEC2やGCPの場合は Compute Engine を使うことが多いと思われる. また Docker image を利用するにしてもサーバーは1台しか使わないので Amazon ECS とかで十分なはず. IaCするうえで「Terraform使うくらいならk8sでいいか」くらいの気持ち. 仕事でもないなら自分の好きなようにやるのが一番. ちなみに2023年4月現在は課金アカウント1つにつき1クラスターまで無料で使える[2](こんなのに貴重な1枠を使っていいのか).
Kubernetes での構築
Minecraftサーバーを構築するには, Minecraftそのものを動かすサーバー本体のほかにサーバーデータを記録するための永続化ディスク(とはいえEC2のようなVMそのものであれば勝手に永続化ディスクがついてくるので意識する必要がない場合もある)と外部から接続できるようにするためのロードバランサーが必要になる. これらはすべてKubernatesの構成ファイルとして表現できる. とても良い. 詳しくは後述するが, 特にロードバランサーを作ったり削除したりができてとても良い.
Google Kubernetes Engine にはフルマネージドの標準モードのほかにAutopilotモードがある. 今回はAutopilotモードを使用した. インフラ周りで考えることが少ないので楽.
Docker image
Minecraftサーバーとして利用できる Docker image として有名なのは itzg/minecraft-server[3] があるが, せっかくなので自分でイメージを作成した. SpigotMCではサーバーとなるJARを事前にビルドする必要がある. DockerなのでJARをビルドするレイヤーとサーバーとしてJARを実行するレイヤーを用意することにした. マイナーバージョンアップ程度であれば dcoker build
時に ARG
を変更するだけで対応できそうだ. JARのランタイムイメージはビルドで使ったイメージと同じものでも構わないが distroless[4] を使うことにした. イメージサイズも小さくなるし, 構成もシンプルなので最適だと思う.
ARG SPIGOT_VERSION=1.19.3
FROM openjdk:17.0.2-slim-bullseye AS build-spigotmc
ARG SPIGOT_VERSION
WORKDIR /usr/local/bin
RUN apt-get update && apt-get install -y \
wget \
git
RUN wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar \
&& java -jar BuildTools.jar --rev ${SPIGOT_VERSION} \
&& mv spigot-${SPIGOT_VERSION}.jar server.jar
RUN echo eula=true > eula.txt
FROM gcr.io/distroless/java17-debian11
COPY --from=build-spigotmc /usr/local/bin/server.jar /usr/local/bin/server.jar
COPY --from=build-spigotmc /usr/local/bin/eula.txt /usr/local/bin/eula.txt
WORKDIR /usr/local/bin
CMD ["-Xmx2048M", "-Xms1024M", "-jar", "/usr/local/bin/server.jar", "nogui"]
Minecraftの設定は server.properties
で設定することになるが, 今のところ特に設定していない. 必要になったらk8sから入れることになるだろう.
永続化ディスク
サーバーのワールドデータは world/
ディレクトリのファイルとして保存されているため, コンテナで運用するためには別途永続化ディスクを用意しなければならない. 一応10GB用意したが, 今のところ1GBも使っていない.
コンテナの設定
ゲームサーバーにありがちだがMinecraftのサーバーはスケールアウトができずスケールアップするしかない. 前述のとおりk8sはAutopilotモードで利用しているがAutopilotのデフォルトだとCPUとメモリが足りないのでここは自前で設定しなければならない.1vCPUより小さくすると描画がもたつくようだ(Autopilotではデフォルトが 0.5).

Cloud Monitoring からも確認できる.
監視
デフォルトでコンテナの標準出力からログが出力されるため, 特に何もしなくてもログ出力される. しかも Cloud Logging のログレベルにそのまま反映される. ユーザーのログイン情報もログに出力されるので, 比較的簡単に管理できそうだ.

特になにもしなくても Cloud Logging にログが記録されている.
運用
ロードバランサー金がかかる

k8sそのものは料金がかからないが, k8sから Cloud Load Balancing を使うと料金がかかる(Cloud Run にしておけばよかったんじゃ…). そのため使わない間は少なくともロードバランサーを削除しておかなければならない. またロードバランサーを作りなおすたびにIPアドレスは変更される. IPアドレスの確認が面倒であれば固定IPにするかDDNSにする必要があるだろう. 今回はここまでの対応はせずにIPアドレスを常に確認することにした.
k8sを使っているためロードバランサーの作成・削除は kubectl を使えばよい. これを Web API でラップしてやればあとは外からAPIを叩くだけでよくなる. 簡単に Web API を構築するには Cloud Functions を使うのが最も簡単だ. しかし Cloud Functions からは kubectl を使うことはできない. なので直接コンテナを利用できるサービスを使う必要がある.
このためにGCPから Kubernetes Python Client[5] を利用するイメージを作った.
これをもとにロードバランサーを作成・削除するイメージを用意した. GitHubのリリースからロードバランサーを構成するYAMLファイルを取得して kubectl を実行すればロードバランサーを作成できる. ロードバランサーを削除するには名前さえわかっていればよい.
これからさらに Web API のインターフェースを用意してやれば外部から叩くことができるようになる. が, 今回は Web API にはせずに Cloud Run jobs で実行するだけにした. Cloud Run jobs はジョブを作成しておけばGCPコンソール上から実行ボタンを押すだけで実行することができる. 今のところはこれでも問題ない. 外から叩くことができるようにしたければ, イメージごと Web API サービスにしてやるか, Cloud Functions から今ある Cloud Run jobs をトリガしてやればよいだろう.
勢いでk8sを使ってMinecraftサーバーを構築してみたが, やはり引っかかるところが多くて大変だった. ただMinecraftが遊びたいだけなら, せいぜい Cloud Run を使っておけばよさそうだ. しかしサーバーの構成管理が容易だし腐ることはないので, とりあえずk8sを選んでおくというのもありかもしれない(責任は取らない).
参考
[1] SpigotMC - High Performance Minecraft
[2] クラスタ管理手数料と無料枠 | 料金 | Google Kubernetes Engine(GKE) | Google Cloud
[3] itzg/minecraft-server - Docker Image | Docker Hub
[4] GoogleContainerTools/distroless: 🥑 Language focused docker images, minus the operating system.
[5] kubernetes-client/python: Official Python client library for kubernetes
0 件のコメント:
コメントを投稿