MinecraftのMODサーバをDockerコンテナを利用して構築する

久しぶりにマルチサーバを立てて友人と遊びたくなったので
今回はサーバをContainer化して構築してみました

## 開発環境

- OS
  - Ubuntu 22.04
- Middleware
  - Docker 20.10.23

## ホストマシンスペック

下記スペックにてGoogleCloud ComputeEngineで起動

- OS
  - Debian 11 bullseye
- Spec
  - e2-medium
    - vCPU : 2
    - mem  : 4GB
- Disk
  - pd-balanced
    - 10GB
- Middleware
  - Podman 3.0.1

## Podman installまで

コンテナランタイムは`Podman`を利用します

```sh
# apt-get update
# apt-get install -y podman
# podman -v
podman version 3.0.1
```

## Dockerイメージ作成

※ここから開発サーバ(PC)での作業
Minecraftサーバ本体となるコンテナイメージを作成します

### Dockerfile作成

```
$ vi Dockerfile
```

- 要件
  - openjdk, v20 : javaが実行できる必要があるため (最新の22だと逆に動かないので20を利用)
  - curl         : fabricからランチャー/ローダーをダウンロードするため
  - mods         : modsディレクトリの中身はあらかじめ開発PCで用意しておいてCOPYでコンテナに配置する
  - world        : /root/worldをVOLUMEとしてマウントし,ホストマシンと共有する →データの永続化が可能になる
  - port         : EXPOSEで25565を開放する
  - command      : ENTRYPOINTでminecraftサーバの起動コマンドを指定する

```Dockerfile
# This is the Dockerfile for Fabric(Mod enabled) Minecraft. version 1.20.1
FROM openjdk:20-slim-bullseye

LABEL maintainer="cokemaniaIIDX cokemaniaiidx@gmail.com"

RUN apt-get update && \
    apt-get install -y curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /root/

RUN curl -OJ https://meta.fabricmc.net/v2/versions/loader/1.20.1/0.14.22/0.11.2/server/jar && \
    java -jar fabric-server-mc.1.20.1-loader.0.14.22-launcher.0.11.2.jar nogui
RUN sed -i -e 's/difficulty=easy/difficulty=hard/g' server.properties && \
    sed -i -e 's/false/true/g' eula.txt

COPY files/mods/ /root/mods
VOLUME /root/world

EXPOSE 25565

ENTRYPOINT [ "java", "-jar", "-Xms3G", "-Xmx3G", "fabric-server-mc.1.20.1-loader.0.14.22-launcher.0.11.2.jar", "nogui" ]
```

### build用ファイル配置

```
$ mkdir -p files/mods
$ cd files/mods
$ gsutil cp gs://BUCKET_MODS_SAVED/* ./  # Storageに保存しておいたものを持ってくる
```

※ 筆者は`Diggus Maximus`という一括破壊MODを入れました

### build & push

※ `$ gcloud auth login`でArtifactRegistryへpush権限のあるユーザ(サービスアカウント)にログインしてから実施

```
$ docker build -t asia-northeast1-docker.pkg.dev/<MY_PROJECT_ID>/<ARTIFACT_RESISTORY_REPO>/fabric-minecraft:latest .
$ docker push asia-northeast1-docker.pkg.dev/<MY_PROJECT_ID>/<ARTIFACT_RESISTORY_REPO>/fabric-minecraft:latest
```

## コンテナ起動

※ ここからはホストマシンでの作業

- イメージプル

```
// まずログイン
# gcloud auth print-access-token | podman login -u oauth2accesstoken --password-stdin asia-northeast1-docker.pkg.dev

// 作ったイメージをpull
# podman pull asia-northeast1-docker.pkg.dev/<MY_PROJECT_ID>/<ARTIFACT_RESISTORY_REPO>/fabric-minecraft:latest
```

- 起動

```
# podman run -d -p 25565:25565 --volume=/mnt/world:/root/world --name minecraft asia-northeast1-docker.pkg.dev/<MY_PROJECT_ID>/<ARTIFACT_RESISTORY_REPO>/fabric-minecraft:latest
```

→ Minecraftクライアントを起動して、サーバにアクセスできればOK!

## 自動起動設定

- systemdファイル生成 & 登録

```
# podman generate systemd --new --files --name minecraft

# cp -Z container-minecraft.service /etc/systemd/system

# systemctl daemon-reload
# systemctl enable container-minecraft.service
```

- 再起動後、確認

```
# podman ps
CONTAINER ID  IMAGE                                                                             COMMAND  CREATED             STATUS                 PORTS                     NAMES
df5105884766  asia-northeast1-docker.pkg.dev/<MY_PROJECT_ID>/<ARTIFACT_RESISTORY_REPO>/fabric-minecraft:latest           About a minute ago  Up About a minute ago  0.0.0.0:25565->25565/tcp  minecraft

# systemctl status container-minecraft.service
● container-minecraft.service - Podman container-minecraft.service
     Loaded: loaded (/etc/systemd/system/container-minecraft.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-09-24 08:04:30 UTC; 1min 21s ago
       Docs: man:podman-generate-systemd(1)
    Process: 546 ExecStartPre=/bin/rm -f /run/container-minecraft.pid /run/container-minecraft.ctr-id (code=exited, status=0/SUCCESS)
    Process: 555 ExecStart=/usr/bin/podman run --conmon-pidfile /run/container-minecraft.pid --cidfile /run/container-minecraft.ctr-id --cgroups=no-conmon --replace -d -p 25565:25565 --volum>
   Main PID: 1176 (conmon)
      Tasks: 2 (limit: 19184)
     Memory: 28.4M
        CPU: 432ms
     CGroup: /system.slice/container-minecraft.service
             └─1176 /usr/bin/conmon --api-version 1 -c df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a -u df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a -r>

Sep 24 08:04:29 minecraft-server01 systemd[1]: Starting Podman container-minecraft.service...
Sep 24 08:04:30 minecraft-server01 podman[555]: 2023-09-24 08:04:30.030078134 +0000 UTC m=+0.656216844 container remove 36565fdf04c65d01ef5e9b1db5ca0e9ef79ba348b043b4ee47eae97c6d3cbf44 (image=asia->
Sep 24 08:04:30 minecraft-server01 podman[555]: 36565fdf04c65d01ef5e9b1db5ca0e9ef79ba348b043b4ee47eae97c6d3cbf44
Sep 24 08:04:30 minecraft-server01 podman[555]: 2023-09-24 08:04:30.070354064 +0000 UTC m=+0.696492751 container create df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a (image=asia->
Sep 24 08:04:30 minecraft-server01 podman[555]: 2023-09-24 08:04:30.523714846 +0000 UTC m=+1.149853570 container init df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a (image=asia-no>
Sep 24 08:04:30 minecraft-server01 podman[555]: 2023-09-24 08:04:30.529661827 +0000 UTC m=+1.155800514 container start df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a (image=asia-n>
Sep 24 08:04:30 minecraft-server01 podman[555]: df5105884766198d78daed04e5f2a8f7eb8c61f89366b1ad829b11b89b6a842a
Sep 24 08:04:30 minecraft-server01 systemd[1]: Started Podman container-minecraft.service.
```

→ ゲーム側でもアクセスできることを確認
問題なさそう！

## バックアップスクリプト作成

ホストサーバのcronにバックアップスクリプトを登録します。

- 5分毎にサーバのストレージ内にバックアップを作成するスクリプト

```bash:/usr/local/bin/backup_world.sh
#!/bin/bash

## ファイル数管理
while [ $(ls /mnt/backups | wc -l) -gt 12 ]
do
  rm $(ls /mnt/backups | head -1)
done

## バックアップ実行
tar -czf /mnt/backups/world_$(date "+%Y-%m-%d-%H-%M").tar.gz /mnt/world
```

- 1時間ごとにバックアップをCloudStorageにエクスポートするスクリプト

```bash:/usr/local/bin/export_backup.sh
#!/bin/bash

## 変数
BUCKET_NAME="backup-bucket"
VERSION="v1_20_1"
FILE=$(ls /mnt/backups | tail -1)


## バックアップ実行
gsutil cp /mnt/backups/$FILE gs://$BUCKET_NAME/world_$VERSION/world.tar.gz
```

※ バックアップを上書きしたくない場合は`world.tar.gz`と指定せずにディレクトリまで指定すればOK

- cronに登録

```
# crontab -e
*/5 * * * * /usr/local/bin/backup_world.sh > /dev/null 2>&1
0 */1 * * * /usr/local/bin/export_backup.sh > /dev/null 2>&1
```

# 終わり

コンテナのディスク永続化の基本を理解して
Minecraftサーバ構築を通じてうまく応用できたと思います

あとPodmanのsystemdファイル出力機能はとても便利ですね
本業でもapacheコンテナ起動などで利用しております

以上です
次記事を作るとしたら 今度はKubernetesで動かしてみた! とかになるんでしょうか(笑)