systemdを使用したMinecraftサーバーの起動/停止

systemdを使用したMinecraftサーバーの起動/停止

私はsysV initスクリプトを使用してMinecraftサーバーを実行してきました。これは非常に良いスクリプトです。 Minecraftを「画面」で実行すると、Minecraftが2回起動せず、Minecraftが閉じるまで待ちます。 Minecraftにコマンドを渡すこともでき、/etc/init.d/minecraft command <command>バックアップを計画するのに役立ちます。

今私はシステム化されたDebian Jessieにアップグレードしました。でも今は元の台本がすごいのでそのままにしています。しかし、私は実際にsystemdを非常にサポートしています。多くの改善、簡素化、集中化が行われているようです。 systemd開発者たちは、「既存のsysVスクリプトは以前と同じように機能する」と約束したことを覚えています。しかし、それはそれほど簡単ではありませんでした!

以前はいくつかの起動スクリプトに問題があったことを覚えています。スクリプトを /etc/init.d に入れて実行可能としてマークするだけでは不十分です。機能するには、スクリプトを「アクティブ化」する必要がありました。 「まあ、私はsystemdで認識され、今度はsystemctlを介して制御できます。コマンドを処理するには古いスクリプトを使用する必要があるようです!」と思いました。

正しく開始されず、正しく停止されず、状態が正しく表示されず、「コマンド」コマンドが欠落しています。私はsystemdがsysVよりもどのように優れているか、そしてすべてを単純化し改善するために何ができるかについての情報を探し始めました。明らかにsystemctl自体は最も簡単な単位ファイルを可能にし、それで十分であることを願っています!さて、systemdが実際にそのような複雑な状況をまったく処理できないのだろうか?

一般的なシステムサービスは基本的にいくつかの要件とExecStartで構成されていることがわかりました。 systemdがデーモンプロセスを監視する必要があるかのように。条件と実行可能ファイル名を入力すると、systemdは起動、停止、その他を処理します。しかし、簡単ではありません! MinecraftのPIDを直接削除することはできません(画面のPIDと同じではないことは言うまでもありません)!各タスクに対してより複雑なスクリプトを作成したいと思います。たぶん「コマンド」のような新しいタスクを追加することができます(わかりました、これはまったく不可能かもしれません)。 「状態」の場合、Javaプロセスを監視し、停止するにはMinecraftコンソールにコマンドを送信してから、Javaと画面の両方が死ぬのを待つ必要があります。また、systemdがSIGHUP、SIGINT、またはSIGTERMだけを試みないようにしたいと思います!

それでは、systemdが提供するすべての「改善事項」と「単純化」を実際に活用できるようにする、きれいで現代的で「期待されるsystemd方式」とは何ですか?確かに、1行で始まり、SIGINTで終了する単純な単一プロセスデーモンよりも複雑なものを処理できるはずですか? systemdデバイスを作成し、次のようにすべてのコマンドで古いスクリプトを呼び出すように手動で指定する必要がありますか?

ExecStart=/etc/init.d/minecraft start
ExecReload=/etc/init.d/minecraft reload
(and how do I make the "stop" command and explain how to find the processes to watch for the "status" command?..)

私はこの点で非常に革新的で、ポエタリングを支持し、システムに賛成し、以前よりも仕事をよりよくする方法がなければならないと信じています。おそらく、ポータリングの一般的な方法と全く異なる方法があります。その!)。しかし、これは大きな改善のようには見えません。以前のように続けるには、多くの組み立てが必要な巨大な退縮のように見えます。 "sysVスクリプトは引き続き動作します"私のポニーテール! Minecraftを正しく停止するためにシステムがシャットダウンしたときに私のスクリプトを呼び出すのか、それとも「systemctl status」を見て、「非アクティブ(死)」であるのかを確認するのかどうかはわかりません。

より良いアイデアがありますか?

答え1

マニュアルページを数回見た後(はい、最初は答えがありません...)、解決策を見つけましたが...うまくいきません。もう少し探した後、ついに最もエレガントなソリューションを見つけました。

[Unit]
Description=Minecraft server
After=local-fs.target network.target

[Service]
WorkingDirectory=/home/minecraft/minecraft_server
User=minecraft
Group=minecraft
Type=forking
# Run it as a non-root user in a specific directory

ExecStart=/usr/bin/screen -h 1024 -dmS minecraft ./minecraft_server.sh
# I like to keep my commandline to launch it in a separate file
# because sometimes I want to change it or launch it manually
# If it's in the WorkingDirectory, then we can use a relative path

# Send "stop" to the Minecraft server console
ExecStop=/usr/bin/screen -p 0 -S minecraft -X eval 'stuff \"stop\"\015'
# Wait for the PID to die - otherwise it's killed after this command finishes!
ExecStop=/bin/bash -c "while ps -p $MAINPID > /dev/null; do /bin/sleep 1; done"
# Note that absolute paths for all executables are required!

[Install]
WantedBy=multi-user.target

これは実際に元のスクリプトよりも良く見えます!しかし、いくつかの回帰があります。

  • コマンドをサーバーコンソールに渡すには、別々のスクリプトを作成する必要があります。
  • 実行してsystemctl start minecraft確認systemctl stop minecraftしてください。それ以外の場合、systemctl status minecraftこれらのコマンドが実際に失敗してもまったく出力を提供しません。これはスクリプトと比較して唯一の主な回帰です。 「常に出力を確認してください」はITのルール#1ですが、systemdはそれを気にしないようです。
  • また、systemdが「PID終了待ち」の解決策なしにサービス終了を管理できることを願っています。以前のinitスクリプトでは、これを手動で実行する必要がありました。これはスクリプトであり、systemdは同じことの複雑なスクリプトの必要性を排除しようとするためです。これにより、すべてのタイムアウトを手動でスクリプト化し、不要なタイムアウトを終了することがなくなります。しかし、「pidが死ぬのを待つ」は、私たちがスクリプトを書く必要がある次の最も一般的なものです。

答え2

要約:画面をまったく使用しないでください。 RCON を使用してサーバーを制御します。

私は画面がMinecraftサーバー管理のための事実上の標準だったことに気づきましたが、両方の方法を試した後、RCONが制御問題をよりきれいに解決するという結論に達しました。

スクリーンセッションでサーバーを実行する理由は、スクリーンセッションを使用してサーバーの標準入力にコマンドを入力してサーバーにコマンドを送信できるためです。サーバーの対話型コンソールを使用するために、screenセッションに直接接続することもできます。これは重要な機能ですが、画面がこれを達成する唯一の方法ではありません。

Minecraftはリモート管理のためのRCONプロトコルをサポートしています。ローカルで使用してサーバーと対話できます。私は使うマクコン単一のコマンドを送信したり、対話型端末として機能したりできるコマンドラインツールです。これは画面よりも強力で複雑ではありません(画面を介してコマンドを送信するとeval常にstuffオーグラになります)。

私のminecraft.serviceファイルは次のとおりです。

[Unit]
Description=Minecraft Server
After=network.target

[Service]
Type=simple
User=minecraft
Group=minecraft
WorkingDirectory=/srv/minecraft
ExecStart=/usr/local/bin/minecraft/start
ExecStop=/usr/local/bin/minecraft/stop
Restart=always

[Install]
WantedBy=default.target

このstartスクリプトは単にサーバー実行ファイルを実行します。

#!/bin/sh

cd /srv/minecraft
java -Xmx12G -Xms12G \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseG1GC \
  -XX:G1NewSizePercent=50 \
  -XX:MaxGCPauseMillis=50 \
  -XX:+AlwaysPreTouch \
  -jar server.jar nogui

スクリプトstopはサーバーにコマンドを送信し、プロセスが停止するのを待ちます。

#!/bin/sh

/usr/local/bin/minecraft/rcon stop

while kill -0 $MAINPID 2>/dev/null
do
  sleep 0.5
done

このrconスクリプトは簡単に言うと次のようになります。

#!/bin/sh

mcrcon -H localhost -P 25575 -p "password omitted" $@

私のスクリプトにはRCONポートとパスワードがハードコーディングされていますが、server.propertiesファイルから簡単にインポートできます。またmcrcon、必要に応じてコマンドライン引数ではなく環境変数を介して許可されます。

その他の注意事項:

  • ファイルでRCONを有効にする必要がありますserver.propertiesが、リモート使用を防ぐためにファイアウォールでポートを閉じることができます。
  • 対話型端末セッションを開始するには、rcon引数なしで実行します(またはmcrconを直接呼び出します)。
  • サーバーログを表示するには、Journalctl を使用します。私はこれを使ってすべてのログをDiscord Webhookに送ります。

編集:他の答えのコメントは、SIGINTがコマンドを送信するかのようにMinecraftサーバーを正常に停止させると主張しますstop。もしそうなら、サーバーを停止するためにRCONを使用する代わりにSIGINTを送信する方が簡単でより良いでしょう。 RCON は、他の管理タスクにまだ役に立ちます。

答え3

私はこのサービスファイルを使います:

[Unit]
Description=Minecraft server
Wants=network.target
After=network.target

[Service]
User=minecraft
Group=minecraft
Nice=5

WorkingDirectory=/home/minecraft/.minecraft/
KillMode=process
KillSignal=SIGINT
SuccessExitStatus=130

ExecStart=/usr/bin/java -Xms1G -Xmx1G -jar /home/minecraft/.minecraft/minecraft_server.jar nogui

[Install]
WantedBy=multi-user.target

停止するには SIGINT(ctrl+c) を使用します。

KillSignal=SIGINT

SuccessExitStatus は正常終了を示します。

SuccessExitStatus=130

答え4

@darkpenguinの答えに基づいたtmuxの内容は次のとおりです。

[Unit]
After=network.target

[Service]
WorkingDirectory=/home/minecraft/server/
Type=forking
User=minecraft
ExecStart=/usr/bin/tmux new-session -s mine1 -n m -d "/home/minecraft/start_minecraft.sh"

# Send "stop" to the Minecraft server console
ExecStop=/usr/bin/tmux send-keys -t mine1 'Stop' Enter
# Wait for the PID to die - otherwise it's killed after this command finishes!
ExecStop=/bin/bash -c "while ps -p $MAINPID > /dev/null; do /bin/sleep 1; done"
# Note that absolute paths for all executables are required!

[Install]
WantedBy=default.target

関連情報