DiscordからGCPインスタンスを操作してみた話

#はじめに
本記事は [GCP初心者がGCEでマイクラサーバを建ててみた話](https://qiita.com/rodotan/items/f5d7f76a1bc9b34ab717) の続きになります。
前回を見ていない方はそちらから見ていただけると幸いです。

さて、前回GCPでマイクラサーバを建てましたので、
今回はそちらをdiscordから操作できるようにしていきたいと思います。

わざわざdiscordからインスタンスの操作をするメリットはある？ と思われますが、
一緒にマイクラをプレイしている人にdiscordのチャンネルに参加してもらい、
管理者の代わりに起動・停止を行えるので、管理者の負担を減らすことができます。

#目標
- bot用サーバの作成
- インスタンス制御用サービスアカウントの作成
- Discord Botの作成

#手順

##１. bot用サーバの作成
今回bot用に使うサーバは、Google Compute Engine（以下、GCE）を使います。
GCEには完全無料で使えるAlways Freeのリソースがあり、今回はこちらを利用します。
Always Freeなので常時起動していても課金は発生しませんし、今回はbot以外の用途はないのでこのリソースで十分運用可能です。

GCEのAlways Freeの詳細は以下の公式ドキュメントを参照してください。
> [Google Cloud の無料枠](https://cloud.google.com/free/docs/gcp-free-tier?hl=ja)

インスタンスについてはこのような設定で作成しました。
細かな設定は今回は省略します。

| 項目       　　　　| 内容　　　　　　     |
|:-----------------|:------------------|
| インスタンス名       | botserver        |
| リージョン           | us-west1         |
| ゾーン             | us-west1-a       |
| マシンタイプ         | f1-micro（vCPUx1, メモリ0.6GB）|   
| ブートディスク        | CentOS 7          |
| IPアドレス |静的IPアドレスを作成 |

##2. インスタンス制御用サービスアカウントの作成
GCPにはサービスアカウントと呼ばれる、プロジェクトやリソースにアクセスできる一定の権限だけ付与したアカウントを作成できます。
サービスアカウントの詳細については以下の公式ドキュメントを参照してください。
> [サービスアカウントについて](https://cloud.google.com/iam/docs/understanding-service-accounts?hl=ja#managing_service_accounts)

今回はこのサービスアカウントにインスタンス（マイクラサーバ）の起動や停止の権限を付与します。
まずは「IAMと管理」から「役割」を作成します。

「IAMと管理」 -> 「役割」 -> 「役割を作成」を選択し、以下のように設定します。

| 項目       　　　　| 内容　　　　　　     |
|:-----------------|:------------------|
| タイトル       | 任意のタイトル名       |
| 権限       |compute.instances.get <br>  compute.instances.start <br> compute.instances.stop |

**[権限の説明]**

| 項目 | 説明 |
|:--|:--|
| compute.instances.get  | Compute Engine インスタンスにログインするアクセス権 |
| compute.instances.start  | Compute Engine インスタンスを起動する権限 |
| compute.instances.stop  | Compute Engine インスタンスを停止する権限  |

ここまでで「役割」の作成は完了です。
続いて「IAMと管理」から「サービスアカウント」を作成します。

「IAMと管理」 -> 「サービスアカウント」 -> 「サービスアカウントを作成」を選択し、以下のように設定します。

| 項目 | 内容 |
|:-:|:-:|
| サービスアカウント名  | 任意のサービスアカウント名  |
| サービスアカウントID  | 任意のサービスアカウントID@{ProjectID}.iam.gserviceaccount.com |
| サービスアカウントの説明  |  自分のわかりやすいように説明文を記載 |
| 役割の選択  | 先ほど作成した「役割」 |
| キーの作成 | JSONタイプ <br> *作成をクリックしjsonファイルをダウンロードしてください。|

キーの作成でダウンロードしたjsonファイルは後ほど必要になるので、無くさないようにしてください。

##3. Discord Botの作成

### 3. 1 botアカウントの作成
まずは[Discord Developer Portal](https://discordapp.com/developers/applications/)でbot用のデペロッパーアカウントを作成しましょう。

デペロッパーアカウントの作成から諸々の設定は以下の記事を参考にさせていただきました。
>[Discord Botアカウント初期設定ガイド for Developer](https://qiita.com/1ntegrale9/items/cb285053f2fa5d0cccdf)

###3. 2 botサーバの設定

1.で作成したインスタンスにログインし諸々の設定をしていきます。

まずは必要なパッケージをインストールしていきます。

```bash

# CentOS7 はデフォルトではpython3系がインストールされていないので、python3系のインストールを実施
$ yum install python3

# discord.pyのインストール
$ python3 -m pip install -U discord.py
```

次に、先ほど作成したサービスアカウントを認証させます。

```bash

# サービスアカウント作成時にDLした鍵ファイル（jsonファイル）をbotサーバにアップロードし、以下のコマンドで認証させます。
$ gcloud auth activate-service-account --key-file "./<projectName>-xxxxxx.json"

# サービスアカウントがactiveになっているかを確認する。*アカウントがactiveになっていればOK
$ gcloud auth list

# 以下実行例
---------------------------------------------------------------
ACTIVE  ACCOUNT
*       <サービスアカウント名>@<プロジェクト名>.iam.gserviceaccount.com
---------------------------------------------------------------
```

botプログラムの作成

```python:mineserver-op.py

# インストールした discord.py を読み込む
import discord
import os
import time

# 自分のBotのアクセストークン
TOKEN = '<アクセストークン>'

client = discord.Client()

# 起動時に動作する処理
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('やっほ！ログインしたよ！')
    print('/help でコマンドの確認ができるよ')

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は処理しない
    if message.author.bot:
        return
#サーバーの起動
    if message.content == '/start':
        await message.channel.send('Server starting up...')
        await message.channel.send('※「start up」が表示されるまで他のコマンドを実行させないこと※')
        os.system('gcloud --account=<サービスアカウント名>@<プロジェクト名>.iam.gserviceaccount.com compute instances start <インスタンス名> --project <プロジェクト名> --zone <ゾーン名>')
        await message.channel.send('up .minecraft_server starting...')
        time.sleep(60)
        await message.channel.send('start up')
#サーバーの停止
    if message.content == '/stop':
        await message.channel.send('Server is stopping')
        await message.channel.send('※「down」が表示されるまで他のコマンドを実行させないこと※')
        os.system('gcloud --account=<サービスアカウント名>@<プロジェクト名>.iam.gserviceaccount.com compute instances stop <インスタンス名> --project <プロジェクト名> --zone <ゾーン名>')
        await message.channel.send('down')

#ヘルプの表示
    if message.content == '/help':
        await message.channel.send('/start : サーバの起動')
        await message.channel.send('/stop : サーバの停止') 

client.run(TOKEN)
```

作成したbotプログラムの実行

```shell
#実行ログを残すようにし、実行
$ nohup python3 mineserver-op.py > ./out.log &
```

ここまででdiscordからインスタンスを操作できるようになりました。
実際にdiscordからコマンドを打ってインスタンスを操作できているか確認しましょう。

#まとめ
今回はインスタンスの起動、停止の最低限の機能しか実装していませんが、
サービスアカウントに欲しい権限を付与すれば色々な機能を実装することが可能です。

余談になりますが、今回の動作確認でマイクラを少しプレイしました。
私がプレイしていた頃から新要素がかなり増えており、またマイクラやりたいな〜って思いましたね（笑）