Minecraft server jako systemd service s výhodami
Minecraft server je vše, jen ne připraven na provoz na serveru. Samotný server lze spustit v příkazovém prostředí, ale není snadné ho provozovat na pozadí, protože administrativní příkazy očekává vkládané na stdin, nejlépe přímo z klávesnice.
Toto se často řeší tak, že se mc server uzavírá do samostatné tmux / screen session. A pomocí funkcí těchto prostředí se potom na stdin posílají jednotlivé příkazy. Výstup je obvykle ztracen.
Mě tento postup příliš nevyhovoval, potřeboval jsem mc server spouštět na pozadí s možností logovat jeho výstup a zachovat možnost posílat příkazy na stdin.
Systemd tento problém řeší, bohužel ne zcela uspokojivým způsobem. Systemd umožňuje tzv. socket activation kde systemd poslouchá na daném socketu (může se jednat o TCP nebo o UNIX socket) a jakmile něco přijde na vstup, tak teprve spustit service a daný socket předat danému procesu.
Ta druhá schopnost se nám bude velmi hodit, ale ta první je trochu nepřijemná, k tomu se dostanu na konci.
Požadavky
Shrňmě si tedy čeho chceme dosáhnout:
- Korektně startovat a ukončit proces mc serveru
- Možnost vidět a logovat výstup z procesu mc serveru
- Možnost posílat na stdin příkazy
První bod je v případě unity .service splněn tak nějak automaticky.
Druhý bod vlastně také, systemd ukládá každý výstup na stdout a strerr do
journálu. Tam lze prohlížet záznamy pomocí journalctl, v tomto případě se hodí
journalctl -f -u mc
a nechat vypisovat log průběžně.
Takže zbývá k řešení třetí bod. Potřebujeme ideálně FIFO, kam budeme posílat
příkazy, třeba ve formě echo 'op user' > mc.socket
a toto napojit na stdin
procesu mc serveru.
Service
Nejprve potřebujeme definovat service. Takže např. /etc/systemd/system/tekkit.service
:
[Unit]
Description=Minecraft Tekkit Server
[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/home/minecraft/Tekkit/
Environment="JAVA_HOME=/usr/java"
ExecStart=/usr/java/bin/java -Xmx4G -Xms4G \
-XX:+UseConcMarkSweepGC \
-XX:+UseParNewGC \
-XX:+CMSIncrementalPacing \
-XX:ParallelGCThreads=4 \
-XX:+AggressiveOpts \
-jar Tekkit.jar nogui
StandardInput=socket
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
User
, Group
, Workdir
a ExecStart
jsou asi jasné, ale v této unitě jsou
navíc definice Standard Input, Output a Error
. stdin
jsme napojili na
socket (který budeme ještě definovat), stdout
a stderr
potom do journalu
(což je default a zde to uvádím pro úplnost).
Socket
Soubor /etc/systemd/system/tekkit.socket
(soubor se musí jmenovat stejně jako .service):
[Socket]
ListenFIFO=/home/minecraft/Tekkit/tekkit.socket
Aktivace
systemctl enable tekkit.socket
systemctl start tekkit.socket
systemctl start tekkit.service
A potom je možné posílat do socketu příkazy a mc je dostane na stdin.
echo "say hello" > /home/minecraft/Tekkit/tekkit.socket
A výpisy můžeme sledovat pomocí:
journalctl -u tekkit -f
Nevýhoda
Výše popsané řešení funguje, ovšem s jedním problémem.
Předávání socketu funguje pouze ve spojení se socket activation. V aktuální verzi systemd to nelze oddělit. Což má za následek to, že pokud je service vypnutá a někdo něco pošle do socketu, tak se service nahodí. No jenže to něco může vypadat i takto:
echo "stop" > /home/minecraft/Tekkit/tekkit.socket
Po tomto “vtipném” příkazu se spustí service, nastartuje se celá mašinerie tekkitu (což prostě trvá) a až bude tekkit při vědomí, přijme příkaz stop a opět se ukončí.
Chápu, že pro mnohé lidi to nemusí být problém a že mohou socket activation
využít i rovnou ke spouštění této služby posláním neutrálního příkazu (třeba
say
). Ale já bych velmi ocenil, kdyby byla možnost předání socketu, ale bez
aktivace.
I s touto drobností tento způsob spouštění mc serveru považuji za vhodnější, než ty běžné postupy s tmux apod.