PM2 einen Server-Neustart überleben lassen (beide Hälften)
Der Server startete neu und die Website kam nicht zurück. Sie kam eine Stunde später zurück, als jemand ein Deployment auslöste. Das sieht nach einem Deployment-Problem aus und ist keines: dieses Deployment hat schlicht wieder von Hand pm2 start ausgeführt.
Damit Ihre Anwendung von selbst startet, braucht es zwei getrennte Dinge, und jedes ist ohne das andere wertlos.
Die zwei Hälften
pm2 save schreibt die aktuelle Prozessliste nach ~/.pm2/dump.pm2. PM2 startet wieder, was in dieser Datei steht, und sonst nichts. Ein Deploy-Skript, das pm2 delete und pm2 start ausführt, aber nie speichert, hinterlässt einen veralteten Dump — oder einen leeren, falls er nie geschrieben wurde.
pm2 startup installiert eine systemd-Unit, die beim Booten pm2 resurrect ausführt und genau diesen Dump liest.
Das eine ohne das andere ergibt einen Bootvorgang, der nichts startet, oder eine gespeicherte Liste, die niemand liest.
Die Falle
pm2 startup als unprivilegierter Benutzer auszuführen installiert überhaupt nichts. Der Befehl erkennt systemd, berechnet den Befehl, der die Unit installieren würde, gibt ihn aus und endet. Die Ausgabe ist lang, grün und schließt mit einer Zeile darüber, dass man den Befehl kopieren kann. Das liest sich wie Erfolg. Passiert ist nichts.
Also: als root ausführen und mit systemd prüfen, nicht mit dem, was PM2 ausgegeben hat.
Die richtige Reihenfolge
Die Anwendung muss laufen, bevor Sie speichern, sonst speichern Sie eine leere Liste.
# als <user> — prüfen, dass die Anwendung läuft
pm2 status
# als root — die Unit für diesen Benutzer installieren
env PATH=$PATH:/usr/bin pm2 startup systemd -u <user> --hp /home/<user>
Das Präfix env PATH=$PATH:/usr/bin ist keine Zierde. Der PATH von root enthält normalerweise kein Node, und die Unit merkt sich den Pfad, mit dem sie erzeugt wurde. Ohne das Präfix existiert die Unit, findet beim Booten aber kein node.
# wieder als <user> — den Dump schreiben
pm2 save
Beide Hälften einzeln prüfen
Sie fallen unabhängig voneinander aus, also prüft man sie unabhängig voneinander.
systemctl is-enabled pm2-<user>
Das muss enabled ausgeben. Alles andere, ob disabled oder Failed to get unit file state, heißt, dass die Unit nie installiert wurde, egal was PM2 gemeldet hat.
grep -o '"name":"[^"]*"' /home/<user>/.pm2/dump.pm2
Hier muss <app> auftauchen. Existiert die Datei nicht oder gibt grep nichts aus, lief pm2 save nie, während die Anwendung online war.
Ohne Neustart testen
Sie müssen keine Produktivmaschine neu starten, um zu wissen, ob das funktioniert.
# als <user>
pm2 kill
# als root
systemctl start pm2-<user>
# als <user>
pm2 status
curl -sf http://127.0.0.1:3000/health
Ist <app> wieder online, funktionieren beide Hälften. Genau das passiert beim Booten.
Führen Sie nach pm2 kill niemals pm2 save aus. Bei getötetem Daemon ist die Prozessliste leer, und das Speichern schreibt diese Leere in den Dump und macht damit genau das kaputt, was Sie gerade testen wollten. Wenn es reflexhaft doch passiert: Anwendung wieder starten und pm2 save erneut ausführen.
pm2 save gehört ins Deploy-Skript
Der Dump veraltet, sobald ein Deployment den Prozess verändert. Wenn Ihr Skript löscht und neu startet, muss es danach speichern:
pm2 delete <app> || true
pm2 start ecosystem.config.cjs --only <app>
pm2 save
Das || true zählt beim ersten Durchlauf, wenn es nichts zu löschen gibt und pm2 delete mit einem Exit-Code ungleich null endet, was unter set -e das ganze Skript abbricht.
Ohne diese letzte Zeile funktioniert alles darüber weiter, bis zum ersten Deployment, das die Anwendung umbenennt, die Zahl der Instanzen ändert oder den Einstiegspunkt austauscht. Danach beschreibt der Dump einen Prozess, den es nicht mehr gibt, und der nächste Neustart erweckt genau den wieder.
Noch etwas zur Boot-Reihenfolge
Die erzeugte Unit trägt After=network.target. Sie ist nicht nach Ihrer Datenbank einsortiert. Bei einem Kaltstart kann die Anwendung starten, bevor MariaDB oder PostgreSQL Verbindungen annimmt, bei der ersten Abfrage scheitern und sich beenden.
Meist löst sich das von selbst: PM2 startet den Prozess neu, beim zweiten oder dritten Versuch steht die Datenbank, und die Website kommt hoch. Das Anzeichen ist ein Neustartzähler über null bei einem ansonsten gesunden Prozess, in der Spalte ↺ von pm2 status.
Erholt sie sich nicht, ordnen Sie die Unit ausdrücklich ein:
sudo systemctl edit pm2-<user>
[Unit]
After=network.target mariadb.service
Nur hinzufügen, wenn Sie das Scheitern gesehen haben. Eine Unit hinter einen langsamen Dienst zu hängen, oder hinter einen auf einem anderen Host, tauscht einen Boot, der es erneut versucht, gegen einen, der hängen bleibt.