GCP上のMinecraft統合版サーバーをdiscordのBotから起動する

GCP上のMinecraft統合版サーバーをdiscordのBotから起動する

この記事では、Google Cloud Platfrom上に設置したMinecraft統合版サーバーを、discordのコマンドから操作する方法を解説します。

この記事で分かること

  • GCP無料枠インスタンスの立て方
  • discord Botの導入
  • コマンドによるインスタンスの起動・停止

参考にしたサイト

この記事の全体の思想は以下の記事から来ています。大変役立ち、よくまとまっているのでお勧めです。

構成図

GCP無料インスタンスを立てる

Google Cloud PlatformにはAlways freeというキャンペーンがあり、無料で使えるインスタンスがあります。現時点では何年でも無料ですので、このサーバーにdiscrodを監視するBotを置き、コマンドが送信されたらプリエンプティブのMinecraftサーバーを起動します。

それでは、この無料インスタンスを作っていきましょう。
無料インスタンスの制限は以下の通り。

項目 詳細
マシンタイプ f1-micro(vCPU×1,0.6GB RAM)
ディスクサイズ 30 GB
OS 無料OS(DebianやUbuntu等)
リージョン アメリカ

この条件内であれば、月744時間、つまり常に無料です。
では、このインスタンスを作っていきましょう。

  1. 前回作成したMinecraftサーバーのあるプロジェクトに移動

  2. Compute Engine>VMインスタンス からインスタンスを作成を選択

  3. 下記画像のようにリージョンとマシンタイプを設定する

    この画像ではus-west1にしています。西海岸の方が日本に近いため、遅延が少ないはずです。私は既に他の無料インスタンスをus-centralで作ってdiscordサーバーとして運用していますが問題ありません。

    画面右に下記のようなメッセージが出れば無料枠となります。

  4. ブートディスクをUbuntu 18.04 LTS Minimalにし、サイズを30GBにします。

  5. 作成でインスタンスを作成します

以上で、無料枠インスタンスの作成は完了です。

試しにSSHでログインしてみましょう。前回の記事と同様、VMインスタンスのSSHボタンをクリックして入ります。

このような画面が出現すれば成功です。

discord Botを導入する

では、discord Botをこのサーバーに設定しましょう。
まずは、簡単なコマンド返しを実装します。

  1. Pythonで実用Discord Bot(discordpy解説)を参考に、操作したいdiscordグループのアクセストークンを取得し、「Botプログラムの作成と起動」に沿って実行する

非常にまとまっているので、そのまま沿って行けば実装できると思います。

Botを起動後、自分のdiscordサーバーで/nekoコマンドを試してみましょう。

このpythonスクリプトで重要な所は、以下の場所です。

@client.event async def on_message(message): # メッセージ送信者がBotだった場合は無視する if message.author.bot: return # 「/neko」と発言したら「にゃーん」が返る処理 if message.content == '/neko': await message.channel.send('にゃーん')

このif message.content == '/hogehoge'を積み重ねれば、様々なコマンドに対する応答を実装できます。
GCPのインスタンスはgloudというコマンドから操作できるので、起動する関数をserver_start()、停止する関数をserver_stop()と仮定すると、

if message.content == '/server start':
    server_start()
if message.content == '/server stop':
    server_stop()

のように記述すればOKなわけです。

これらを実行するpythonファイルを作っていきましょう。

discordからサーバーを起動する

pythonファイルを作る前に、メインのマイクラ鯖を操作する権限を持つアカウントの作成が必要となります。

インスタンスの起動・停止権限を持つユーザーを作る

  1. GCP>プロダクト>IAMと管理>役割 を選択

  2. 「役割を作成」を選択

  3. 適当なタイトル(minecraft-admin等)をつけ、権限を追加を選択

  4. 権限に以下の4つを追加する

    • compute.instances.start
    • compute.instances.sto
    • compute.instances.get
    • compute.zoneOperations.get

    追加するときは検索欄に入れると便利です。見つかったらチェックを入れて追加しましょう。

  5. 作成をクリック
    これでサーバーの起動・停止権限がある役割が作成できました。
    次は、この役割を与えられるユーザーを作ります。

  6. IAMと管理>サービスアカウント へ移動

  7. サービスアカウントを作成を選択

  8. サービスアカウント名に「minecraft-admin」等の名前を付け、作成

  9. 先程作成した役割を付与する。(私の場合は役割の名前をMineAdminにしたので、これを選択)

  10. キーの作成(オプション)からキーを作成する。この時、JSON形式にすること。

    これでサービスアカウントの作成は完了です。

JSONファイルはすぐに使います。

サーバーの秘密鍵をダウンロード・登録する

  1. discord用のサーバー(無料インスタンス)にSSHでつなぐ
  2. 右上の
  3. 以下のコマンドを実行する
gcloud auth activate-service-account --key-file xxxxxxxxxxxx.json

※xxxxxx.jsonの部分は、先程アップロードしたjsonファイル名にしてください。

これで、このサーバーからminecraftサーバーを起動・停止できるようになりました。

コマンドに反応するpythonスクリプトを書く

以下の通りのpythonファイルを、discord用鯖の適当な場所に置きましょう。冒頭のすべて大文字になっている変数はご自分の環境に合わせて変更してください。GCP_PROJECT_NAMEはプロジェクト選択ウィンドウのIDのところに記載されています。

IDがHOGE1221であれば、GCP_PROJECT_NAME="HOGE1221"とします。

#references: https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f, https://qiita.com/neight0903/items/b243c730bd09d3562654
# インストールした discord.py を読み込む
import discord
import subprocess
# 自分のBotのアクセストークンに置き換えてください
TOKEN = 'THi5IsDuMMyaCCesSTOK3nQ4.Cl2FMQ.ThIsi5DUMMyAcc3s5ToKen7kKWs'
# サービスアカウントID
SERVICE_ACCOUNT_ID=hogehoge@hogehoge.iam.gserviceaccount.com
# プロジェクト名
GCP_PROJECT_NAME=hogehoge
# マイクラサーバーインスタンスの名前
MINECRAFT_INSTANCE_NAME=hogehoge
#MINECRAFT_INSTANCE_NAME=minecraft-server
# マイクラサーバーインスタンスのゾーン
MINECRAFT_INSTANCE_ZONE=hogehoge
#MINECRAFT_INSTANCE_ZONE=asia-northeast1-b
# 接続に必要なオブジェクトを生成
client = discord.Client()


#サーバー起動処理
def server_start():
    command = f'/snap/bin/gcloud --account={SERVICE_ACCOUNT_ID} compute instances start {MINECRAFT_INSTANCE_NAME} --project {GCP_PROJECT_NAME} --zone {MINECRAFT_INSTANCE_ZONE}'
    subprocess.call(command.split())
    return
#サーバー停止処理
def server_stop():
    command = f'/snap/bin/gcloud --account={SERVICE_ACCOUNT_ID} compute instances stop {MINECRAFT_INSTANCE_NAME} --project {GCP_PROJECT_NAME} --zone {MINECRAFT_INSTANCE_ZONE}'
    subprocess.call(command.split())
    return
# 起動時に動作する処理
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('ログインしました')

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return
    # 「/neko」と発言したら「にゃーん」が返る処理
    if message.content == '/neko':
        await message.channel.send('にゃーん')

    if message.content == '/server start':
        server_start()
        await message.channel.send('starting server...')
    
    if message.content == '/server stop':
        server_stop()
        await message.channel.send('stopping server...')
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)

コマンドの実行

python3 discord_minecraft.py

ログインしましたと表示されれば成功です。

コマンドのバックグラウンド実行

nohup python3 discord_minecraft.py &

nohupという機能を使い、バックグランド実行である&オプションをつけて実行しています。こうすればsshからログアウトしてもdiscordBotが動き続けます。

ここで、トークンを指定したdiscordグループで/server startと打ってみましょう。返答があり、gcpのダッシュボードでminecraft鯖の起動が確認できれば実装成功です!

サーバーの自動起動

以上の設定をすれば、minecraftサーバーが入っているインスタンスの起動がdiscordからできます。
しかし、インスタンスが起動してもminecraftサーバーは起動しません。インスタンス起動時にminecraftサーバーも自動起動する必要があります。

systemdファイルの作成

ubuntuにはsystemdと呼ばれるシステム起動機能が備わっています。ここに登録しておけば、起動時に指定したスクリプトを実行できます

  1. minecraft鯖用インスタンスを起動してsshで接続
  2. vi /etc/systemd/system/minecraft.service と入力し、ファイル編集画面にする
  3. iを押して文字挿入モードにする
  4. 以下のソースコードをコピーして、Ctrl + Vで貼り付ける
    [Unit]
    Description=minecraft bedrock server service
    After=network.target
    [Service]
    User=root
    Group=root
    WorkingDirectory=MINECRAFTSERVERDIRECTORY
    ExecStart=/usr/bin/screen -DmS bds MINECRAFTSERVERDIRECTORY/bedrock_server
    ExecStop=/usr/bin/screen -r bds -X stuff $"say SERVER SHUTTING DOWN IN 15 SECONDS...\n"
    ExecStop=/bin/sleep 15
    ExecStop=/usr/bin/screen -r bds -X stuff $"save-all\n"
    ExecStop=/usr/bin/screen -r bds -X stuff $"stop\n"
    Restart=no
    
    [Install]
    WantedBy=multi-user.target
    

    MINECRAFTSERVERDIRECTORYには自分のインストールしたminecraftサーバーのパスを入れましょう。例えばユーザー名hogeのホームディレクトリ内のminecraftというフォルダに入れた場合は/home/hoge/minecraftを入れます。

  5. sudo service minecraft startで、systemdのサービスとしてminecraftを実行。
  6. sudo suで管理者になってから、screen -r bdsでminecraft鯖のコンソールに入れたら成功。

以上の設定で、インスタンス起動時に自動的にminecraftサーバーが起動するようになります!

まとめ

以上がdiscordBotからminecraft鯖を起動する方法でした。統合版はスマホからもプレイできるため、例えば電車内からdiscord経由で鯖を起動してログインするという遊び方もできます。

再度今回の流れをまとめますと、

  1. discord用の常時起動無料鯖を建てる
  2. discordBot用のアクセストークンを取得
  3. discordBotのpythonファイルを書き実行
  4. minecraft鯖に戻り、systemdを登録
  5. discordから/server start/server stopで遠隔起動できるようになる!

といった感じでした。何か不明点やエラーなどがございましたらお気軽にtwitterかコメント欄でお知らせください。

「GCP上のMinecraft統合版サーバーをdiscordのBotから起動する」に21件のコメントがあります

  1. bot起動コマンドを実行したのですが以下の文章が出てきてしまい起動が出来ません。

    Traceback (most recent call last):
    File “minecraft-bot10.py”, line 3, in
    import discord
    ModuleNotFoundError: No module named ‘discord’

    記事の通りに作成したり自分なりに調べしたりして何度も試したのですが起動できませんでした。
    どうすればよいのでしょうか?

    1. コメントありがとうございます。

      エラーには「discordという名前のライブラリが見つからない」と書いてあります。
      本記事で紹介しているdiscord導入記事(https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)をご覧いただき、インストールしてください。

  2. systemdの設定ファイル、Type=forkingを足さないとすぐ落ちました。
    今は動いてます。

  3. bot起動コマンドを実行したのですが以下の文章が出てきてしまい起動が出来ません。

    File “discord_minecraft.py”, line 8
    SERVICE_ACCOUNT_ID=mcbe-admin@civic-network-257705.iam.gserviceaccount.com
    ^
    SyntaxError: invalid syntax

    記事の通りに作成したり自分なりに調べしたりして何度も試したのですが起動できませんでした。
    どうすればよいのでしょうか?

    1. discord_minecraft.pyの8行目の文法が間違っていると書かれています。
      SERVICE_ACCOUNT_IDの右辺に改行などが入ってないでしょうか?

      1. なんとか使用できるようになったのですが
        自分のインストールしたminecraftサーバーのパスがわかりません
        この記事と前回の記事のとおり作った場合どうなるのでしょうか

        1. 何度も申し訳ありません
          起動時にうまくサーバーが起動しないのですが何か要因があるのでしょうか

          1. いえ、沢山コメントいただいてありがたいです。
            コメントの中に、「systemdのファイルの中にType=forkingを入れないとすぐに落ちてしまう」という助言がありました。私の環境ではなくても動くのですが、これを以下のように挿入して動くか試してもらえませんか?
            [Service]
            User=root
            Group=root
            Type=forking

          2. エラーメッセージありがとうございます。
            私の書いたsystemdファイルではUserをrootにしており、おそらくmidori2352さんのsshしたままのアカウントでは動きません。screenはrootの中で起動していて、権限的に見れないのではないかと思います。
            【解決案①】
            – “sudo su“でrootに入り、systemdを起動してみる。MINECRAFTSERVERDIRECTORYは/home/midori2352 のように書き換える
            【解決案②】
            Userをmidori2352に書き換える(もしかしたらGroupの書き換えも必要かもしれない)

            これで試してもらえませんか?systemdに殆ど明るくないため、解決案に自信はありません…

        2. 前回の記事の通りですと、ホームディレクトリ「~/」にあるかと思います。「cd ~/」と打って、「ls」でサーバーのファイルがあるか確認してください。
          このようになっている場合、MINECRAFTSERVERDIRECTORYの部分は「~」とするだけでOkです。

          1. ご返信いただきありがとうございます
            上記2つのことを試してみましたが今度は
            「Job for minecraft.service failed because the control process exited with error code.
            See “systemctl status minecraft.service” and “journalctl -xe” for details.」
            と出ました、systemctl statusと journalctlの文面をおいておきます
            「 minecraft.service – minecraft bedrock server service
            Loaded: loaded (/etc/systemd/system/minecraft.service; disabled; vendor preset: enabled)
            Active: failed (Result: exit-code) since Fri 2020-05-29 04:53:29 UTC; 30s ago
            Process: 1985 ExecStop=/usr/bin/screen -r bds -X stuff $say SERVER SHUTTING DOWN IN 15 SECONDS…
            (code=exited, status=1/FAILURE)
            Process: 1967 ExecStart=/usr/bin/screen -DmS bds ~/bedrock_server (code=exited, status=0/SUCCESS)

            May 29 04:53:29 mcbe-server systemd[1]: Starting minecraft bedrock server service…
            May 29 04:53:29 mcbe-server screen[1985]: No screen session found.
            May 29 04:53:29 mcbe-server systemd[1]: minecraft.service: Control process exited, code=exited status=1
            May 29 04:53:29 mcbe-server systemd[1]: minecraft.service: Failed with result ‘exit-code’.
            May 29 04:53:29 mcbe-server systemd[1]: Failed to start minecraft bedrock server service.」

            — Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat

            — A new session with the ID 1 has been created for the user midori2352.

            — The leading process of the session is 1780.
            May 29 04:53:23 mcbe-server systemd[1]: Started Session 1 of user midori2352.
            — Subject: Unit session-1.scope has finished start-up
            — Defined-By: systemd
            — Support: http://www.ubuntu.com/support

            — Unit session-1.scope has finished starting up.

            — The start-up result is RESULT.
            May 29 04:53:23 mcbe-server systemd[1790]: pam_unix(systemd-user:session): session opened for user midori2352 by (u
            May 29 04:53:23 mcbe-server systemd[1]: Started User Manager for UID 1001.
            — Subject: Unit user@1001.service has finished start-up
            — Defined-By: systemd
            — Support: http://www.ubuntu.com/support

            — Unit user@1001.service has finished starting up.

            — The start-up result is RESULT.
            May 29 04:53:28 mcbe-server sudo[1936]: midori2352 : TTY=pts/0 ; PWD=/home/midori2352 ; USER=root ; COMMAND=/usr/sb
            May 29 04:53:28 mcbe-server sudo[1936]: pam_unix(sudo:session): session opened for user root by midori2352(uid=0)
            May 29 04:53:29 mcbe-server systemd[1]: Starting minecraft bedrock server service…
            — Subject: Unit minecraft.service has begun start-up
            — Defined-By: systemd
            — Support: http://www.ubuntu.com/support

            — Unit minecraft.service has begun starting up.
            May 29 04:53:29 mcbe-server screen[1985]: No screen session found.
            May 29 04:53:29 mcbe-server systemd[1]: minecraft.service: Control process exited, code=exited status=1
            May 29 04:53:29 mcbe-server systemd[1]: minecraft.service: Failed with result ‘exit-code’.
            May 29 04:53:29 mcbe-server systemd[1]: Failed to start minecraft bedrock server service.
            — Subject: Unit minecraft.service has failed
            — Defined-By: systemd
            — Support: http://www.ubuntu.com/support

            — Unit minecraft.service has failed.

            — The result is RESULT.
            May 29 04:53:29 mcbe-server sudo[1936]: pam_unix(sudo:session): session closed for user root

          2. やはりできないようです
            自動起動させなくても遊べるのでこのままで行こうと思います
            最後になりますがGCPの中に入っているワールドディレクトリをダウンロードする方法を御存知であれば教えていただきたいです

          3. お力になれず申し訳ないです。
            ダウンロードですが、ワールドディレクトリの名前を「worlds」とした場合、「zip -r worlds.zip worlds」と打つことでworlds.zipファイルを作成します。その後「readlink -f worlds.zip」でzipファイルへのパスが取得できるので、それをコピーしてください。
            最後にssh画面の右上にある歯車のマーク>「ファイルをダウンロード」を選択し、コピーしたパスを貼り付ければダウンロードできます。

  4. systemdファイルの作成の4、5、6について教えてください

    文字挿入モードにして4でペーストした後、5のコマンド入力をするまでの間をどのように対応すればよいでしょうか。
    ファイル編集画面→i→文字挿入モード→escでファイル編集画面→:wqで保存して終了?
    その後、5のコマンド入力という流れでよいのでしょうか?

  5. systemdファイル作成の部分で追加で質問をさせてください
    文字挿入モード→escでファイル編集画面→:wqで保存して終了をしようとしたのですが
    下記表示が出てしまいました。
    “/etc/systemd/system/minecraft.service” E212: Can’t open file for writing

    やはりやり方は間違っているのでしょうか?
    大変申し訳ございませんが、流れを教えていただけますと助かります。
    何卒よろしくお願いいたします

  6. お世話になります。
    なんとか⑤までは進めたのですが
    下記エラーが出てしまいまして、対応策をお教えいただけますと助かります。
    コードが間違えているんですかね?

    ××××××××××@minecraft-server:~$ sudo service minecraft start
    Job for minecraft.service failed because a timeout was exceeded.
    See “systemctl status minecraft.service” and “journalctl -xe” for details.
    ××××××××××@minecraft-server:~$ sudo systemctl status minecraft.service
    ● minecraft.service – minecraft bedrock server service
    Loaded: loaded (/etc/systemd/system/minecraft.service; disabled; vendor preset: enabled)
    Active: failed (Result: timeout) since Mon 2020-09-14 13:54:41 UTC; 1min 8s ago
    Process: 4300 ExecStart=/usr/bin/screen -DmS bds /home/××××××××××/bedrock_server (code=exited, status=1/FAILURE)
    Tasks: 0 (limit: 2332)
    CGroup: /system.slice/minecraft.service
    Sep 14 13:53:10 minecraft-server systemd[1]: Starting minecraft bedrock server service…
    Sep 14 13:54:41 minecraft-server systemd[1]: minecraft.service: Start operation timed out. Terminating.
    Sep 14 13:54:41 minecraft-server systemd[1]: minecraft.service: Control process exited, code=exited status=1
    Sep 14 13:54:41 minecraft-server systemd[1]: minecraft.service: Failed with result ‘timeout’.
    Sep 14 13:54:41 minecraft-server systemd[1]: Failed to start minecraft bedrock server service.
    ××××××××××@minecraft-server:~$

    ××××××××××はユーザー名です。

    1. お世話になります
      systemdファイル作成の部分の
      おそらく⑥の方までいけたようなのですが、
      screen -r bdsを実行しても下記状態になりました。
      これはscreenが実行できていないということでしょうか。

      root@minecraft-server:/home/××××××××××# screen -r bds
      There is no screen to be resumed matching bds.

      1. お世話になります。
        最終的にsystemdファイル作成の⑥までいけたのですが
        SSHから接続してコマンド入力すれば接続できるのですが、
        discordからだとインスタンスの起動で止まっており、minecraftサーバーが立ち上がっていないようです。
        考えられるバグの可能性どこかありますでしょうか。

        ファイル編集画面の内容は下記にしております。
        [Unit]
        Description=minecraft bedrock server service
        After=network.target
        [Service]
        User=root
        Group=root
        Type=forking
        WorkingDirectory=/home/××××××××××
        ExecStart=/usr/bin/screen -DmS bds /home/××××××××××/bedrock_server
        ExecStop=/usr/bin/screen -r bds -X stuff $”say SERVER SHUTTING DOWN IN 15 SECONDS…\n”
        ExecStop=/bin/sleep 15
        ExecStop=/usr/bin/screen -r bds -X stuff $”save-all\n”
        ExecStop=/usr/bin/screen -r bds -X stuff $”stop\n”
        Restart=no

        [Install]
        WantedBy=multi-user.target

        1. わくてく

          詳細ありがとうございます。
          自動起動してsshした際、sudo suで管理者になってからマインクラフト鯖が走っているか確認してもらえませんか?
          もしそれでもなければ、systemdの登録を間違っている可能性があります。お送りいただいたsystemdのファイルは特に問題なさそうです。

  7. service minecraft start
    を実行すると、
    以下のエラーが発生して実行できないようです
    何が原因でしょうか?
    Warning: The unit file, source configuration file or drop-ins of minecraft.service changed on disk. Run ‘systemctl
    daemon-reload’ to reload units.

    1. わくてく

      これだけで原因は分かりませんが、エラーを見るにコマンド「systemctl daemon-reload」を実行しろと言われています。これを実行したあと再度「service minecraft start」を試してもらえますか?

わくてく へ返信する 返信をキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です