使用 systemd 启动/关闭 Minecraft 服务器

dar*_*uin 8 debian systemd minecraft

我一直在运行带有 sysV init 脚本的 Minecraft 服务器。这是一个非常好的脚本;它在“屏幕”中运行 Minecraft;它可以确保 Minecraft 永远不会启动两次;它会等待 Minecraft 在停止时关闭。它甚至可以向 Minecraft 传递命令/etc/init.d/minecraft command <command>——这对于计划备份很有用。

现在我已经升级到 Debian Jessie,它有 systemd。但是现在,我保留我的旧式脚本,因为它很棒。尽管如此,我实际上非常支持 systemd - 它看起来确实有很多改进、简化和集中化。我记得 systemd 开发人员承诺“旧的 sysV 脚本会像以前一样工作”,但事实证明这并不容易!

我记得之前有一些启动脚本有问题;显然,仅仅将脚本放入 /etc/init.d 并将其标记为可执行已经不够了 - 我必须“启用”它们才能使它们工作。“好吧,”我想,“现在它被 systemd 识别了,现在我可以通过 systemctl 来控制它——它可能应该只使用我的旧脚本来处理命令!” 事实证明我错了。

它不能正常启动,不能正常停止,不能正常显示状态,更不用说缺少“命令”命令了。我已经开始寻找有关 systemd 如何比 sysV 更好的信息,以及我可以做些什么来简化和增强一切。显然, systemctl 只是自己制作了最简单的单元文件,希望它就足够了!现在我想知道 systemd 是否真的无法处理如此复杂的情况!

我看到一个普通的 systemd 服务基本上由一些要求和 ExecStart 组成。就像 systemd 需要监视守护进程一样。输入条件和可执行文件名称,systemd 将处理它的启动、停止以及其他事情。但是没那么容易!!你不能只是杀死 Minecraft 的 PID(更不用说它与屏幕的 PID 不同)!我想为每个动作编写更复杂的脚本,甚至可能添加像“命令”这样的新动作(好吧,我已经接受了这可能根本不可能)。对于“状态”,它必须监视 Java 进程,对于停止,它必须向 Minecraft 控制台发送命令,然后等待 Java 和屏幕都死掉!我还想确保 systemd 不会只是尝试 SIGHUP 或 SIGINT 或 SIGTERM 它!

那么,真正允许我们利用 systemd 为我们提供的所有“改进”和“简化”的巧妙、现代、“预期的 systemd 方式”是什么?当然,它应该能够处理比在一行中启动并用 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?..)
Run Code Online (Sandbox Code Playgroud)

在这方面,我非常支持创新、支持 Poettering 和支持系统化,我相信应该有一种方法可以比以前做得更好——也许以一种完全不同的方式,就像 Poettering 通常那样(我喜欢他!)。但这看起来并没有太大的改进——更像是一个巨大的回归,需要大量的杂物才能像以前一样继续。“sysV 脚本将继续工作”,我的马尾辫!我什至不能确定它是否调用我的脚本在系统关闭时正确停止 Minecraft,或者只是查看“systemctl status”并看到它已经“inactive (dead)”。

有什么更好的想法吗?

dar*_*uin 11

浏览了几次联机帮助页后(是的,第一次没有答案......),我想出了一个解决方案......但没有用。在浏览更多之后,我终于想出了最优雅的解决方案。

[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
Run Code Online (Sandbox Code Playgroud)

这确实比我的原始脚本看起来更好!但是,有一些回归。

  • 如果我想将命令传递到服务器控制台,那么我必须制作一个单独的脚本来完成它。
  • 运行systemctl start minecraft或 后systemctl stop minecraft,一定要检查systemctl status minecraft,因为这些命令根本没有输出,即使它们实际上失败了。与脚本相比,这是唯一的主要回归-“始终检查您的输出”是 IT 中的第一条规则,但 systemd 似乎并不关心它...
  • 此外,我希望 systemd 能够在没有“等待 PID 死亡”的解决方法的情况下管理服务关闭。在旧的 init 脚本中,我不得不手动执行此操作,因为它是一个脚本,而 systemd 正试图消除对相同内容的复杂脚本的需求;它消除了在所有超时中手动编写脚本并杀死没有死亡的人的需要,但“等待 pid 死亡”是下一个最常见的事情,我们仍然需要编写脚本。


Tho*_*ith 10

tl;dr:根本不要使用屏幕。使用RCON来控制服务器。

我意识到屏幕一直是管理 Minecraft 服务器的事实上的标准,但在尝试了这两种方法之后,我得出的结论是 RCON 以一种更简洁的方式解决了控制问题。

在屏幕会话中运行服务器的原因是,您可以使用屏幕会话将命令填充到服务器的标准输入中,从而将命令发送到服务器。您还可以直接附加到屏幕会话以使用服务器的交互式控制台。这些都是重要的功能,但屏幕并不是实现它们的唯一方法。

Minecraft 支持 RCON 协议进行远程管理。您可以在本地使用它与服务器交互。我使用mcrcon命令行工具,它可以发送单个命令或作为交互式终端。这比屏幕更强大,而且可以说更不那么老套(通过eval和发送命令stuff总是让我畏缩)。

我的minecraft.service文件如下所示:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

stop脚本向服务器发送命令并等待进程停止:

#!/bin/sh

/usr/local/bin/minecraft/rcon stop

while kill -0 $MAINPID 2>/dev/null
do
  sleep 0.5
done
Run Code Online (Sandbox Code Playgroud)

rcon脚本只是一个简写:

#!/bin/sh

mcrcon -H localhost -P 25575 -p "password omitted" $@
Run Code Online (Sandbox Code Playgroud)

我的脚本对 RCON 端口和密码进行了硬编码,但您可以轻松地从server.properties文件中获取它们。另外,mcrcon如果您愿意,将通过环境变量而不是命令行参数接受它们。

其他注意事项:

  • 您必须在server.properties文件中启用 RCON,但您可以在防火墙中关闭该端口以防止远程使用它。
  • 要启动交互式终端会话,只需rcon不带参数运行(或直接调用 mcrcon)。
  • 要查看服务器日志,请使用journalctl。我用它来将我的所有日​​志发送到 Discord webhook。

编辑:另一个答案中的评论声称 SIGINT 将导致 Minecraft 服务器正常停止,就像发送命令一样stop。如果是这样,那么只发送 SIGINT 而不是使用 RCON 来停止服务器会更简单,因此可以说更好。RCON 对于其他管理任务仍然有用。