Ein fehlgeschlagener Start von OpenVPN kommt oft als Ressourcenbeschwerde samt gut gemeinter Empfehlung, ein Limit anzuheben, und die Empfehlung hilft nur etwa in der Hälfte der Fälle. Hinter diesen Fehlern stehen zwei unabhängige Zähler. Der eine ist ein Kernel-Limit für die Anzahl der Tasks, die eine reale Benutzer-ID besitzt, und wird beim Ausführen des Prozesses geerbt. Der andere ist ein cgroup-Budget, das der Dienstmanager auf eine Unit und alles, was sie forkt, anwendet. Den effektiven Wert beider zu lesen, für die Unit, die tatsächlich läuft, kostet weniger als eine Vermutung.

Zwei Zähler, zwei Fragen

LimitNPROC ist RLIMIT_NPROC unter einem systemd-Namen, und seine Abrechnungseinheit ist die reale Benutzer-ID. Jeder Prozess, den dieser Benutzer besitzt, zählt gegen dieselbe Summe, einschließlich Arbeit, die von anderen Units, von einer Anmeldesitzung oder von einem Scheduler-Job gestartet wurde. Das Limit ist daher keine Obergrenze pro Dienst, selbst wenn es innerhalb einer Service-Unit gesetzt ist, und ein Dienst, der für jeden Client einen Helfer forkt, konkurriert mit allem anderen, was unter diesem Konto läuft. Ein ausgelasteter Cron-Job unter demselben Benutzer kann eine VPN-Unit über eine Linie schieben, die sie allein nie überschritten hat.

TasksMax hat einen anderen Eigentümer: den pids-Controller am cgroup der Unit, der Tasks statt Prozesse zählt. Threads sind Tasks, also kann ein scheinbar einthreadiger Daemon mit einem Worker-Pool ein Budget verbrauchen, das auf den ersten Blick weit größer wirkt als seine Prozesszahl. EffectiveTasksMax meldet, was die cgroup-Hierarchie tatsächlich zulässt, nachdem die übergeordneten Slices mitgeredet haben.

Warum die Root-Ausnahme die Antwort ändert

Der Kernel wendet RLIMIT_NPROC nicht auf einen Prozess an, der CAP_SYS_RESOURCE oder CAP_SYS_ADMIN trägt, weshalb eine Unit, die als root läuft, ohne Privilegien abzugeben, durch diese Einstellung praktisch unbegrenzt ist. Diese einzige Ausnahme erklärt die widersprüchlichen Ratschläge, die Leute austauschen: LimitNPROC anzuheben repariert ein Deployment, das Privilegien abgegeben hat, und bewirkt bei einem, das privilegiert blieb, überhaupt nichts. Prüfen Sie User= und ob der Daemon nach dem Start die Identität wechselt, bevor Sie dieser Einstellung Verdienst oder Schuld zuschreiben. Eine privilegierte Unit, die später auf ein unprivilegiertes Konto wechselt, fällt im Moment des Wechsels wieder unter das Limit, was auch die Zeitachse des Fehlers lesenswert macht.

Die Fehlerstufe grenzt es weiter ein. Eine Ablehnung während der Ausführungsvorbereitung, ein vom Daemon selbst gemeldeter Fork-Fehler und eine abgelehnte Task-Erstellung innerhalb eines cgroups erzeugen unterschiedliche Journal-Zeilen, und nur eine davon wird mit einem größeren Budget beantwortet.

Lesen Sie die effektiven Werte, nicht die der Shell

ulimit -u einer interaktiven Shell beschreibt diese Shell. Es beschreibt nicht, was der Dienstmanager dem Daemon übergeben hat, und sagt nichts über die cgroup-Abrechnung. Fragen Sie stattdessen die Unit ab, mit dem Instanznamen, der auf dem Host existiert, statt mit einem Beispiel aus einer Anleitung.

systemctl show [email protected] -p User -p LimitNPROC -p TasksCurrent -p TasksMax -p EffectiveTasksMax
cat /proc/$(systemctl show -p MainPID --value [email protected])/limits | grep -i processes

In einem Container braucht die Antwort zwei weitere Messungen, weil ein innerer Wert von infinity nur die Sicht des Gasts beschreibt. Fragen Sie den Host-Eigentümer nach der eigenen pids-Obergrenze des Containers und bestätigen Sie, ob der innere Dienstmanager diesen Controller überhaupt delegieren kann. Eine verschachtelte Unit kann kein Budget halten, das ihr übergeordnetes cgroup nie gewährt hat.

Bemessen Sie die Korrektur und belegen Sie sie unter Last

Wählen Sie eine Zahl aus der Arbeitslast statt aus dem Fehlertext. Zählen Sie die Prozesse, die der Daemon pro verbundenem Client oder pro Helferaufruf startet, multiplizieren Sie mit der Nebenläufigkeit, die das Deployment tragen soll, addieren Sie einen Puffer für administrative Sitzungen und vergleichen Sie diese Zahl dann mit den beiden effektiven Werten. Eine lokale Drop-in-Überschreibung hält die Änderung prüfbar und übersteht Paketaktualisierungen, während das Bearbeiten einer mitgelieferten Unit-Datei die Änderung verbirgt, bis das nächste Update sie ersetzt. Schreiben Sie die Rechnung neben die Überschreibung, denn die nächste Person, die sie anhebt oder senkt, braucht die Annahmen und nicht die endgültige Zahl.

Budgetänderungen werden bei der Ausführung angewendet, sodass ein bloßes Neuladen bereits laufende Prozesse nicht neu dimensioniert. Starten Sie die Unit über ihren normalen Lebenszyklus neu, akzeptieren Sie die Sitzungsunterbrechung, und lesen Sie die Zähler auf der neuen Instanz erneut.

sudo systemctl daemon-reload
sudo systemctl restart [email protected]
systemctl show [email protected] -p TasksCurrent -p EffectiveTasksMax
sudo journalctl -u [email protected] -b --no-pager | tail -n 20

Was die Reparatur belegt, ist eine neu gestartete Unit, deren Taskzahl unter beiden Obergrenzen bleibt, während sie die beabsichtigte Last trägt, plus ein Journal, das auf derselben Stufe keine Task- oder Fork-Erschöpfung mehr meldet. Bewahren Sie die Vorher-und-nachher-Zahlen bei der Änderung auf, damit die nächste Untersuchung mit einer Messung beginnt statt mit demselben Ratschlag. Wenn die Zahl die Obergrenze weiterhin erreicht, war die Rechnung falsch, statt dass das Limit zufällig zu klein war, und dieser Unterschied lohnt sich vor dem nächsten Neustart zu kennen.

Ähnliche Artikel