AWS EC2+Dockerで無料のマイクラサーバーを立てた話


# 概要
身内用にマイクラサーバーを立てる機会があったので、せっかくならとAWS EC2とDockerを使って立ててみました。

AWSには一年間の無料枠があり、EC2サーバーを月750時間動かすことができます。（複数サーバーを立てた場合、サーバーごとではなくそれぞれのサーバーの合計となるので注意）

今回作ったもののソースコードはこちら

https://github.com/Nitoryu008/aws-docker-mc-server

# AWS EC2サーバーを立てる
今回は以下のような構成でEC2サーバーを立てました。t4g.smallは無料利用枠の対象には書かれていませんが、以下のページによると2024年12月31日まで無料で利用できるようです。

https://aws.amazon.com/jp/ec2/faqs/#t4g-instances

| 項目 | 値 |
|:----:|:----:|
| 名前 | Minecraft Server |
| Amazonマシンイメージ | Amazon Linux 2023 AMI |
| アーキテクチャ | 64ビット (Arm) |
| インスタンスタイプ | t4g.small |
| キーペア | RSA(pem形式) |
| ストレージ | 8GiB |

ストレージは後からでも追加できるのでとりあえずデフォルトの8GiBにしています。

## セキュリティグループの設定

| タイプ | ポート範囲 | ソース |
|:------:|:---------:|:-------------:|
| カスタムTCP | 25565 | Anywhere-IPv4 |

Minecraft Serverはデフォルトで25565ポートを使用するので解放してあげましょう。デフォルトポートを変更した場合はこちらの変更も忘れずに。

# Dockerをインストール

```shell:Docker
sudo yum install -y docker
sudo service docker start
sudo usermod -a -G docker ec2-user
```

```shell:Docker Compose
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.4.1/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
```

# Dockerでマイクラサーバーを立てる
良さそうなDocker Imageがあったのでそれを使います。Minecraftのバージョンは1.21.1です。

https://hub.docker.com/r/itzg/minecraft-server

Docker Composeをいい感じに設定
```yml:docker-compose.yml
services:
  mc-1: #サービス名
    image: itzg/minecraft-server
    tty: true
    stdin_open: true
    ports:
      - "25565:25565"
    environment:
      EULA: "TRUE"
      VERSION: "1.21.1" #Minecraftバージョン
    volumes:
      - ./data/mc-1:/data #/data/(サービス名)にデータを保存
  mc-2:
    image: itzg/minecraft-server
    tty: true
    stdin_open: true
    ports:
      - "25570:25565" #mc-1と被らないよう25570ポートに変更
    environment:
      EULA: "TRUE"
      VERSION: "1.21.1"
    volumes:
      - ./data/mc-2:/data
  mc-3: ...
```

Dockerコンテナ上で動かすことで複数のワールドを一つの物理サーバー内で共存させることができます。しかし、今回使用したt4g.smallは性能上同時に一つのワールドまでしか起動できません。（二つ以上起動するとサーバーが死にました。）無料なのでそこは我慢。

## ワールドを起動する

```shell
docker-compose up -d mc-1(docker-compose.ymlで設定したサービス名)
```

docker-compose up -dするとすべてのワールドが起動してしまうので、サービス名を指定することで特定のワールドのみ起動できます。


## ワールドを停止する

```shell
docker-compose rm -fsv mc-1(docker-compose.ymlで設定したサービス名)
```

docker-compose downではなくdocker-compose rm を使うことで特定のワールドのみ停止できます。

# サーバーをバックアップ

まず最初にGitとGitHubの設定をします。

## Git, GitHub CLIのインストール

```shell:Git
sudo yum install -y git
```
```shell:GitHub
type -p yum-config-manager >/dev/null || sudo yum install yum-utils
sudo yum-config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo
sudo yum install gh
```

https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

https://github.com/cli/cli/blob/trunk/docs/install_linux.md#amazon-linux-2-yum

## GitHub CLIでログイン
GitHubでrepo権限をもつTokenを作成

https://github.com/settings/tokens


```shell:GitHub CLI
gh auth login
```
色々聞かれるのでGitHubで生成したTokenを使ってログイン

https://techblog.ap-com.co.jp/entry/2021/08/23/091131

## サーバー制御用スクリプト

```shell:minecraft.sh
#!/bin/bash

MCDIR="~/minecraft"
worlds=("mc-1" "mc-2")

# 全てのワールドを起動
start() {
  cd "$MCDIR"
  docker-compose up -d
}

# 全てのワールドを停止
stop() {
  cd "$MCDIR"
  docker-compose down
}

# 全てのワールドを保存
saveAll() {
  cd "$MCDIR"
  for world in ${worlds[@]}; do
    docker-compose exec $world rcon-cli save-all
  done
}

case "$1" in
save-all)
  saveAll
  sleep 30
  ;;
start)
  start
  ;;
stop)
  stop
  ;;
*)
  echo $"Usage: $0 {start|stop|save-all}"
  ;;
esac
```

## バックアップ用スクリプト

```shell:backup_github.sh
#!/bin/bash

DATE=`date '+%Y-%m-%d %H:%M:%S %z'`

# docker-compose.ymlで設定したサービス名を指定
worlds=("mc-1" "mc-2")

cd ~/minecraft

bash ~/minecraft/scripts/minecraft.sh save-all

for world in ${worlds[@]}; do
  docker-compose exec $world rcon-cli "say バックアップ中..."
done

git add -A
git commit -m "$DATE"
git pull origin main --rebase
git push origin main

for world in ${worlds[@]}; do
  docker-compose exec $world rcon-cli "say バックアップ完了"
done
```

## リポジトリの設定
適当なリポジトリを作り、docker-compose.yml, /scripts/backup_github.sh, /scripts/minecraft.shを配置します。その後リポジトリを~/minecraftフォルダにcloneします。

```shell
cd ~
gh repo clone "リポジトリ名" minecraft
```

backup_github.shを実行すればバックアップできます。

```shell
bash backup_github.sh
```

## バックアップを定期実行する
いちいち手動で実行するのは面倒なのでcronに登録して定期実行します。

```shell
# cronをインストール
sudo yum install cronie -y
sudo systemctl enable crond.service
sudo systemctl start crond.service
# 例：一時間ごとにバックアップするよう設定
sudo crontab -e
0 * * * * bash ~/minecraft/scripts/backup_github.sh
sudo systemctl restart cron
```

# 感想

- cronの代わりにsystemdのタイマー機能があるらしい。
- docker-composeで全てのコンテナにコマンドを送る方法が分からなかったため、とりあえず配列で全サービスを列挙している。もっとスマートにしたい気持ち。
- そのうちオンプレサーバーに移行する予定。サーバー欲しい。

# 参考
[ローコストで24時間MineCraftサーバーを実現する](https://qiita.com/ekko/items/851fb0cc9eddb9680071#cron%E7%99%BB%E9%8C%B2)
[AWS EC2インスタンスにdockerとdocker-composeをインストールして簡単なWEBサービスを立ち上げる方法](https://qiita.com/y-dobashi/items/e127211b32296d65803a#2-dokcer%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
[Minecraft Server on Docker (Java Edition)](https://docker-minecraft-server.readthedocs.io/en/latest/)
[Docker Compose の一部のサービスだけ Up/Down する](https://aton-kish.github.io/blog/post/2020/10/04/docker-compose-rm/)