MinecraftマルチサーバをDockerで建てる方法

前回の続き。ここではAWS EC2にUbuntuを入れたあと、Dockerを動かしてコンテナ上でMinecraftのマルチサーバであるSpigotを動かす。

IaaSの上でDockerを建てるというのはリソースの無駄ではないかというのは正直その通りである。だが、これはフレンドや家族内で使うサーバであって業務用ではなく、「安く借りたサーバをなるべく汚さずキレイに使い、定期的なバージョンアップの手間も省きたい」という願いから生まれたソリューションであることをご理解戴きたい。いらなくなったら捨てれば良いということすらしたくないのである。

構築するMinecraftサーバの要件

  • 安定版のSpigotが動作する
    • 現在は1.16.4
  • 管理系のプラグインを利用する
    • Dynmap
    • GriefPrevention

サーバOSはUbuntuのLTSを利用する。現在のLTSは20.04である。

ちなみに、Dynmapはブラウザで以下のような地図が確認できるようになるプラグインである。迷子防止に役立つ。

Grieaf protectionは主にクリーパーによる建造物破壊を防ぐために入れている。

手順

インスタンスのデプロイ

マネージメントコンソールでポチポチしてサーバを建てる。グローバルIPを振った瞬間ポートスキャンが飛んでくるので、セキュリティグループの設定はしっかりしておく。面倒であればとりあえず「インバウンドルール」にマイIPだけ入れておけば最低限何とかなる。最初はVPCとかの設定もあるがその辺はググって欲しい。AWSでセキュアなサーバを建てる方法は多分いっぱいある。

やることをざっくりリストにすると以下になる。

  1. (請求アラートを設定する)
  2. VPCでサブネットを切る(PublicVPCでいい)
  3. セキュリティグループを作成し、インバウンドルールにマイIPを設定する
  4. EC2でサーバをデプロイする。このときにさっき作ったセキュリティグループを設定する。作成時に秘密鍵を作成してダウンロードする
  5. ElasticIPを1個割り当てする
  6. ElasticIPをデプロイされたサーバに関連付けする

RIを使うなら先に買っておくこと。

OSへのログイン時のユーザ名はUbuntu、パスワードではなく秘密鍵で入る。

Dockerのインストールまで

以下のコマンドを何も考えずに打つ。
最近のインスタンスはアーキテクチャがarmのこともあるが、そのときは[arch=amd64]を[arch=arm64]にすること。

sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get -y install apt-transport-https ca-certificates curl screen
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get -y install docker-ce

sudo docker run hello-world

Hello Worldが出ればOK。

Dockerの設定

Dockerは原則Dockerfileに書かれたことしかせず、停止したら中のデータは消える。ワールドの情報が消えては困るのでボリュームを使ってコンテナの外に出しておき、コンテナ起動時にマウントさせる方式をとる。

mkdir -p /world/world /world/world_nether /world/world_the_end /world/plugins

screen -S console
mkdir -p minecraft-prj/spigot-1.16.4
cd minecraft-prj

cat << EOS > spigot-1.16.4/Dockerfile
FROM ubuntu:20.04
RUN apt-get update; apt-get install -y openjdk-11-jre-headless wget git vim
RUN wget "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar"  -O BuildTools.jar
RUN git config --global core.autocrlf input
RUN java -jar BuildTools.jar -rev 1.16.4
RUN echo "eula=true" > eula.txt

RUN echo > server.properties
RUN echo 'generator-settings={"coordinateScale":684.412,  "heightScale":684.412,  "lowerLimitScale":512.0,  "upperLimitScale":512.0,  "depthNoiseScaleX":200.0,  "depthNoiseScaleZ":200.0,  "depthNoiseScaleExponent":0.5,  "mainNoiseScaleX":80.0,  "mainNoiseScaleY":160.0,  "mainNoiseScaleZ":80.0,  "baseSize":8.5,  "stretchY":12.0,  "biomeDepthWeight":1.0,  "biomeDepthOffset":0,  "biomeScaleWeight":1,  "biomeScaleOffset":0, "seaLevel":63, "useCaves":true, "useDungeons":true, "dungeonChance":7, "useStrongholds":true, "useVillages":true, "useMineShafts":true, "useTemples":true, "useRavines":true, "useWaterLakes":true, "waterLakeChance":4, "useLavaLakes":true, "lavaLakeChance":80, "useLavaOceans":false, "fixedBiome":-1, "biomeSize":5, "riverSize":3, "dirtSize":33, "dirtCount":10, "dirtMinHeight":0, "dirtMaxHeight":256, "gravelSize":33, "gravelCount":8, "gravelMinHeight":0, "gravelMaxHeight":256, "graniteSize":33, "graniteCount":10, "graniteMinHeight":0, "graniteMaxHeight":80, "dioriteSize":33, "dioriteCount":10, "dioriteMinHeight":0, "dioriteMaxHeight":80, "andesiteSize":33, "andesiteCount":10, "andesiteMinHeight":0, "andesiteMaxHeight":80, "coalSize":17, "coalCount":20, "coalMinHeight":0, "coalMaxHeight":128, "ironSize":9, "ironCount":20, "ironMinHeight":0, "ironMaxHeight":64, "goldSize":9, "goldCount":2, "goldMinHeight":0, "goldMaxHeight":32, "redstoneSize":8, "redstoneCount":8, "redstoneMinHeight":0, "redstoneMaxHeight":16, "diamondSize":8, "diamondCount":1, "diamondMinHeight":0, "diamondMaxHeight":16, "lapisSize":7, "lapisCount":1, "lapisCenterHeight":16, "lapisSpread":16}' >> server.properties
RUN echo "force-gamemode=true" >> server.properties
RUN echo "allow-nether=true" >> server.properties
RUN echo "enforce-whitelist=false" >> server.properties
RUN echo "defaultgamemode=0" >> server.properties
RUN echo "gamemode=0" >> server.properties
RUN echo "broadcast-console-to-ops=true" >> server.properties
RUN echo "enable-query=false" >> server.properties
RUN echo "player-idle-timeout=0" >> server.properties
RUN echo "difficulty=2" >> server.properties
RUN echo "spawn-monsters=true" >> server.properties
RUN echo "op-permission-level=2" >> server.properties
RUN echo "pvp=true" >> server.properties
RUN echo "snooper-enabled=false" >> server.properties
RUN echo "level-type=DEFAULT" >> server.properties
RUN echo "hardcore=false" >> server.properties
RUN echo "enable-command-block=true" >> server.properties
RUN echo "max-players=20" >> server.properties
RUN echo "network-compression-threshold=256" >> server.properties
RUN echo "resource-pack-sha1=" >> server.properties
RUN echo "max-world-size=29999984" >> server.properties
RUN echo "server-port=25565" >> server.properties
RUN echo "server-ip=" >> server.properties
RUN echo "spawn-npcs=true" >> server.properties
RUN echo "allow-flight=false" >> server.properties
RUN echo "level-name=world" >> server.properties
RUN echo "view-distance=10" >> server.properties
RUN echo "resource-pack=" >> server.properties
RUN echo "spawn-animals=true" >> server.properties
RUN echo "white-list=false" >> server.properties
RUN echo "generate-structures=true" >> server.properties
RUN echo "online-mode=true" >> server.properties
RUN echo "max-build-height=256" >> server.properties
RUN echo "level-seed=" >> server.properties
RUN echo "prevent-proxy-connections=true" >> server.properties
RUN echo "use-native-transport=true" >> server.properties
RUN echo "enable-rcon=false" >> server.properties
RUN echo "motd=Welcome to Minecraft Server Spigot." >> server.properties
RUN echo "spawn-protection=2" >> server.properties
RUN echo "use-native-transport=true" >> server.properties

CMD java -server -Xms1024m -Xmx3072m -XX:+UseG1GC -jar spigot-1.16.4.jar -c server.properties nogui

EXPOSE 80 25565
EOS

力業で済まない。
generator-settingsは我が家標準の川を大きめに作るレシピである。無くてもいいので必要に応じて消して欲しい。seedには何も入れてないので適当なseedをいれること。もちろん無くてもよい。

Dockerの起動

cd spigot-1.16.4
sudo docker build --no-cache -t="Spigot1.16.4/spigot" .

タグは適当に。–no-cacheにより、適宜最新のSpigotがダウンロードされるがバージョンを指定しているために勝手にバージョンアップはされない。ビルドが終わるまでしばらく待つ。

sudo docker run -i -t --name Spigot1.16.4 -v /world/world:/world -v /world/world_nether:/world_nether -v /world/world_the_end:/world_the_end -v /world/plugins:/plugins -p=25565:25565 -p 80:80 Spigot1.16.4/spigot:latest

Dockerを起動すると、コンソール上にMinecraftサーバが動くのが見えるはず。
起動が終わったら一度PCでMinecraftを起動し、EC2に割り当てられたグローバルIPで接続確認するのもよい。

一度起動したらstopコマンドで停止する。

プラグインの導入

DynmapGrief Preventionをプラグインフォルダに入れる。
wgetで直接サーバに落とそうとするとCDNに怒られるので、先にPCにダウンロードしておいたほうがよい。
scpの使い方は省く。Teratermからやれ。

サーバにscp後、ローカルのjarをコンテナから見える場所に移動する。

cp Dynmap-3.0-SNAPSHOT-spigot.jar /world/plugins
cp GriefPrevention.jar /world/plugins

プラグインの設定ファイルを生成するために一度起動する。先ほどstopでMinecraftサーバが停止したが、同時にコンテナも死んでいるため起動してコンソールにアタッチする。

sudo docker start Spigot1.16.4
sudo docker attach Spigot1.16.4

起動ログの中にDynmapとGrief Preventionが出てきたら成功。エラーの場合は何か赤字が出る。起動確認したらまたstopで殺す。

Dynmapはデフォルトではport 8123でリスンする。ブラウザでポートを指定するのが面倒なので80に変更する。

cd /world/plugins/dynmap/
vi configuration.txt

webserver-port: 8123という行があるので80にして保存する。viの使い方はググって欲しい。

再度、起動する。

sudo docker start Spigot1.16.4
sudo docker attach Spigot1.16.4

これでDynmapとGrief Preventionが導入されたSpigotサーバを建てることができた。
構築についてはここまでで完了である。

ログアウトする前に

作業はscreen上で実施しているので、screenをデタッチすることでホストに戻ることができる(dockerの設定の頭くらいでscreenを起動している)。screenのデタッチはCtrl+aを押した後dを押す。すると、screenを起動する直前の画面が表示されるはずだ。これによって、SSHコンソールを閉じたら一緒にdockerも落ちるみたいなことが無くなる。デタッチ後はSSHコンソールは閉じてよい。Minecraftサーバはscreenが生成した仮想コンソール上で生き続ける。

逆に、screenに復帰したい場合は以下のコマンドを打つ。

screen -r

なにがしかの原因でMinecraftサーバが不正落ちすることはあり得る。そのときはscreenの中で人知れず死んでいるかコンテナそのものが死んでいるので、先のコンテナ起動のコマンドですぐにサーバを復帰させることができる。起動する前にscreenコマンドを打っておくこと。

Enjoy.

Comments are closed.