はじめに
最近、友人と「7 Days to Die」というゾンビサバイバルゲームにハマっていて、休日の夜な夜な集まってプレイしています。
これまでは自宅PCでサーバーを立てて友人を招待していたのですが、
- マシンスペックの限界(自分のPCが重くなる)
- 自分がPCを起動していないと誰も遊べない
という2つの壁にぶつかっていました。せっかくなら勉強も兼ねて、AWS EC2にサーバーを移してみることにしました。
環境要件
- 1人〜6人で遊べる程度のスペック
- 誰でも(AWSアカウントを持っていない友人でも)簡単に起動・停止できる
全体像
インフラはすべてTerraformで管理し、大きく3つのパーツで構成しています。
-
EC2: 7 Days to Dieのサーバー本体。
m6i.xlarge(4vCPU/16GB)でUbuntu 24.04を使用 - Lambda: Discordでログインした友人だけが押せる起動・停止ボタン
- EventBridge Scheduler: 消し忘れ対策として毎朝6時に強制停止する保険
CDKやCloudFormationは使わず、Terraformの素の.tfファイルのみ、しかもVPCも新規に作らずアカウントのデフォルトVPCをそのまま使っています。個人のホームラボ用途でネットワーク設計に凝っても得るものが少ないと判断し、あえてシンプルに倒しました。
ゲームサーバー本体の構築
EC2のuser_data(初回起動時に自動実行されるスクリプト)で、SteamCMDのインストールからゲームサーバーの起動まで全自動にしています。
# ライセンス同意を事前回答して無人インストール
echo steam steam/question select "I AGREE" | debconf-set-selections
echo steam steam/license note "" | debconf-set-selections
apt-get install -y steamcmd
# 7DTDサーバー本体(App ID: 294420)
sudo -u ubuntu /usr/games/steamcmd \
+force_install_dir /home/ubuntu/7dtd \
+login anonymous \
+app_update 294420 validate \
+quit
プロセス管理はsystemdに任せています。
「起動する度に最新版へ」への変更
最初はサーバー作成時(user_data実行時)にだけapp_updateを1回叩く形でした。しかしこれだと、Terraformでサーバーを作り直さない限りゲーム本体が更新されません。7 Days to DieはBeta版でアップデート頻度が高いゲームなのと、サーバーのバージョンとユーザーのsteamのバージョンが一致しない場合はゲームをすることが出来ません。
そこで、systemdのExecStartPreにSteamCMDの更新コマンドを移動し、サービスが起動するたびに(=EC2が起動するたびに)最新版を検証・更新してからサーバーを立ち上げる方式に変更しました。
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/7dtd
ExecStartPre=/usr/games/steamcmd +force_install_dir /home/ubuntu/7dtd +login anonymous +app_update 294420 validate +quit
ExecStart=/home/ubuntu/7dtd/startserver.sh -configfile=serverconfig.xml
Restart=on-failure
RestartSec=15
TimeoutStartSec=1800
更新チェック自体に数分かかることがあるためTimeoutStartSec=1800(30分)を設定し、After=network-online.target / Wants=network-online.targetでネットワークが確実に立ち上がってからSteamCMDが動くようにしています。別途アップデート用のcronやスクリプトを持たずに済むのが利点です。
「誰でも起動・停止できる」
Discord OAuth2でログインし、特定のDiscordサーバー(ギルド)のメンバーだけが使えるWeb操作パネルをLambda + 関数URLで作ることにしました。
- API GatewayではなくLambda Function URLを直接公開(認証はLambda内部のDiscordログインで行うため、関数URL自体は
authorization_type = "NONE")
resource "aws_iam_role_policy" "control_panel" {
policy = jsonencode({
Statement = [
{
Effect = "Allow"
Action = ["ec2:StartInstances", "ec2:StopInstances"]
Resource = "arn:aws:ec2:ap-northeast-1:${data.aws_caller_identity.current.account_id}:instance/${aws_instance.server.id}"
}
]
})
}
お金の話
土日祝の夜、最大でも4時間程度しか遊ばない使い方なので、サーバーの停止し忘れを防ぐようにしました。
- 毎日6:00(JST)のEventBridge Scheduler強制停止
- 手動停止(Web操作パネルのStopボタン)
resource "aws_scheduler_schedule" "daily_shutdown" {
schedule_expression = "cron(0 6 * * ? *)"
schedule_expression_timezone = "Asia/Tokyo"
target {
arn = "arn:aws:scheduler:::aws-sdk:ec2:stopInstances"
role_arn = aws_iam_role.scheduler.arn
input = jsonencode({ InstanceIds = [aws_instance.server.id] })
}
}
すでに停止しているインスタンスに対してこのスケジューラが発火しても何も起きないので、安全に二重化できています。
見積もりは月2,500円前後(稼働40時間+EBS+固定IP)としていて、AWS Budgetsで$30/月のアラートも設定しています。
発展編(今後やりたいこと)
- 15分間ゲームサーバーに人がいなかった場合に自動停止する
- シャットダウン前のワールドデータS3自動バックアップ
- 7days用サーバー構築MCP
- サーバーのスケールアップの自動化
まとめ
初めてAWSのEC2を立てて見たのですが、AWSのコンソール画面でEC2が立ったときは感動しました。
これからもできるだけ安く運用(目指せ0円!)出来るように改良重ねたいと思います!
- もっと他の記事も読んでみたい方
- 当社に興味がある方はこちら👀
- 当社のサービスに興味がある方はこちら👀

