Bei einem Fehlstart zeigte das Skript `tail -30` des Logs — also das Ende eines Stacktrace, die Rahmenliste, gerade das was nichts erklaert. Jetzt wird der letzte Startversuch herausgeschnitten und daraus die Ursachenkette gezeigt; die tiefste Zeile ist die Antwort. Dazu ein Hinweis auf den einen Fall, der beim Umstieg zwangslaeufig auftritt: eine Datenbank aus der Zeit mit MODE=PostgreSQL passt nicht mehr und muss einmal weg. Ausserdem eine Neustart-Grenze in der Unit: Ohne sie laeuft ein kaputtes Deployment endlos im Kreis, `is-active` sagt dauerhaft "activating", und der eigentliche Fehler steht irgendwo weit oben im wachsenden Log. Mit ihr endet es nach fuenf Fehlstarts sichtbar in "failed". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
89 lines
3.5 KiB
Desktop File
89 lines
3.5 KiB
Desktop File
[Unit]
|
|
# Werkbaum — Backend als systemd-User-Unit (D77).
|
|
#
|
|
# Vorlage: scripts/deploy-backend.sh setzt __JAVA__, __DIR__, __XMX__ und
|
|
# __PORT__ ein und legt die Datei als ~/.config/systemd/user/werkbaum-backend.service
|
|
# ab. Ein User-Unit braucht kein root; dass er auch ohne offene Sitzung
|
|
# weiterläuft, liegt an `Linger=yes` — auf der Zielumgebung gemessen gesetzt
|
|
# (D76-Nachtrag 1). Fehlt es, hilft `loginctl enable-linger`.
|
|
#
|
|
# Die Pfade kommen als `%h/…` herein, nicht als `$HOME/…`: systemd expandiert
|
|
# in `WorkingDirectory` und `EnvironmentFile` **keine** Shell-Variablen. Ein
|
|
# `$HOME` stünde dort wörtlich, und der Dienst startete nicht — der Fehler
|
|
# säße in einer Datei, die man nur auf dem Server zu sehen bekommt.
|
|
Description=Werkbaum backend
|
|
After=network.target
|
|
|
|
# Nach fuenf Fehlstarts in fuenf Minuten gibt systemd auf. Ohne diese Grenze
|
|
# laeuft ein kaputtes Deployment endlos im Kreis: `is-active` sagt dann
|
|
# dauerhaft „activating", das Log waechst, und der eigentliche Fehler steht
|
|
# irgendwo weit oben. Mit ihr endet es sichtbar in „failed".
|
|
StartLimitIntervalSec=300
|
|
StartLimitBurst=5
|
|
|
|
[Service]
|
|
Type=simple
|
|
WorkingDirectory=__DIR__
|
|
|
|
# Zugangsdaten kommen aus einer Datei mit Modus 600, nie aus dem Repository
|
|
# und nie aus der Unit selbst (`systemctl --user show` gäbe sie sonst preis).
|
|
EnvironmentFile=__DIR__/env
|
|
|
|
# Speicher ist auf diesem Host die knappe Größe, nicht die Rechenzeit
|
|
# (D76-Nachtrag 3): 3,9 GB gesamt, davon rund 300 MB frei, ein weiteres GB
|
|
# liegt schon im Swap. Die cgroup-Grenze des Pakets (3147 MB) ist eine
|
|
# Erlaubnis, keine Reservierung; bemessen wird am Freien.
|
|
#
|
|
# Gemessen mit 30 angelegten Dokumenten und anschließendem GC (D77):
|
|
#
|
|
# ohne Angaben RSS 291 MB (Heap belegt 359 MB)
|
|
# nur -Xmx384m RSS 254 MB (Heap belegt 359 MB)
|
|
# -Xmx192m -Xms48m + Ratios RSS 174 MB (Heap belegt 46 MB)
|
|
# dito mit G1 statt Serial RSS 194 MB
|
|
#
|
|
# Der große Hebel sind NICHT die Obergrenzen, sondern die
|
|
# Freiraum-Verhältnisse: Ohne sie behält der Kollektor den einmal gewachsenen
|
|
# Heap, obwohl nach einem GC nur ~45 MB leben. Mit ihnen gibt er ihn zurück.
|
|
# SerialGC statt G1 spart weitere 20 MB und kostet bei zehn Beobachtern nichts,
|
|
# was auffiele.
|
|
#
|
|
# ExitOnOutOfMemoryError: lieber ein sauberer Neustart durch systemd als ein
|
|
# Dienst, der noch läuft und nichts mehr beantwortet.
|
|
ExecStart=__JAVA__ \
|
|
-Xmx__XMX__ \
|
|
-Xms48m \
|
|
-XX:MinHeapFreeRatio=20 \
|
|
-XX:MaxHeapFreeRatio=40 \
|
|
-Xss512k \
|
|
-XX:MaxMetaspaceSize=192m \
|
|
-XX:ReservedCodeCacheSize=64m \
|
|
-XX:MaxDirectMemorySize=64m \
|
|
-XX:+UseSerialGC \
|
|
-XX:+ExitOnOutOfMemoryError \
|
|
-Dserver.port=__PORT__ \
|
|
-Dserver.address=127.0.0.1 \
|
|
-jar __DIR__/werkbaum-backend.jar
|
|
|
|
# Der Dienst lauscht nur auf 127.0.0.1 (siehe -Dserver.address): Von außen
|
|
# kommt man ausschließlich über den Apache-Proxy, und damit gilt dessen
|
|
# HTTPS — das Master-Passwort geht nie im Klartext über das Netz.
|
|
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
# Beim Stoppen erst freundlich fragen; ein laufender Long-Poll hält bis zu
|
|
# 25 s, deshalb darf das Beenden etwas dauern.
|
|
TimeoutStopSec=40
|
|
KillSignal=SIGTERM
|
|
|
|
# Bewusst ohne die Sandbox-Optionen (`PrivateTmp`, `ProtectHome` und
|
|
# Verwandte): In einem User-Unit brauchen sie unprivilegierte
|
|
# Benutzer-Namensräume, und wo die abgeschaltet sind, startet der Dienst
|
|
# gar nicht erst. `NoNewPrivileges` ist ein schlichtes prctl und geht immer.
|
|
NoNewPrivileges=true
|
|
|
|
StandardOutput=append:__DIR__/backend.log
|
|
StandardError=append:__DIR__/backend.log
|
|
|
|
[Install]
|
|
WantedBy=default.target
|