Minecraft サーバを GCE 上に建てるコマンド

僕は Minecraft を遊んだことはないが最近知人からマイクラサーバを建ててほしいと頼まれたのでドキュメントを諸々読みながら GCE 上に建ててみた。

コマンド自体は Minecraft に限らず応用が効くと思うので将来のためにメモっておく。

最低限

CONTAINER_IMAGE="itzg/minecraft-server:java8"
INSTANCE_NAME="minecraft"
MACHINE_TYPE="e2-highcpu-2"
ZONE="asia-northeast1-a"
MEMORY="1500M"
DISK_AUTO_DELETE="no"

CONTAINER_ENV="\
EULA=TRUE,\
TZ=Asia/Tokyo,
MEMORY=$MEMORY\
"

gcloud compute instances create-with-container $INSTANCE_NAME \
  --container-image $CONTAINER_IMAGE \
  --machine-type $MACHINE_TYPE \
  --zone $ZONE \
  --container-env $CONTAINER_ENV \
  --create-disk name=$INSTANCE_NAME-data,device-name=$INSTANCE_NAME-data,size=10GB,auto-delete=$DISK_AUTO_DELETE \
  --container-mount-disk mount-path="/data",name=$INSTANCE_NAME-data \
  --tags minecraft-server

gcloud compute firewall-rules create allow-minecraft \
  --allow tcp:25565 --target-tags minecraft-server

MACHINE_TYPEZONEMEMORY あたりは要件に応じて変えてください)

コマンドの意味は gcloud CLI リファレンス 1 を見れば全部書いてあるけれど順に解説していく。

create-with-container

まずサーバを建てるときに Docker イメージが公開されていたら嬉しいなと思って検索したら既に公開してくれている先人がいたので感謝しかないですね:

github.com

Tag に latest を指定するとこの記事を書いている時点では Java 11 でサーバが起動するようだが個人的に latest よりもバージョンは明示的に指定したいと思っているのと Java 15 は LTS ではないようなので java8 にした (筆者は Minecraft にも Java にも詳しくないという事情もある)。

GCE では「Docker コンテナをデプロイして起動するように仮想マシンVMインスタンスまたはインスタンス テンプレートを構成でき」る 2 ので、これを使いましょう。 そのコマンドが

gcloud compute instances create-with-container INSTANCE_NAME \
  --container-image CONTAINER_IMAGE

です。コマンドのリファレンスはこちら:

https://cloud.google.com/sdk/gcloud/reference/compute/instances/create-with-container

ちなみに各 VM インスタンスにデプロイできるコンテナは 1 つのみという制限 3 があり、 複数コンテナをデプロイしたい場合は Kubernetes (GKE) を使ってくださいということらしい。

さてこれでサーバは起動するのですが Minecraft はゲームなのでゲームデータという状態を持ち、その状態はコンテナ外のどこかに永続化したい。

上記の minecraft-server の README を読んでみるとホスト側のデータ保存用ディレクトリをコンテナの /data ディレクトリにマウントすれば良いと書いてあり、 gcloud compute instances create-with-container コマンドでは --create-disk オプションを指定するとインスタンスの作成時と同時に永続ディスクを作成してインスタンスにアタッチすることができ、さらに --container-mount-disk オプションを一緒に指定することでその作成したディスクをコンテナの指定した path にマウントできる、のでそいつを使おうという寸法です。

ここで注意すべき点としては --container-mount-disk でマウントするディスクはそのディスク名と device-name(そのディスクの、OS が認識する名前(?)。詳しいことはよくわからない……)が同じでなければならない、ということです (これらが異なるとマウントに失敗します)。 なので --create-disk 時に明示的に namedevice-name に同じに名前を指定しています。

また auto-delete はこのインスタンスが削除されたときに自動でこの永続ディスクも一緒に削除するかどうかを指定するフラグです。 これを no に指定することで、誤ってインスタンスを削除してしまい今まで作ったワールドデータが全部吹っ飛んでしまう、というような事故を防ぎます。 デフォルト値は yes なので、検証用に別のサーバを一時的に建てるというような場合は auto-delete を指定する必要はないかもしれません。

--tags は後述の firewall-rules で説明します。

firewall-rules

無事マイクラサーバが建ってもクライアント側から接続できないと意味が無いので、マイクラ用のポートだけファイアウォールに穴を開けたいと思います。

さてガイドが存在して:

cloud.google.com

コンテナポートには、ホスト VM ポートへの 1 対 1 のマッピングがあります。たとえば、コンテナポート 80 はホスト VM ポート 80 にマップされます。Compute Engine ではポートの公開(-p)フラグをサポートしていないため、このフラグを指定しなくても、マッピングは機能します。

コンテナのポートを公開するには、ホスト VM インスタンスのポートへのアクセスを許可するようにファイアウォール ルールを構成します。コンテナの対応するポートには、ファイアウォールのルールに従って自動的にアクセスできるようになります。

具体的には create-with-container 時に --tags オプションでインスタンスにタグを付け、firewall-rules createファイアウォールルールを作成し、そのルールを適用したい対象インスタンスのタグを --target-tags オプションで指定すれば良いというわけですね。

https://cloud.google.com/sdk/gcloud/reference/compute/firewall-rules/create#--target-tags

Minecraft はデフォルトで 25565 番ポートを使用するようなのでそのポートのトラフィックを許可することにします。

これで無事マイクラサーバが順当に建ち、クライアントから接続して遊べるようになるでしょう。

--container-env についてもう少し

Minecraft には op 権限を持つユーザという概念があるらしく、上述の minecraft-server Docker image では OPS 環境変数Minecraft ユーザ名をカンマ区切りで指定することで op 権限を付与することができる 4 ので、 必要に応じて CONTAINER_ENV に追加されたし。

また Minecraft 界隈ではサードパーティによる独自拡張である "mod" が多数公開されていて、複数の mods をクライアント・サーバに配置して遊ぶことがよく行われている(らしい)。 上述の minecraft-server Docker image では MODS 環境変数に jar ファイルの URL をカンマ区切りで指定するとコンテナ起動時に自動でその jar ファイルをダウンロードしてくれる 5 (便利ですね)。

ここで読者諸賢は --container-env=[KEY=VALUE, …,…] という構文中で VALUE としてナイーブにカンマを使うと KEY=VALUE の組を分ける意味のカンマと衝突し構文エラーになることに気付くであろう。 実際 create-with-container しようとすると以下のようにエラーが表示される:

ERROR: (gcloud.compute.instances.create-with-container) argument --container-env: Bad syntax for dict arg: [...]. Please see `gcloud topic flags-file` or `gcloud topic escaping` for information on providing list or dictionary flag values with special characters.

エラーメッセージが親切にも案内してくれているように gcloud topic escaping を読んでみましょう:

https://cloud.google.com/sdk/gcloud/reference/topic/escaping

……なるほど!!!つまり --container-env=^DELIM^ と書くことで各 KEY=VALUE 同士の delemeter をカンマではなく DELIM に変更することができるので、その上で VALUE にカンマを含めることができるというわけですね。 DELIM: とか - のような一文字だけでなく :: とか -- のような複数文字も許されています。MODS に URL を指定することを考えると :: あたりが良い選択肢ではないでしょうか。

具体例として今回僕は Buildcraft, Storage Drawers, Packing Tape という mod を入れることにしたので、 CONTAINER_ENV は以下のようになりました:

CONTAINER_ENV="\
^::^\
EULA=TRUE::\
TZ=Asia/Tokyo::\
MEMORY=$MEMORY::\
TYPE=FORGE::\
MODS=\
https://edge.forgecdn.net/files/2502/739/buildcraft-7.1.23.jar,\
https://github.com/jaquadro/StorageDrawers/releases/download/sd-1.10.8/StorageDrawers-1.7.10-1.10.8.jar,\
https://github.com/gigaherz/PackingTape/releases/download/v0.3.5/Packing.Tape-0.3.5.jar::\
VERSION=1.7.10::\
OPS=pione30,foo_user,bar_user\
"

おわりに

具体例として Minecraft の話をしましたが Docker Hub に image が公開されていて環境変数の指定だけで設定を変えてスッと起動できる場合であればコマンド数本で手軽に GCE にインスタンスを建てられるんだな〜ということがわかったので良い収穫でした。

やはり Docker image を起点としてなるべく状態を持たないように起動できると検証用にサーバを建てたり壊したりするのが簡単かつ気軽にできて良いですね。