はじめに
最近Minecraftなどのマルチサーバーをレンタルできるというサービスをしばしば目にします。
ただ少し高く、無料や安価なプランだとラグがひどいということがあるのでAWS EC2で必要最小限の要素だけつけて、安価でMinecraftサーバーを構築してみようと思います。
ローカルでサーバー構築すれば良いじゃんという意見もあるかと思いますが、めんどくさい、ポート解放できない、PCのスペックが足りない、PCつけっぱなしにしたくない、などローカルでは出来ない・やりたくない理由もあるのでパブリッククラウド上に構築するのは選択肢の1つになり得ると思います。
AWSでMinecraftサーバーを構築するという記事はいくつかありましたが、CloudFormation で行う記事がなかなかなかったため、CloudFormationで作成する方法を記事としてまとめようと思います。
AWSアカウントの登録、作業用のIAMロール作成の手順は本記事の中では取り扱いません。
リソースを作成すると料金が発生するためご注意ください。
1分でわかるCloudFormation
CloudFormation はIaC(Infrastructure as Code)と呼ばれる技術で、コードからAWSリソースを作成することが出来ます。
他に有名なIaCとしてTerraformがありますが、CloudFormationはAWSが提供していることもあり、AWSに特化しています。
CloudFormationテンプレート
Minecraft-Server-Base.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Building a Minecraft Server Base
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: minecraft-server-vpc
IGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: minecraft-server-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref IGW
PubSub:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: minecraft-server-subnet
PubSubRT:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: minecraft-server-rt
PubSubToInternet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PubSubRT
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
AssociatePubSubToRT:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PubSub
RouteTableId: !Ref PubSubRT
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: minecraft-server-sg
GroupDescription: Allow Minecraft Port
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 25565
ToPort: 25565
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: minecraft-server-sg
MinecraftServerAccessRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Tags:
- Key: Name
Value: minecraft-server-iam-role
Outputs:
PubSub:
Description: Public Subnet
Value: !Ref PubSub
Export:
Name: "minecraft-server-public-subnet"
SecurityGroup:
Description: EC2 Security Group
Value: !Ref SecurityGroup
Export:
Name: "minecraft-server-ec2-security-group"
MinecraftServerAccessRole:
Description: Session Mangager Access Role
Value: !Ref MinecraftServerAccessRole
Export:
Name: "minecraft-server-iam-role"
Minecraft-Server-EC2.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Building a Minecraft Server
Parameters:
MinecraftServerUrl:
Type: String
Default: https://piston-data.mojang.com/v1/objects/84194a2f286ef7c14ed7ce0090dba59902951553/server.jar
Description: Minecraft Server Download URL
JavaVersion:
Type: String
Default: 17
AllowedValues:
- 11
- 16
- 17
- 21
Description: |
Java version
~1.16: Java11
1.17~: Java16
1.18~: Java17
1.20.5~: Java21
MemorySize:
Type: String
Default: 1300M
Description: Server Memory Size
Resources:
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: "/"
Roles:
- !ImportValue "minecraft-server-iam-role"
EC2:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ad215c298e692194
InstanceType: t4g.small
IamInstanceProfile: !Ref InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !ImportValue "minecraft-server-public-subnet"
GroupSet:
- !ImportValue "minecraft-server-ec2-security-group"
UserData:
"Fn::Base64": !Sub |
#!/bin/bash
sudo dnf install -y java-${JavaVersion}-amazon-corretto-headless
sudo adduser minecraft
sudo mkdir /opt/minecraft/
sudo mkdir /opt/minecraft/server/
cd /opt/minecraft/server
sudo wget ${MinecraftServerUrl}
sudo chown -R minecraft:minecraft /opt/minecraft/
sudo java -Xmx1300M -Xms1300M -jar server.jar nogui
sleep 40
sed -i 's/false/true/p' eula.txt
touch start
printf '#!/bin/bash\njava -Xmx${MemorySize} -Xms${MemorySize} -jar server.jar nogui\n' | sudo tee -a start
sudo chmod +x start
sleep 1
sudo touch stop
printf '#!/bin/bash\nkill -9 $(ps -ef | pgrep -f "java")' | sudo tee -a stop
sudo chmod +x stop
sleep 1
cd /etc/systemd/system/
sudo 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' | sudo tee -a minecraft.service
sudo systemctl daemon-reload
sudo systemctl enable minecraft.service
sudo systemctl start minecraft.service
Tags:
- Key: Name
Value: minecraft-server-instance
作成するAWSリソース
VPC
デフォルトのVPCを利用しても良いですが、削除している場合もあると思うので新たに作成しています。
RFC1918に従ってCidrを設定します。
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: minecraft-server-vpc
サブネット
Minecraftサーバーを構築する場合、外部に公開されていないと一緒に遊ぶユーザー、それどころか自分ですらサーバーに参加できないのでパブリックサブネットを利用します。
PubSub:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: minecraft-server-subnet
インターネットゲートウェイ
サブネットを作成しただけではインターネットに接続できないので、インターネットに接続するためにインターネットゲートウェイを作成し、サブネットにアタッチする必要があります。
IGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: minecraft-server-igw
ルートテーブル
リクエストを制御するためにルートテーブルを作成し、ルートを定義します。
PubSubRT:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: minecraft-server-rt
セキュリティグループ
必要な通信以外を行わないようにセキュリティグループを作成します。
Minecraftのポート番号は25565のため、そのポートはインバウンド通信を受け入れてあげる必要があります。
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: minecraft-server-sg
GroupDescription: Allow Minecraft Port
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 25565
ToPort: 25565
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: minecraft-server-sg
EC2インスタンス
メインとなるサーバーを動作させるEC2インスタンスです。
OSはAmazon Linux 2023を利用し、インスタンスタイプはt4g.smallを選択します。
今回は大人数で遊ぶわけではなかったため、最小限のスペックにしています。実際に動作しせてみて、重いようであればインスタンスタイプを変更すると正常に動作するようになるかもしれません。
MinecraftサーバーはJavaで動作するためJavaをダウンロードする必要があります。
UserDataにサーバー構築用コマンドを設定することで、インスタンス内部で作業せずにすぐに遊ぶことができます。
EC2:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ad215c298e692194
InstanceType: t4g.small
IamInstanceProfile: !Ref InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !ImportValue "minecraft-server-public-subnet"
GroupSet:
- !ImportValue "minecraft-server-ec2-security-group"
UserData:
"Fn::Base64": !Sub |
#!/bin/bash
sudo dnf install -y java-${JavaVersion}-amazon-corretto-headless
sudo adduser minecraft
sudo mkdir /opt/minecraft/
sudo mkdir /opt/minecraft/server/
cd /opt/minecraft/server
sudo wget ${MinecraftServerUrl}
sudo chown -R minecraft:minecraft /opt/minecraft/
sudo java -Xmx1300M -Xms1300M -jar server.jar nogui
sleep 40
sed -i 's/false/true/p' eula.txt
touch start
printf '#!/bin/bash\njava -Xmx${MemorySize} -Xms${MemorySize} -jar server.jar nogui\n' | sudo tee -a start
sudo chmod +x start
sleep 1
sudo touch stop
printf '#!/bin/bash\nkill -9 $(ps -ef | pgrep -f "java")' | sudo tee -a stop
sudo chmod +x stop
sleep 1
cd /etc/systemd/system/
sudo 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' | sudo tee -a minecraft.service
sudo systemctl daemon-reload
sudo systemctl enable minecraft.service
sudo systemctl start minecraft.service
Tags:
- Key: Name
Value: minecraft-server-instance
IAM ロール
EC2インスタンスで作業をするために今回はセッションマネージャーを利用しますが、そのためにはAmazonSSMManagedInstanceCore
ポリシーが必要なためIAM ロールにポリシーを付与し、そのIAMロールをEC2インスタンスにアタッチする必要があります。
MinecraftServerAccessRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Tags:
- Key: Name
Value: minecraft-server-iam-role
料金について
実際に私が利用してみたところ2人で月に8回ほど4時間遊んで2ドル弱でした。
現在は円安のため相対的に高くなってしまいますが、かなり安価に利用できます!
Volumeを利用している限り、EC2を起動していなくても料金が発生してしまいますが、Minecraftのデータは全て一つのフォルダに格納されるため、そのフォルダさえS3など、どこか別の場所に保存しておけば、EC2を終了してVolumeを削除してしまっても問題ありません。その場合遊ぶたびにCloudFormationを流す必要がありますが...
ついでに
ずっとサーバーをつけっぱなしにしておくのは料金がかかってしまうので、停止し忘れた時も自動でサーバーを停止するようにスケジューラーを利用することをお勧めします。
AWSの場合EventBridge Schedularを利用するとスケジュール機能を実装することができます。
以下のCloudFormationテンプレートを利用すると毎日午前3時にインスタンスを停止するスケジューラを作成することができます。
Minecraft-Server-Schedular.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Scheduler to stop the Minecraft Server
Parameters:
InstanceId:
Type: AWS::EC2::Instance::Id
Description: EC2 Instance ID to be stopped
ScheduleStopTime:
Type: String
Default: "cron(0 3 * * ? *)"
Description: Time to stop the EC2 Instance
ScheduleTimezone:
Type: String
Default: Asia/Tokyo
Description: Schedule timezone
Resources:
SchedulerToStopEC2:
Type: AWS::Scheduler::Schedule
Properties:
Name: "minecraft-server-stop-scheduler"
FlexibleTimeWindow:
Mode: "OFF"
ScheduleExpression: !Ref ScheduleStopTime
ScheduleExpressionTimezone: !Ref ScheduleTimezone
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ec2:stopInstances
Input: !Sub |-
{
"InstanceIds": ["${InstanceId}"]
}
RoleArn:
Fn::GetAtt:
- SchedulerToStopEC2Role
- Arn
SchedulerToStopEC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: EC2Stop
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:StopInstances
Resource:
- "*"
使い方
-
こちらのサイトで遊びたいバージョンのサーバーのダウンロードリンクを取得する
-
AWSにサインインする
-
リージョンを自分の住んでいる地域の近くにする
-
CloudFormationの画面を開く
-
CloudFormationテンプレートを流してスタックを作成する
- スタックの作成>新しいリソースを使用(標準)を押下
- テンプレートのアップロード>ファイルの選択を押下し、以下のCloudFormationテンプレートをアップロード
-
Minecraft-Server-Base.yml
- Parameters
- なし
- Parameters
-
Minecraft-Server-EC2.yml
- Parameters
- MinecraftServerUrl: 手順1で取得したダウンロードリンク
- JavaVersion: Minecraftのバージョンに合わせたJavaバージョン
- ~1.16: Java11
- 1.17~: Java16
- 1.18~: Java17
- 1.20.5~: Java21
- MemorySize: 大人数でなければデフォルト
- Parameters
-
- スタックオプションはデフォルトで次に進む
-
AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。
にチェックを入れて送信ボタンを押下
- 注意点
- IAMを変更するため確認画面でチェックをつける必要がある
- スタックを作成する順番を逆にするとうまくいかないため注意
- インスタンス起動のタイミングでEULAに自動的に同意するため注意
-
MinecraftでサーバーIPの欄にパブリックIPv4アドレスをつけてサーバーに接続する
- EC2インスタンスが作成された後もインスタンス内部で設定を行っているため、5分程待ってからアクセスする必要がある
-
遊び終わったらEC2インスタンスを停止する
- 稼働させている限り料金が発生してしまう
- 停止ではなく終了してしまうとデータが消えてしまうので、理由がない限り停止を推奨
備考
- 個人利用のため安価で遊べるように1リージョン1AZに配置している
- 既存のワールドファイルを利用する場合はS3経由でEC2インスタンス上に配置するのが楽
- Amazon Linux2023には最初からAWS CLIが導入されているので、cli経由でS3のファイルを簡単にEC2インスタンス内に配置可能
- サーバーの操作はセッションマネージャーでEC2インスタンスに接続して行う
- Roleを作成する必要がある
- AmazonSSMManagedInstanceCoreポリシー
- Amazon Linux 2023には最初からエージェントがインストールされている
- 特に料金はかからない
- Roleを作成する必要がある
- バックアップを取得する設定などはしていないので注意
- コストを抑える目的で特にIPを固定していないため、インスタンスを再起動するたびにIPが変更される
おわりに
私が中学生の時にMinecraftにめちゃめちゃハマっていたのですが、そのころはエンジニアになるなんて思ってもいませんでした。
その頃を思うと感慨深いものがあります。
参考