CDKでMinecraftサーバを作ってみた

CDKでMinecraftサーバを作ってみた
この記事をシェアする

はじめに

こんにちは、スカイアーチHRソリューションズのsugawaraです。

先日公開されたAWS公式ブログにて、「Amazon EC2 に Java 版 Minecraft サーバーをセットアップ」という記事を見つけました。自分は普段あまりゲームをやらないため、自前のサーバでプレイすることがゲームができるとは知りませんでした。いい機会だと思ったので、実際に構築してみました。

※実際にプレイするにはライセンスの購入が必要なため、上記の記事と同様にサーバのセットアップまでとなります(サーバを用意すれば無料でできると勘違いしていました。。。)

なお、マネジメントコンソールでの手動構築だと、ブログ記事と同じになってしまうため、今回はCDK(TypeScript)で書いてみました。

環境

WSL2
CDK 2.121.1
Node.js 18.18.1

流れ

まずWSL上に作業フォルダMinecaftを作成し、そのディレクトリにてCDKの準備をします。

$ mkdir Minecraft
$ cd Minecraft
$ cdk init app --language typescript

作成されたフォルダは下記のような構成になっています。

├── bin
│   └── minecraft.ts        // ここにenvを定義
├── cdk.json
├── jest.config.js
├── lib
│   └── minecraft-stack.ts  // ここにAWSリソースを定義
├── package.json
├── package-lock.json
├── README.md
├── test
│   └── minecraft.test.ts
└── tsconfig.json

bin/minecraft.tsファイルにて、デプロイするアカウントとリージョンを指定します。今回はGithubなどにはpushしないため、シンプルにハードコーディングします。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { MinecraftStack } from '../lib/minecraft-stack';

const app = new cdk.App();
new MinecraftStack(app, 'MinecraftStack', {
  env: {
    account: 'YOUR_AWS_ACCOUNT',
    region: 'ap-northeast-1'
  }
});

なお、ハードコーディングしないデプロイ先の指定方法は下記の記事にまとめていますのでご参照ください。

そしてlib/minecraft-stack.tsは下記のように定義します。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as fs from 'fs';

export class MinecraftStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // デフォルトVPCの取得
    const vpc = ec2.Vpc.fromLookup(this, 'VPC', {
      isDefault: true,
    });

    // セキュリティグループの作成
    const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', {
      vpc,
      securityGroupName: 'Minecraft Security Group',
      description: 'Security group with ports open for Minecraft Server & SSH.',
      allowAllOutbound: true
    });

    // SSHとカスタムポートのルールを追加(東京リージョンを想定)
    securityGroup.addIngressRule(ec2.Peer.ipv4('3.112.23.0/29'), ec2.Port.tcp(22), 'ap-northeast-1 EC2 Instance Connect');
    securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(25565), 'Allow Minecraft connections');

    // キーペアの作成
    const keyPair = new ec2.KeyPair(this, 'KeyPair', {
      keyPairName: 'MinecraftKey',
      type: ec2.KeyPairType.RSA
    })

    // ユーザデータファイルの読み込み
    const userDataScript = fs.readFileSync('./lib/userdata.sh', 'utf8');

    // EC2インスタンスを作成
    const instance = new ec2.Instance(this, 'Instance', {
      vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PUBLIC
      },
      instanceType: new ec2.InstanceType('t4g.small'),
      machineImage: new ec2.AmazonLinuxImage({
        generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023,
        cpuType: ec2.AmazonLinuxCpuType.ARM_64
      }),
      securityGroup: securityGroup,
      keyPair: keyPair,
      userData: ec2.UserData.custom(userDataScript),
    });

    // EIPをインスタンスに割り当て
    const eip = new ec2.CfnEIP(this, 'EIP', {
      instanceId: instance.instanceId,
    });
  }
}

また、lib配下にはuserdata.shを追加で作成します。内容はブログ記事からそのまま転載しております。

#!/bin/bash

# *** INSERT SERVER DOWNLOAD URL BELOW ***
# Do not add any spaces between your link and the "=", otherwise it won't work. EG: MINECRAFTSERVERURL=https://urlexample


MINECRAFTSERVERURL=https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar


# Download Java
sudo yum install -y java-17-amazon-corretto-headless
# Install MC Java server in a directory we create
adduser minecraft
mkdir /opt/minecraft/
mkdir /opt/minecraft/server/
cd /opt/minecraft/server

# Download server jar file from Minecraft official website
wget $MINECRAFTSERVERURL

# Generate Minecraft server files and create script
chown -R minecraft:minecraft /opt/minecraft/
java -Xmx1300M -Xms1300M -jar server.jar nogui
sleep 40
sed -i 's/false/true/p' eula.txt
touch start
printf '#!/bin/bash\njava -Xmx1300M -Xms1300M -jar server.jar nogui\n' >> start
chmod +x start
sleep 1
touch stop
printf '#!/bin/bash\nkill -9 $(ps -ef | pgrep -f "java")' >> stop
chmod +x stop
sleep 1

# Create SystemD Script to run Minecraft server jar on reboot
cd /etc/systemd/system/
touch minecraft.service
printf '[Unit]\nDescription=Minecraft Server on start up\nWants=network-online.target\n[Service]\nUser=minecraft\nWorkingDirectory=/opt/minecraft/server\nExecStart=/opt/minecraft/server/start\nStandardInput=null\n[Install]\nWantedBy=multi-user.target' >> minecraft.service
sudo systemctl daemon-reload
sudo systemctl enable minecraft.service
sudo systemctl start minecraft.service

# End script

CDKコードが完成したら、minecraftディレクトリにて下記のコマンドを実行します。

$ cdk diff
$ cdk deploy

問題なくデプロイされたら、マネジメントコンソール上でインスタンスを確認します。

次に、作成されたEC2インスタンスへ、EC2 Instance Connectを使って接続します。

下記の画面が出ればリソースが正しく作成されています。

最後に、ブログ記事にあるコマンドでサーバの停止と起動を実行して、オペレータを追加してみます。
まずはサーバの停止と起動です。起動には多少時間がかかります。

[ec2-user@ip-172-31-44-196 ~]$ cd /opt/minecraft/server/
[ec2-user@ip-172-31-44-196 server]$ sudo ./stop
[ec2-user@ip-172-31-44-196 server]$ sudo ./start
Starting net.minecraft.server.Main
[23:15:59] [ServerMain/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]
[23:16:01] [ServerMain/INFO]: Loaded 7 recipes
[23:16:02] [ServerMain/INFO]: Loaded 1271 advancements
[23:16:03] [Server thread/INFO]: Starting minecraft server version 1.20.4
[23:16:03] [Server thread/INFO]: Loading properties
[23:16:03] [Server thread/INFO]: Default game type: SURVIVAL
[23:16:03] [Server thread/INFO]: Generating keypair
[23:16:03] [Server thread/INFO]: Starting Minecraft server on *:25565
[23:16:03] [Server thread/INFO]: Using epoll channel type
[23:16:03] [Server thread/INFO]: Preparing level "world"
[23:16:11] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:16] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:17] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:17] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[23:16:18] [Server thread/INFO]: Time elapsed: 6650 ms
[23:16:18] [Server thread/INFO]: Done (14.870s)! For help, type "help"

Doneが表示されたら、Minecraft コマンドが実行できるようになりますので、プレイヤーをオペレータとして追加します。

$ op sugawara

以上でブログ記事の内容を終わっていますが、ライセンスをお持ちの方はJava版のMinecraftを起動し、[サーバを追加]を選択することで、構築したEC2インスタンスを利用したMinecraftがプレイできるようです。今回はあくまでサーバのセットアップまでとなります。

最後に後片付けとして、下記のコマンドもしくはCloudFormationの画面からスタックの削除をしてください。

$ cdk destroy

おわりに

今回はブログ記事の通り、MinecraftサーバのセットアップまでをCDK化してみました。

このブログのためだけにライセンス購入はしなかったため、実際にMinecraftの表示はしていません。興味がある人はライセンス購入をした上でやってみてください!

この記事をシェアする
著者:sugawara
元高校英語教員。2023 Japan AWS All Certifications Engineers。IaCやCI/CD、Platform Engineeringに興味あり。