サービスとして起動したマイクラサーバからログインの通知をLINEで受け取るまで


きっかけ

  • 強くなりたいしVPS借りて遊ぶぞ!
  • せっかくVPS借りるしマイクラのサーバ立てたらみんな(1-4人)でマルチできるんじゃね?
  • マイクラのサーバをサービスで動かしたらいちいちSSHせずに接続できるな、Realms借りなくてもいいじゃん!
  • でもなんか勝手に使われるのもアレだし、第三者に入られても困るし、ログイン時ぐらいは通知を受け取れるようにしたい
  • メールとかどうせ見ないしLineに通知するかー

環境

  • さくらのVPS 1GBプラン
  • CentOS 7.6.1810

要件・計画

  • ログイン・ログアウトの通知をラインに飛ばす
    →「〇〇 joined/left the game」のログが拾えればいい
    grepコマンドでgameを含む行を抽出すればいける?
  • ログイン・ログアウトの都度、その場で情報が欲しい
    →マイクラサーバのログ出力を受け取る別プロセスを、マイクラサーバと一緒に動かしたい →サービス実行時のシェルスクリプトでパイプをうまく繋げてやる?
  • できるだけ簡単にしたい
    → シェルスクリプトで完結させたい+メモリ消費も異常に増大してないかは確認したい

実装

マイクラサーバ

以下の記事様を参考にし、マイクラのサーバがサービスとして動くようにします(感謝)
MinecraftServerをサービスとして起動する(Ubuntu14.04)

これにより今
* run.shというシェルスクリプトがあり、サービス起動時にはこのスクリプトが実行される
* run.shを起動するマイクラサーバをminecraft_serverというサービス名で登録し、起動・終了・ステータスの確認ができる

という状態になっていると想定します。

LINE通知

LINEのボットを作成済みで、curlによりボットに向けてメッセージを送ることができるようにします。
以下のようなサイトを参考にし、デベロッパ登録、チャンネル作成、ボット作成、トークン取得まで進めます。(感謝)
Google Apps ScriptでPushメッセージのLINE Botを作る。

かつ、受け取った文字列をそのままメッセージに投げてやるスクリプトも作成します。
以下のようなスクリプトを作成しました。注意点として:

  • Access TokenとUser Idには自分のbotの値を入れてください
  • sleep 1sは短時間に連続して送信しないための待ち処理です。待ち時間は適宜値を変えて使用してください
  • 追記:文字列$strの出力を`${str:33}'としてやることで、ログの時刻などを削り内容のみが送られて来るようにできました
send.sh
#!/bin/bash

while true
do
        read v
        if [ -z "$str" ]; then
                break
        fi
        curl -v -X POST https://api.line.me/v2/bot/message/push \
-H 'Content-Type:application/json' \
-H 'Authorization: Bearer {Access Token}' \
-d '{
    "to": "{User Id}",
    "messages":[
        {
            "type":"text",
            "text":"'"${str:33}"'"
        }
    ]
}'
sleep 1s
done

スクリプト

あとはrun.shを以下のように書き換えてやれば、gameという文字列が含む行がラインに送信されます。

run.sh
#!/bin/bash

cd /home/minecraft/minecraft
java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui | grep game --line-buffered | ./send.sh

できた。よかった

これをした後にsudo systemctl start minecraft_serverを実行すると以下のように無事ラインの方に通知が来ました。

そっかこの行も引っかかるのか・・・・まあいいか
実際にマイクラを立ち上げて自分でログイン・ログアウトして見ました

なんかいい感じにメッセージが来てますね、よかったよかった

実装前実験

実は最初、以下のようにスクリプトを書いていたのですが何も送られて来ず、わりと長時間はまっていました。

run.sh
#!/bin/bash

cd /home/minecraft/minecraft
java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui | grep game | ./send.sh

なんでだ??ということで色々調べるため、簡単なスクリプトを書いて実験しました。

準備

まず、以下のようなスクリプトを作成しました。

test.sh
#!/bin/bash 

echo hoge
echo fuga hoge
sleep 1s
echo fuga hogehogehoge 
echo pikopiko
echo piko fuga
sleep 1s
echo hogehoge
echo fugafuga
echo hogefuga
sleep 5s

実行

一秒ごとに複数行適当な文字列が出力され、プログラムの終了前には5秒ほど待ちが入ります。
これを用意した上で、以下のコマンドを実行しました。

./test.sh | grep hoge

その結果、hogeを含む行だけが表示され、しかもechoでhogeを含む行が出力されるであろうタイミングで、grepの結果が表示されるという動作をしました。

次に、この後ろにラインへの送信スクリプトを繋げました。

./test.sh | grep hoge | ./send.sh

その結果、hogeを含む行だけが表示はされましたが、test.shが終了するタイミングで(つまりスクリプト末尾のsleep 5sで待機した後に)ラインの送信が始まりました。

はえ〜・・・・

なんでかは調べてないのでわかりませんが、grepで後ろに別のスクリプトを繋げると、前のプログラムが終了するまでgrep結果を後ろに渡さないんですね

ということで検索したら、そのものズバリな記事がヒットしました(感謝)
grep の出力をバッファさせない

ので、--line-bufferedのオプションを入れたらうまくいきました。基本的なことなのかなあ
パフォーマンスが低下するかもとのことなので接続時メモリ状況などには注意する必要があるかもですね

動作確認

念のため、ログ通知のスクリプトをサービスに含めた場合と含めない場合で消費メモリ量を比較して見ました。

grepsend.shも起動した場合

              total        used        free      shared  buff/cache   available
Mem:            991         858          73           0          59          23
Swap:          4095         192        3903

serverのみ起動した場合

              total        used        free      shared  buff/cache   available
Mem:            991         871          65           0          54          12
Swap:          4095         162        3933

大丈夫・・・だよなあ???
serverのみに比べて爆発的に増えたというわけでもなさそうですし、大丈夫じゃないかなあと