paint-brush
Micro-DevOps mit Systemd: Laden Sie jeden gewöhnlichen Linux-Server aufby@tylerjl
5,185
5,185

Micro-DevOps mit Systemd: Laden Sie jeden gewöhnlichen Linux-Server auf

Tyler6m2023/09/05
Read on Terminal Reader
Read this story w/o Javascript

Container-Orchestratoren wie Kubernetes bieten eine schwindelerregende Vielfalt an Funktionen, aber müssen Sie die Kosten für zusätzliche Komplexität bezahlen? Erfahren Sie, wie moderne Funktionen von systemd dabei helfen können, die Sicherheit und Ausfallsicherheit für Workloads unter Linux zu verwalten, damit Sie in kleinerem Maßstab effizient arbeiten können.
featured image - Micro-DevOps mit Systemd: Laden Sie jeden gewöhnlichen Linux-Server auf
Tyler HackerNoon profile picture
0-item
1-item

Plattformen wie Kubernetes, Nomad oder jede in der Cloud gehostete Platform-as-a-Service (Paas) bieten eine Vielzahl leistungsstarker Funktionen. Von der Skalierung von Arbeitslasten über die Verwaltung von Geheimnissen bis hin zu Bereitstellungsstrategien sind diese Arbeitslast-Orchestratoren darauf optimiert, die Skalierung der Infrastruktur auf unterschiedliche Weise zu unterstützen.


Aber müssen Betreiber immer die Kosten für maximale Skalierbarkeit bezahlen? Manchmal überwiegen die Kosten von Komplexität und Abstraktion ihre Vorteile. Viele Entwickler verlassen sich stattdessen auf radikal einfache Bereitstellungsarchitekturen, um die Verwaltung zu vereinfachen. Zwei virtuelle private Server hinter einem Load Balancer sind ein wesentlich einfacher zu verwaltender Stack im Vergleich zu einem weitläufigen Cluster von Mikroservern über eine Flotte von Container-Hosts. Dies kann sich auszahlen, wenn bei Problemen weniger bewegliche Teile zu debuggen oder zu aktualisieren sind, wenn es an der Zeit ist, sie zu warten.


Die Grundlage für viele moderne Linux-Distributionen ist systemd und verfügt über eine Reihe leistungsstarker Funktionen, die oft mit Container-Orchestratoren oder PaaS-Systemen vergleichbar sind. In diesem Artikel untersuchen wir, wie Sie die neuesten Systemd-Funktionen nutzen können, um viele der Fähigkeiten dieser anderen großen Systeme ohne Verwaltungsaufwand zu nutzen und jeden gewöhnlichen Linux- Server zu einer sehr leistungsfähigen Anwendungsplattform zu machen.

Ein systemischer Primer

Auf einem einzelnen Host ist das Schreiben einer systemd .service Datei eine ideale Möglichkeit, einen verwalteten Prozess auszuführen. Meistens müssen Sie die Anwendung überhaupt nicht ändern: systemd unterstützt eine Vielzahl verschiedener Arten von Diensten und kann sich entsprechend anpassen.


Betrachten Sie beispielsweise diesen einfachen .service , der definiert, wie ein einfacher Webdienst ausgeführt wird:


 [Unit] Description=a simple web service [Service] ExecStart=/usr/bin/python3 -m http.server 8080


Denken Sie an die Standardeinstellungen für systemd-Dienste: ExecStart= muss ein absoluter Pfad sein, Prozesse sollten nicht in den Hintergrund verzweigen und Sie müssen möglicherweise erforderliche Umgebungsvariablen mit der Option Environment= festlegen.


Wenn es in eine Datei wie /etc/systemd/system/webapp.service eingefügt wird, wird ein Dienst erstellt, den Sie mit systemctl steuern können:


  • systemctl start webapp startet den Prozess.
  • systemctl status webapp zeigt an, ob der Dienst ausgeführt wird, seine Betriebszeit und die Ausgabe von stderr und stdout sowie die ID des Prozesses und andere Informationen.
  • systemctl stop webapp beendet den Dienst.


Darüber hinaus werden alle auf stderr und stdout gedruckten Ausgaben von „journald“ aggregiert und sind über das Systemjournal (mit journalctl ) oder gezielt über das Flag --unit zugänglich:


 journalctl --unit webapp


Da Journald seinen Speicher standardmäßig rotiert und verwaltet, ist das Sammeln von Protokollen über das Journal eine gute Strategie zur Verwaltung des Protokollspeichers.


Im Rest dieses Artikels werden Optionen zur Verbesserung eines Dienstes wie diesem untersucht.

Geheimnisse

Container- Orchestratoren wie Kubernetes unterstützen die Möglichkeit, Geheimnisse sicher einzuschleusen: Werte, die aus sicheren Datenspeichern stammen und laufenden Arbeitslasten ausgesetzt werden. Sensible Daten wie API-Schlüssel oder Passwörter erfordern eine andere Behandlung als Umgebungsvariablen oder Konfigurationsdateien, um eine unbeabsichtigte Offenlegung zu vermeiden.


DieSystemd-Option LoadCredential= unterstützt das Laden vertraulicher Werte aus Dateien auf der Festplatte und deren sichere Bereitstellung für laufende Dienste. Wie gehostete Plattformen, die Geheimnisse remote verwalten, behandelt systemd Anmeldeinformationen anders als Werte wie Umgebungsvariablen, um sicherzustellen, dass sie sicher aufbewahrt werden.


Um ein Geheimnis in einen Systemd-Dienst einzufügen, platzieren Sie zunächst eine Datei, die den Geheimwert enthält, in einem Pfad im Dateisystem. Um beispielsweise einen API-Schlüssel für eine .service Einheit verfügbar zu machen, erstellen Sie eine Datei unter /etc/credstore/api-key um die Datei über Neustarts hinweg beizubehalten, oder unter /run/credstore/api-key , um zu vermeiden, dass die Datei dauerhaft bestehen bleibt (die Der Pfad kann beliebig sein, aber systemd behandelt diese credstore Pfade als Standard. In beiden Fällen sollte die Datei mit einem Befehl wie chmod 400 /etc/credstore/api-key eingeschränkte Berechtigungen haben.


Definieren Sie im Abschnitt [Service] der .service Datei die Option LoadCredential= und übergeben Sie ihr zwei durch einen Doppelpunkt ( : ) getrennte Werte: den Namen der Anmeldeinformationen und ihren Pfad. Um beispielsweise unsere /etc/credstore/api-key Datei „Token“ zu nennen, definieren Sie die folgende Systemd-Dienstoption:


 LoadCredential=token:/etc/credstore/api-key


Wenn systemd Ihren Dienst startet, wird das Geheimnis dem laufenden Dienst unter einem Pfad der Form ${CREDENTIALS_DIRECTORY}/token angezeigt, wobei ${CREDENTIALS_DIRECTORY} eine von systemd gefüllte Umgebungsvariable ist. Ihr Anwendungscode sollte jedes auf diese Weise definierte Geheimnis zur Verwendung in Bibliotheken oder Code einlesen, die sichere Werte wie API-Tokens oder Passwörter erfordern. In Python können Sie dieses Geheimnis beispielsweise mit Code wie dem folgenden lesen:


 from os import environ from pathlib import Path credentials_dir = Path(environ["CREDENTIALS_DIRECTORY"]) with Path(credentials_dir / "token").open() as f: secret = f.read().strip()


Anschließend können Sie die secret Variable mit dem Inhalt Ihres Secrets für alle Bibliotheken verwenden, die möglicherweise einen API-Token oder ein Passwort erfordern.

Neustarts

Eine weitere Funktion von Orchestratoren wie Nomad ist die Möglichkeit, einen abgestürzten Workload automatisch neu zu starten . Unabhängig davon, ob es sich um einen unbehandelten Anwendungsfehler oder eine andere Ursache handelt, ist der Neustart fehlgeschlagener Anwendungen eine sehr nützliche Funktion, die oft die erste Verteidigungslinie beim Entwurf einer ausfallsicheren Anwendung darstellt.


Die Option Restart= systemd steuert, ob systemd einen laufenden Prozess automatisch neu startet. Für diese Option gibt es mehrere mögliche Werte, aber für Basisdienste ist die Einstellung on-failure für die meisten Anwendungsfälle gut geeignet.


Eine weitere Einstellung, die Sie bei der Konfiguration des automatischen Neustarts berücksichtigen sollten, ist die Option RestartSec= , die vorgibt, wie lange systemd warten soll, bevor der Dienst erneut gestartet wird. Normalerweise sollte dieser Wert angepasst werden, um zu vermeiden, dass fehlgeschlagene Dienste in einer engen Schleife neu gestartet werden und möglicherweise zu viel CPU-Zeit für den Neustart von Prozessen verbraucht wird. Ein kurzer Wert, der nicht zu lange wartet, wie z. B. 5s reicht normalerweise aus.


Optionen wie RestartSec= , die Dauerperioden oder zeitbasierte Werte akzeptieren, können je nach Bedarf verschiedene Formate wie 5min 10s oder 1hour analysieren. Weitere Informationen finden Sie im Handbuch für systemd.time .


Schließlich bestimmen zwei weitere Optionen, wie aggressiv systemd versucht, ausgefallene Einheiten neu zu starten, bevor es schließlich aufgibt. StartLimitIntervalSec= und StartLimitBurst= steuern, wie oft eine Einheit innerhalb eines bestimmten Zeitraums starten darf. Zum Beispiel die folgenden Einstellungen:


 StartLimitBurst=5 StartLimitIntervalSec=10


Es ist nur zulässig, dass ein Gerät innerhalb eines Zeitraums von 10 Sekunden maximal fünfmal versucht, zu starten. Wenn der konfigurierte Dienst innerhalb von 10 Sekunden ein sechstes Mal versucht, zu starten, stoppt systemd den Versuch, das Gerät neu zu starten, und markiert ihn stattdessen als failed .


Wenn Sie alle diese Einstellungen kombinieren, könnten Sie die folgenden Optionen für Ihre .service Einheit einbeziehen, um automatische Neustarts zu konfigurieren:


 [Unit] StartLimitBurst=5 StartLimitIntervalSec=10 [Service] Restart=on-failure RestartSec=1


Diese Konfiguration startet einen Dienst neu, wenn er fehlschlägt – das heißt, er wird unerwartet beendet, z. B. mit einem Exit-Code ungleich Null –, nachdem eine Sekunde gewartet wurde, und stoppt den Versuch, den Dienst neu zu starten, wenn er im Verlauf mehr als fünf Mal versucht, zu starten von 10 Sekunden.

Service-Härtung

Einer der Hauptvorteile der Ausführung innerhalb eines Containers ist das Sicherheits-Sandboxing. Durch die Segmentierung eines Anwendungsprozesses vom zugrunde liegenden Betriebssystem ist es viel schwieriger, etwaige im Dienst vorhandene Schwachstellen zu einer vollständigen Gefährdung zu eskalieren. Laufzeitumgebungen wie Docker erreichen dies durch eine Kombination aus Kontrollgruppen und anderen Sicherheitsprimitiven.


Sie können mehrere Systemd-Optionen aktivieren, um ähnliche Einschränkungen durchzusetzen, die dazu beitragen können, einen zugrunde liegenden Host vor unvorhersehbarem Arbeitslastverhalten zu schützen:


  • ProtectSystem= kann den Schreibzugriff auf sensible Systempfade wie /boot und /usr beschränken. Die Dokumentation für diese Option listet alle verfügbaren Optionen auf, aber im Allgemeinen ist die Einstellung dieser Option auf „ full eine vernünftige Standardeinstellung, um diese Dateisystempfade zu schützen.
  • ProtectHome= kann die Verzeichnisse /home , /root und /run/user mit der Einstellung read-only -only auf schreibgeschützt setzen oder sie, wenn auf true gesetzt, als leere Verzeichnisse in das Dateisystem des Dienstes einbinden. Sofern Ihre Anwendung nicht unbedingt auf diese Verzeichnisse zugreifen muss, kann die Einstellung auf „ true das System sicher gegen unrechtmäßigen Zugriff auf diese Verzeichnisse schützen.
  • PrivateTmp= verwaltet ein separates /tmp und /var/tmp für den konfigurierten Dienst, sodass temporäre Dateien für diesen Dienst und andere Prozesse privat bleiben. Sofern es keinen zwingenden Grund dafür gibt, dass Prozesse Informationen über temporäre Dateien austauschen, ist die Aktivierung dieser Option sinnvoll.
  • NoNewPrivileges= ist eine weitere sichere und unkomplizierte Möglichkeit, einen Dienst zu härten, indem sichergestellt wird, dass der ausgeführte Prozess seine Berechtigungen nicht erhöhen kann. Wenn Sie sich nicht sicher sind, ob andere Härtungsoptionen verwendet werden können, ist dies im Allgemeinen eine der am wenigsten problematischen Optionen.


Die Handbuchseite für systemd.exec ist eine hilfreiche Ressource zum Erkunden der verschiedenen Optionen, die für ausführbare Arbeitslasten wie Dienste gelten.

Weiterführende Literatur

Die Handbuchseiten für das systemd-Projekt sind umfangreich und nützlich, um sich über alle verfügbaren Optionen zum Ausführen eigener Anwendungen zu informieren. Unabhängig davon, ob Sie einen dauerhaften Dienst wie einen Webserver oder eine periodische .timer Einheit als Ersatz für einen Cron-Job ausführen, kann die Systemd-Dokumentation hilfreiche Anleitungen bieten.