0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【EC2】Minecraftのサーバ運用【統合版】

0
Last updated at Posted at 2025-09-30

概要

マインクラフト統合版をローコストでサーバー運用する。(2025/10/01現在)
以下の順で解説

  1. 背景
  2. 料金
  3. 参考記事
  4. 環境
  5. 環境選定
  6. 運用サーバインスタンスの作成
  7. サーバインスタンスにログイン
  8. IAMロールの作成
  9. インスタンス自動起動用関数URL作成
  10. マイクラのお試し起動
  11. 自動シャットダウン用クーロン作成
  12. Tips
  13. おまけ

背景

マインクラフト公式でもrealmsをつかってサーバを立てられるけど、~2人で3.99ドル/月、~10人で7.99ドルと結構高いので、サーバを作ることにした。

料金

結論から言うと、課金詳細は以下になった

項目 課金詳細 1ヶ月料金ドル(1週間で26時間遊んだ場合)
EC2 $0.0304 per on demand instance hour 3.2832
EBS $0.096 per GB-month 0.288※
IP $0.005 per In-use 0.56
合計 3.9696

※ 2週間くらい運用して容量3GB未満なので、一旦EBSは3GBとする

1日4時間弱までであれば、realmsの3.99ドル/月より安く済みそう

details

image2.png
image.png

参考記事

ソースコード

今回使用したソースコードは以下においてます
https://github.com/keiich/mincraft_operation
(2025/11/03現在 マインクラフトの自動アップデート機能も追加しました)

環境

項目 詳細
クライアントPC windows11
クラウド環境 AWS EC2
マインクラフトサーバ 統合版 (Bedrock Edition)
EC2インスタンスOS Ubuntu

環境選定

クラウドサーバ

ロリポップfor gamersや、シンクラウドなどいろいろあったが、以下の理由でec2を選んだ。

  1. realmsより安く済ます
    1. これが一番大きい理由
  2. 従量課金制
    1. 後述するが、ログインユーザが誰もいない場合は自動でサーバを落とすようにしてサーバ代を節約する

マインクラフトサーバ

java版で作っている記事が多かったので最初はjava版で構築しようとしていたが、
統合版だとPC以外でも遊べるので統合版にした。

image.png

EC2インスタンスOS

最初はよく使われているaws linux 2023を使おうとしたが、
統合版だとBedrock Editionのzipファイルを解凍して、実行時に以下のように、使っているLinux環境のライブラリのバージョンが、bedrock_serverが要求するものより古い
エラーが発生したため、ubuntuにした。

./bedrock_server: /lib64/libcurl.so.4: no version information available (required by ./bedrock_server) 
./bedrock_server: /lib64/libm.so.6: version GLIBC_2.35' not found (required by ./bedrock_server)

運用サーバインスタンスの作成

まずはawsにログイン(「コンソールにサインイン」)。アカウントがなければ右上の「アカウントを作成」から。ここでクレカなどの登録が必要になる。
https://aws.amazon.com/jp/
サインインしたらヘッダーにて「アジアパシフィック(東京)」を指定 → 検索窓でec2と検索
でてきたダッシュボードから「インスタンスを起動」
※ここで「アジアパシフィック(東京)」を指定することを忘れない。ラムダ関数を制作するときにも同じ領域を選ぶことになるため。
image.png

設定項目

項目 詳細 備考
名前 適当
Amazon マシンイメージ (AMI) Ubuntu Server 24.04 LTS (HVM), SSD Volume Type
インスタンスタイプ t2.small 後ほどスケール変更可能(なはず)
キーペア名 新しいキーペアの作成を押下 (後述)
ネットワーク設定 (後述)

それ以外はデフォルト

キーペア名

新しいキーペアの作成を押下したら、以下を選ぶ

キーペア名: 任意
キーペアのタイプ: RSA
プライベートキーファイル形式: .pem

キーペアを作成したらローカルPCにダウンロードされるので、ダウンロード先を把握しておく

ネットワーク設定

右の編集ボタンを押下

インバウンドセキュリティグループのルール
  • ローカルPCからのアクセス
項目 値 備考
タイプ ssh
ソースタイプ 自分のIP ローカルPCのIPアドレスが勝手に追加される
  • マインクタラフトサーバからのアクセス
    「セキュリティグループを追加」を2回押下して以下2つの設定も追加
項目 値 備考
タイプ カスタムUDP
ポート範囲 19132 IPV4用
ソースタイプ 任意の場所
項目 値 備考
タイプ カスタムUDP
ポート範囲 19133 IPV6用
ソースタイプ 任意の場所

それ以外はデフォルトの設定。
最後に「インスタンスを起動」を押下すると、インスタンスが立ち上がる。

サーバインスタンスにログイン

linux系のコマンドが使えるため、自分はgit bashをインストールして作業した。
以下のリンクからdownloadできる。
https://gitforwindows.org/

pemファイルを用意

インスタンス作成時に作ったキーペア(pemファイル)を、

 ~/.ssh/[任意の名前].pem 

に保存する。
ファイルの中身は以下のような秘密鍵形式。

-----BEGIN RSA PRIVATE KEY-----
文字列
-----END RSA PRIVATE KEY-----

保存手順は以下参考

# ダウンロード先から、.sshディレクトリの中へコピー
cp /c/Users/[ユーザ名]/Downloads/[ファイル名(my-key-pair)].pem ~/.ssh/
# pemファイルの権限を変更
 chmod 400  ~/.ssh/[ファイル名].pem

以下コマンドでログイン
(例)

ssh -i ~/.ssh/[ファイル名].pem ubuntu@52.194.221.92

ここで、

1 2
ubuntu AMI 名(基本的にこの名前になる)
52.194.221.92 パブリック IPv4 アドレス(例)

である。

それぞれ、EC2→リソースのインスタンス(実行中)→インスタンスの状態=running のフィルターを外す
で出てきたインスタンスIDを押下することで確認できる。
中に入ると、以下のようになる。
(例)

ubuntu@ip-52.194.221.92:~$

IAMロールの作成

これがないと、以下で作成する関数URLでEC2の操作ができない。

  1. awsの検索窓でiamと検索してでてきたiamセクションを押下
  2. 左の一覧の「アクセス管理」→「ロール」を押下
  3. 右上の「ロールを作成」を押下
  4. 「サービスまたはユースケース」でLambdaを選んで「次へ」
    image.png
  5. 許可ポリシーに以下2つ選んで次へ
項目 説明
AmazonAPIGatewayPushToCloudWatchLogs lambdaの成功や失敗ログをcloudwatchのuiで確認できるようにする(いらないかも)
AWSQuickSetupStartStopInstancesExecutionPolicy Lambda関数がEC2を起動・終了できる権限
  1. 「ロール名」に適当に名前を入れて、「ロールを作成」

インスタンス自動起動用関数URL作成

次にインスタンスを自動起動させるための関数URLを作成します。

  1. awsの検索窓で「lambda」と入力、候補で出てきた「lambda」を押下
  2. 右上の「関数を作成」を押下
  3. 以下項目を設定し、再度「関数を作成」を押下
項目 詳細 備考
関数名 任意の名前
ランタイム Python 3.12 最新のバージョンでok
アーキテクチャ デフォルト x86_64
実行ロール 先ほど作ったロールを選択 「デフォルトの実行ロールの変更」→「既存のロールを使用する」を押下
関数URLを有効化 チェックする 「その他の構成」を押下。認証タイプは「NONE」※1

※1 誰でもURLを押せるようになるので、そうしたくない場合は別途調べてください。自分は友達を信じて公開中。

インスタンスが出来上がったら、「コード」のコードソース画面で以下を書き込む

import boto3
import time
import requests

def lambda_handler(event, context):
    path = event.get("rawPath", "")
    if path.endswith("favicon.ico"):
        return {
            "statusCode": 204,
            "body": ""  # 何もしない
        }
    # EC2インスタンスID
    instance_id = '[インスタンスID]'

    # EC2インスタンスを起動するためのパラメータ
    ec2 = boto3.client('ec2')

    # まず現在の状態を確認
    instance = ec2.describe_instances(InstanceIds=[instance_id])
    state = instance['Reservations'][0]['Instances'][0]['State']['Name']
    webhook_url = '[投稿したいdiscordチャンネルのwebhook_url]'

    if state == 'running':
        # すでに起動している場合
        public_ip = instance['Reservations'][0]['Instances'][0].get('PublicIpAddress', '未割り当て')
        data = {
            'content': f'インスタンスはすでに起動中です。\rサーバーアドレス:{public_ip}'
        }
        requests.post(webhook_url, json=data)
        return public_ip

    else:
        response = ec2.start_instances(InstanceIds=[instance_id])

        # EC2インスタンスが起動するのを待つ
        instance_running = False
        while not instance_running:
            instance = ec2.describe_instances(InstanceIds=[instance_id])
            state = instance['Reservations'][0]['Instances'][0]['State']['Name']
            if state == 'running':
                instance_running = True
            else:
                time.sleep(5)

        # EC2インスタンスのパブリックIPアドレスを取得する
        instance = ec2.describe_instances(InstanceIds=[instance_id])
        public_ip = instance['Reservations'][0]['Instances'][0]['PublicIpAddress']
    
        #DiscordにWebhookを送信
        data = {
            'content': f'インスタンスは起動しました\rサーバーアドレス:{public_ip}'
        }
        requests.post(webhook_url, json=data)
    
        return public_ip

ここで、インスタンスID、webhook urlはそれぞれ

項目 詳細
インスタンスID EC2→リソースのインスタンスを押下して出てきたID
webhook_url※ こちらで取得:https://zenn.dev/discorders/articles/discord-webhook-guide

である

※このurlを取得することで特定のdiscordチャンネルに通知できます。通知が不要な場合はDiscordにWebhookを送信しているコードをコメントアウトでOK

コード解説

Favorite Icon取得用パスの場合は処理をスキップする

関数URLが実行されると、ほとんどの端末で

  • タブのアイコンを取得(favicon)
  • 本実行

というように同じルートパスで2回に分けてリクエストが送られる。
よって、pathの最後がfavicon.icoで終わるリクエストは無視するようにする

現在のインスタンスの状態を確認

a. すでに起動している場合

  • サーバーアドレスと、起動している旨をdiscordに送信、クリックしたリンク先でもサーバーアドレスを返す

b. 起動していない場合

  • インスタンスが起動するまでsleep(5秒)をループさせて待つ。
  • 起動したら、サーバーアドレスと起動している旨をdiscordに送信、クリックしたリンク先でもサーバーアドレスを返す

レイヤーの選択

上記のコードでimportしているrequestsモジュールを使えるようにするために、レイヤーを追加する。

  1. 一番下までスクロールして「レイヤーを追加」を押下
  2. ARNを指定をを選択
  3. 「arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p312-requests:17」をコピペで張り付け
  4. 追加を押下

※レイヤーはこちら から参照する
リンクを押下したら、「ap-northeast-1 Asia Pacific (Tokyo)」のhtmlのリンクを押下、すると「arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p312-requests:17」がでてくる。

タイムアウトの設定

「設定」タブ→一般設定→編集から、
タイムアウトを3分に設定して、「保存」

サーバー起動

設定タブ→関数URLから「https://********.lambda-url.ap-northeast-1.on.aws/」を押下
数秒してサーバーアドレスが表示されればOK
image.png
discordへの投稿設定をしていれば以下投稿される
image.png

サーバを起動させるたびにパブリックIPが変化する。IPを固定させることもできるが、
保持するのに費用がかかる可能性があるため、サーバ停止するたびにIPを破棄する方針をとる

マイクラ お試し起動

以下コードと説明

mkdir mincraft;cd mincraft

# https://www.minecraft.net/ja-jp/download/server/bedrockから
# Ubuntuのダウンロードボタンを右クリック、リンクのコピーで取得
wget https://www.minecraft.net/bedrockdedicatedserver/bin-linux/bedrock-server-1.21.102.1.zip
sudo apt install unzip
unzip bedrock-server-1.21.102.1.zip
chmod +x bedrock_server
## 一番下に****を入力
vim server.properties

## お試し起動
LD_LIBRARY_PATH=. ./bedrock_server
# 「stop」でマイクラサーバを止める

マイクラサーバの細かな設定は、
minecraft/server.propertiesファイルを直接いじることで設定できる。設定後はマイクラサーバのリロードで反映
以下参照
https://hikicomoron.net/minecraft_server_properties_be
(難易度はデフォルトでeasyになっている)

マイクラ デーモン起動

EC2サーバが立ち上がったらマイクラサーバが勝手に立ち上がるようにする
/etc/systemd/system/minecraft.service ファイルをvimで編集

[Unit]
Description=Minecraft Bedrock Server
After=network-online.target

[Service]
WorkingDirectory=/home/ubuntu/minecraft
User=ubuntu

# ExecStartPre/ExecStartはサーバ起動時のコマンド

# キャッシュが残っていると、「スキンが壊れています」と表示され
# サーバーに入れない可能性があるため、
# 起動前に premium_cache をリネームしてバックアップ
ExecStartPre=/bin/bash -c 'if [ -d premium_cache ]; then mv premium_cache premium_cache_backup_$(date +%%Y%%m%%d_%%H%%M%%S); fi'

# ログファイル名を現在時刻をサフィックスにし、ファイル名をcurrent_logfile.txtに保存。(後ほど使うため)
# screenコマンドでマイクラサーバをバックグラウンド実行。
# (できればtmuxのほうがいいらしいが、デフォルトで入っているscreenにした)
ExecStart=/usr/bin/screen -DmS minecraft bash -c 'logfile="bedrock_server_$(date +%%Y-%%m-%%d_%%H).log"; echo $logfile > /home/ubuntu/minecraft/current_logfile.txt; ./bedrock_server >> $logfile 2>&1'

# サーバリロード時のコマンド
ExecReload=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff "reload"\\015'

# サーバ停止時のコマンド
## マインクラフトは基本的に3分おきに自動保存されているが、タイミングによってはサーバ停止時にデータが保存されない可能性もあり得るので、以下コマンドで安全に保存する
ExecStop=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff "say Server Shutdown. Saving map..."\\015'
ExecStop=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff "save hold"\\015'
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff "save resume"\\015'
ExecStop=/bin/sleep 2
ExecStop=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff "stop"\\015'
Restart=on-failure
RestartSec=60s

[Install]
WantedBy=network-online.target

※マイクラサーバ起動時に書き込まれるbedrock_server_$(date +%%Y-%%m-%%d_%%H).logには、ユーザのログイン、ログアウトタイミングが書き込まれる

ログファイルの中身は以下のような感じ

details
[2025-10-09 11:35:37:848 INFO] Starting Server
[2025-10-09 11:35:37:848 INFO] Version: 1.21.111.1
[2025-10-09 11:35:37:848 INFO] Session ID: cb6a695c-6ef1-4a13-9666-ab985ae5abaa
[2025-10-09 11:35:37:848 INFO] Build ID: 37061739
[2025-10-09 11:35:37:848 INFO] Branch: r/21_u11
[2025-10-09 11:35:37:848 INFO] Commit ID: 9b62a5d98ee63f3c28bbee06536d7acdf94f00e2
[2025-10-09 11:35:37:848 INFO] Configuration: Publish
[2025-10-09 11:35:37:856 INFO] Level Name: Bedrock level
[2025-10-09 11:35:37:901 INFO] No CDN config file found at: cdn_config.json for dedicated server
[2025-10-09 11:35:37:901 INFO] Game mode: 0 Survival
[2025-10-09 11:35:37:901 INFO] Difficulty: 2 NORMAL
[2025-10-09 11:35:37:923 INFO] Content logging to console is enabled.
[2025-10-09 11:35:41:452 INFO] Opening level 'worlds/Bedrock level/db'
[2025-10-09 11:35:41:701 INFO] [SERVER] Pack Stack - None
[2025-10-09 11:35:43:496 INFO] IPv4 supported, port: 19132: Used for gameplay and LAN discovery
[2025-10-09 11:35:43:496 INFO] IPv6 supported, port: 19133: Used for gameplay
[2025-10-09 11:35:43:543 INFO] Server started.
[2025-10-09 11:35:58:271 INFO] Player connected: userA, xuid: ******
[2025-10-09 11:36:04:453 INFO] Player Spawned: userA xuid: ******, pfid:
[2025-10-09 12:58:21:648 INFO] Player disconnected: userA, xuid: ******, pfid:
[2025-10-09 13:00:02:721 INFO] Saving...
[2025-10-09 13:00:07:432 INFO] Changes to the world are resumed.

デーモンを起動させる

## 新しく作成・編集した .service ファイルを systemd に再読み込みさせる
sudo systemctl daemon-reload
## サーバー起動時(OS起動時)に自動で起動するよう設定
sudo systemctl enable minecraft.service
## すぐにMinecraftサーバーを起動
sudo systemctl start minecraft.service

起動後の状態確認

systemctl status minecraft.service

running になっていればOK
以下コマンドで起動中のデーモンのログを確認できる(マイクラサーバのログではない)

sudo journalctl -u minecraft.service -f

自動シャットダウン用クーロン作成

以下のpythonファイルをcronに登録することで、一定時間おきに実行させ、
ログインユーザがいなかったらEC2サーバごとシャットダウンさせる。

ActiveMonitoring.py
#!/usr/bin/env python3
import datetime
import os
import requests

# current_logfile.txt から実際のログファイル名を読む
logfile_marker = "/home/ubuntu/minecraft/current_logfile.txt"
# 以前設定したdiscordのwebhook_url
webhook_url = '[webhook_url]'
try:
    with open(logfile_marker, "r") as f:
        file_name = f.read().strip()
except FileNotFoundError:
    print("LOG FILE MARKER NOT FOUND!")
    exit(1)

# ログファイルパス
file_path = f"/home/ubuntu/minecraft/{file_name}"

# カウンタ初期化
in_cnt = 0
out_cnt = 0
starting = True
server_start_seconds = None  # サーバー起動時刻(秒)

try:
    with open(file_path, 'r') as f:
        lines = f.readlines()
except FileNotFoundError:
    print("LOG FILE NOT FOUND!")
    exit(1)

for line in lines:
    line = line.strip()
    if not line:
        continue
    if not line.startswith('['):
        continue

    parts = line[1:].split()
    if len(parts) < 2:
        continue

    timestamp_str = parts[1]  # 'hh:mm:ss:ms'
    hms = timestamp_str.split(':')
    if len(hms) < 3:
        continue

    hour, minute, second = int(hms[0]), int(hms[1]), int(hms[2])
    log_seconds = hour * 3600 + minute * 60 + second

    # サーバー起動時刻を Server started. の行から取得
    if server_start_seconds is None and 'Server started.' in line:
        server_start_seconds = log_seconds
        continue

    # プレイヤー接続・切断の判定
    if 'Player connected:' in line:
        in_cnt += 1
    if 'Player disconnected:' in line:
        out_cnt += 1

# 起動15分以内か判定
if server_start_seconds is not None:
    now = datetime.datetime.now()
    now_seconds = now.hour*3600 + now.minute*60 + now.second
    diff = now_seconds - server_start_seconds
    if diff < 0:
        diff += 24*3600  # 日をまたいだ場合
    starting = diff < 15*60
else:
    # Server started. の行が見つからなかった場合
    print("Server start time not found in log.")
    starting = True  # 安全のため起動中扱い

# 判定結果を出力
if starting:
    print('Server is starting... (shutdown disabled for first 15 min)')
elif in_cnt > out_cnt:
    print('Someone is logged in.')
else:
    print('No players online. Shutting down...')
    data = {
        'content': f'ログインユーザがいなくなったため、インスタンスはシャットダウンしました。'
    }
    requests.post(webhook_url, json=data)
    os.system('sudo shutdown -h now')

上記のファイルをクーロンに登録する。
以下は10分おきにログインユーザを確認する例

# 編集
ubuntu@ip-172-31-39-51:~/minecraft$ sudo crontab -e
# 確認
ubuntu@ip-172-31-39-51:~/minecraft$ sudo crontab -l
*/10 * * * * /usr/bin/python3 /home/ubuntu/ActiveMonitoring.py >> /home/ubuntu/cron_error.log 2>&1

以上

Tips

バージョンアップ

  • サーバに置いているマインクラフトのバージョンとユーザが起動するマインクラフトのバージョンを合わせないと起動できない。
    • バージョンアップはbedrockの最新のバージョンをwget
    • minecraft/worldディレクトリさえあれば前のデータを移し替えらえる

座標表示の仕方

minecraft/server.propertiesを編集して以下trueにする

allow-cheats=true

マイクラ内設定変更の権限を与える

# screen起動中のプロセスに入る
screen -r minecraft

# 設定変更権限を与える
op [マイクラユーザID]

# ctrl+a,dで抜け出す

サーバリスタート

sudo systemctl restart minecraft

マイクラのゲーム内に入り、チャットボックスで座標表示コマンドを入力

/gamerule showCoordinates true

表示されたらallow-cheatをfalseに戻しておく

EC2サーバでのsudo実行においてパスワードを求められた時

sudo passwd ubuntu 

でパスワード設定しておく

おまけ

AWS登録するだけでクレジットが140ドルくらいもらえてた(1年の期限付き 20251001現在 )
ので絶対realmsより安い!!!!!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?