我一直在使用 sysV init 脚本运行 Minecraft 服务器。这是一个非常好的剧本;它在“屏幕”中运行 Minecraft;它可以确保 Minecraft 不会被启动两次;停止时它会等待 Minecraft 关闭。它甚至可以向 Minecraft 传递命令/etc/init.d/minecraft command <command>
——这对于计划备份很有用。
现在我已经升级到 Debian Jessie,它有 systemd。但现在,我保留了旧式脚本,因为它很棒。不过,我实际上非常支持 systemd - 它看起来确实有很多改进、简化和集中化。我记得 systemd 开发人员承诺“旧的 sysV 脚本将像以前一样工作”,但事实证明这并不那么容易!
我记得之前有一些启动脚本有问题;显然,仅仅将脚本放入 /etc/init.d 并将其标记为可执行已经不够了 - 我必须“启用”它们才能使它们工作。 “好吧,”我想,“现在它被 systemd 识别了,现在我可以通过 systemctl 控制它 - 它可能应该只使用我的旧脚本来处理命令!”事实证明我错了。
它无法正常启动,无法正常停止,无法正确显示状态,更不用说缺少“command”命令了。我开始寻找有关 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?..)
在这方面,我非常支持创新、支持 Poettering 和支持 systemd,并且我相信应该有一种方法可以比以前做得更好——也许是一种完全不同的方式,就像 Poettering 经常采用的方式(我喜欢他!)。但这看起来并不像是一个很大的改进——更像是一个巨大的倒退,需要大量的组装才能像以前一样继续下去。 “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 中的第一条规则,但 systemd 似乎并不关心它...... - 另外,我希望 systemd 能够管理服务关闭,而无需“等待 PID 终止”解决方法。在旧的 init 脚本中,我必须手动执行此操作,因为它是一个脚本,而 systemd 正在尝试消除对相同事物的复杂脚本的需求;它消除了手动编写所有超时脚本并杀死没有死掉的人的需要,但是“等待 pid 死掉”是下一个最常见的事情,我们仍然需要编写脚本。
答案2
tl;dr:根本不要使用屏幕。使用RCON来控制服务器。
我意识到屏幕一直是管理 Minecraft 服务器的事实上的标准,但在尝试了这两种方法之后,我得出的结论是 RCON 以一种更简洁的方式解决了控制问题。
在屏幕会话中运行服务器的原因是,您可以使用屏幕会话将命令填充到服务器的标准输入中,从而将命令发送到服务器。您还可以直接附加到屏幕会话以使用服务器的交互式控制台。这些都是重要的功能,但屏幕并不是实现它们的唯一方法。
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
如果您愿意,将通过环境变量而不是命令行参数接受它们。
其他注意事项:
- 您必须在
server.properties
文件中启用 RCON,但您可以在防火墙中关闭该端口以防止远程使用它。 - 要启动交互式终端会话,只需
rcon
不带参数运行(或直接调用 mcrcon)。 - 要查看服务器日志,请使用journalctl。我用它来将我的所有日志发送到 Discord webhook。
编辑:另一个答案中的评论声称 SIGINT 将导致 Minecraft 服务器正常停止,就像发送命令一样stop
。如果是这样,那么只发送 SIGINT 而不是使用 RCON 来停止服务器会更简单,因此可以说更好。 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