One place for hosting & domains

      So bearbeiten Sie die Sudoers-Datei


      [*]

      Einführung

      Die Trennung von Berechtigungen ist eines der grundlegenden Sicherheitsparadigmen, die in Linux- und Unix-ähnlichen Betriebssystemen implementiert sind. Normale Benutzer arbeiten mit eingeschränkten Berechtigungen, um den Umfang ihres Einflusses auf ihre eigene Umgebung und nicht auf das breitere Betriebssystem zu reduzieren.

      Ein spezieller Benutzer namens root verfügt über Super-Benutzer-Berechtigungen. Dies ist ein Administratorkonto ohne die Einschränkungen, die für normale Benutzer gelten. Benutzer können Befehle mit Super-Benutzer- oder root-Berechtigungen auf verschiedene Arten ausführen.

      In diesem Artikel wird erläutert, wie Sie root-Berechtigungen korrekt und sicher erhalten. Ein besonderer Schwerpunkt liegt dabei auf der Bearbeitung der Datei /etc/sudoers.

      Wir werden diese Schritte auf einem Ubuntu 20.04-Server ausführen, aber die meisten modernen Linux-Distributionen wie Debian und CentOS sollten auf ähnliche Weise funktionieren.

      In diesem Leitfaden wird davon ausgegangen, dass Sie die hier beschriebene Ersteinrichtung des Servers bereits abgeschlossen haben. Melden Sie sich als regulärer Benutzer ohne root-Berechtigung bei Ihrem Server an und fahren Sie unten fort.

      Hinweis: Dieses Tutorial befasst sich ausführlich mit der Eskalation von Berechtigungen und der sudoers-Datei. Wenn Sie einem Benutzer nur sudo-Berechtigungen hinzufügen möchten, lesen Sie unsere Schnellstart-Tutorials zum Erstellen eines neuen Sudo-fähigen Benutzers für Ubuntu und CentOS.

      So erhalten Sie root-Berechtigungen

      Es gibt drei grundlegende Möglichkeiten, um root-Berechtigungen zu erhalten, die sich in ihrem Entwicklungsstand unterscheiden.

      Anmelden als root-Benutzer

      Die einfachste und unkomplizierteste Methode zum Abrufen von root-Berechtigungen besteht darin, sich direkt als root-Benutzer bei Ihrem Server anzumelden.

      Wenn Sie sich bei einem lokalen Computer anmelden (oder eine Out-of-Band-Konsolenfunktion auf einem virtuellen Server verwenden), geben Sie an der Anmeldeaufforderung root als Benutzernamen ein und geben Sie das root-Passwort ein, wenn Sie dazu aufgefordert werden.

      Wenn Sie sich über SSH anmelden, geben Sie den root-Benutzer vor der IP-Adresse oder dem Domänennamen in Ihrer SSH-Verbindungszeichenfolge an:

      • ssh root@server_domain_or_ip

      Wenn Sie keine SSH-Schlüssel für den root-Benutzer eingerichtet haben, geben Sie das root-Passwort ein, wenn Sie dazu aufgefordert werden.

      Verwenden Sie su, um ein root-Benutzer zu werden

      Die direkte Anmeldung als root-Benutzer wird normalerweise nicht empfohlen, da es einfach ist, das System für nicht administrative Aufgaben zu verwenden, was gefährlich ist.

      Der nächste Weg, um Superuser-Berechtigungen zu erhalten, ermöglicht es Ihnen, jederzeit der root-Benutzer zu werden, wie Sie es benötigen.

      Wir können dies tun, indem wir den Befehl su aufrufen, der für „Ersatzbenutzer“ steht. Geben Sie Folgendes ein, um root-Berechtigungen zu erhalten:

      Sie werden aufgefordert, das Kennwort des root-Benutzers einzugeben. Anschließend werden Sie in eine root-Shell-Sitzung versetzt.

      Wenn Sie die Aufgaben abgeschlossen haben, für die root-Berechtigungen erforderlich sind, kehren Sie zu Ihrer normalen Shell zurück, indem Sie Folgendes eingeben:

      Verwenden Sie sudo, um Befehle als root-Benutzer auszuführen

      Der letzte Weg, um root-Berechtigungen zu erhalten, den wir diskutieren werden, ist mit dem Befehl sudo.

      Mit dem Befehl sudo können Sie einmalige Befehle mit root-Berechtigungen ausführen, ohne eine neue Shell erstellen zu müssen. Es wird wie folgt ausgeführt:

      Im Gegensatz zu su fordert der Befehl sudo das Passwort des aktuellen Benutzers an, nicht das root-Passwort.

      Aufgrund seiner Auswirkungen auf die Sicherheit wird Sudo-Zugriff Benutzern standardmäßig nicht gewährt und muss eingerichtet werden, bevor er ordnungsgemäß funktioniert. In unseren Schnellstart-Tutorials zum Erstellen eines neuen Sudo-fähigen Benutzers für Ubuntu und CentOS erfahren Sie, wie Sie einen Sudo-fähigen Benutzer einrichten.

      Im folgenden Abschnitt werden wir ausführlicher erläutern, wie Sie die Sudo-Konfiguration ändern können.

      Was ist Visudo?

      Der Befehl sudo wird über eine Datei unter /etc/sudoers konfiguriert.

      Warnung: Bearbeiten Sie diese Datei nie mit einem normalen Texteditor! Verwenden Sie stattdessen immer den Befehl visudo!

      Da eine falsche Syntax in der Datei /etc/sudoers zu einem Systembruch führen kann, bei dem es nicht möglich ist, erhöhte Berechtigungen zu erhalten, ist es wichtig, den Befehl visudo zum Bearbeiten der Datei zu verwenden.

      Der Befehl visudo öffnet wie gewohnt einen Texteditor, überprüft jedoch beim Speichern die Syntax der Datei. Dies verhindert, dass Konfigurationsfehler sudo-Vorgänge blockieren. Dies ist möglicherweise Ihre einzige Möglichkeit, root-Berechtigungen zu erhalten.

      Traditionell öffnet visudo die Datei /etc/sudoers mit dem vi-Texteditor. Ubuntu hat visudo jedoch so konfiguriert, dass stattdessen der nano-Texteditor verwendet wird.

      Wenn Sie ihn wieder in vi ändern möchten, geben Sie den folgenden Befehl ein:

      • sudo update-alternatives --config editor

      Output

      There are 4 choices for the alternative editor (providing /usr/bin/editor). Selection Path Priority Status ------------------------------------------------------------ * 0 /bin/nano 40 auto mode 1 /bin/ed -100 manual mode 2 /bin/nano 40 manual mode 3 /usr/bin/vim.basic 30 manual mode 4 /usr/bin/vim.tiny 10 manual mode Press <enter> to keep the current choice[*], or type selection number:

      Wählen Sie die Zaus, die der Auswahl entspricht, die Sie treffen möchten.

      Unter CentOS können Sie diesen Wert ändern, indem Sie Ihrem ~/.bashrc die folgende Zeile hinzufügen:

      • export EDITOR=`which name_of_editor`

      Geben Sie die Datei ein, um die Änderungen zu implementieren:

      Nachdem Sie visudo konfiguriert haben, führen Sie den Befehl aus, um auf die Datei /etc/sudoers zuzugreifen:

      So bearbeiten Sie die Sudoers-Datei

      In Ihrem ausgewählten Texteditor wird die Datei /etc/sudoers angezeigt.

      Ich habe die Datei von Ubuntu 18.04 kopiert und eingefügt, wobei Kommentare entfernt wurden. Die Datei CentOS /etc/sudoers enthält viele weitere Zeilen, von denen einige in diesem Leitfaden nicht behandelt werden.

      /etc/sudoers

      Defaults        env_reset
      Defaults        mail_badpass
      Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
      
      root    ALL=(ALL:ALL) ALL
      
      %admin ALL=(ALL) ALL
      %sudo   ALL=(ALL:ALL) ALL
      
      #includedir /etc/sudoers.d
      

      Werfen wir einen Blick darauf, was diese Zeilen bewirken.

      Standardzeilen

      In der ersten Zeile „Defaults env_reset“ wird die Terminalumgebung zurückgesetzt, um alle Benutzervariablen zu entfernen. Dies ist eine Sicherheitsmaßnahme, mit der potenziell schädliche Umgebungsvariablen aus der sudo-Sitzung entfernt werden.

      In der zweiten Zeile, wird mit dem Befehl Defaults mail_badpass das System angewiesen, Benachrichtigungen über fehlerhafte sudo-Passwortversuche an den konfigurierten mailto-Benutzer zu senden. Standardmäßig ist dies das root-Konto.

      Die dritte Zeile, die mit „Defaults Secure_path=…“ beginnt, gibt den Pfad an (die Stellen im Dateisystem, nach denen das Betriebssystem nach Anwendungen sucht), die für sudo-Operationen verwendet werden. Dies verhindert, dass Benutzerpfade verwendet werden, was schädlich sein kann.

      Benutzerberechtigungszeilen

      Die vierte Zeile, die die sudo-Berechtigungen des root-Benutzers vorschreibt, unterscheidet sich von den vorhergehenden Zeilen. Schauen wir uns an, was die verschiedenen Felder bedeuten:

      • root ALL=(ALL:ALL) ALL Das erste Feld gibt den Benutzernamen an, für den die Regel gilt (root).

      • root ALL=(ALL:ALL) ALL Das erste „ALL“ gibt an, dass diese Regel für alle Hosts gilt.

      • root ALL=(ALL:ALL) ALL Dieses „ALL“ gibt an, dass der root-Benutzer Befehle wie alle Benutzer ausführen kann.

      • root ALL=(ALL:ALL) ALL Dieses „ALL“ gibt an, dass der root-Benutzer Befehle wie alle Gruppen ausführen kann.

      • root ALL=(ALL:ALL) ALL Das letzte „ALL“ gibt an, dass diese Regeln für alle Befehle gelten.

      Dies bedeutet, dass unser root-Benutzer jeden Befehl mit sudo ausführen kann, solange er sein Passwort angibt.

      Gruppenberechtigungszeilen

      Die nächsten beiden Zeilen ähneln den Benutzerberechtigungszeilen, geben jedoch sudo-Regeln für Gruppen an.

      Namen, die mit % beginnen, geben Gruppennamen an.

      Hier sehen wir, dass die Administratorgruppe jeden Befehl als jeder Benutzer auf jedem Host ausführen kann. Ebenso hat die sudo-Gruppe die gleichen Berechtigungen, kann aber auch wie jede andere Gruppe ausgeführt werden.

      Inklusive Zeile /etc/sudoers.d

      Die letzte Zeile könnte auf den ersten Blick wie ein Kommentar aussehen:

      /etc/sudoers

      . . .
      
      #includedir /etc/sudoers.d
      

      Sie **beginnt mit einem #, das normalerweise einen Kommentar anzeigt. Diese Zeile zeigt jedoch tatsächlich an, dass Dateien im Verzeichnis /etc/sudoers.d ebenfalls bezogen und angewendet werden.

      Dateien in diesem Verzeichnis folgen denselben Regeln wie die Datei /etc/sudoers. Jede Datei, die nicht mit ~ endet und keinen . enthält, wird gelesen und an die sudo-Konfiguration angehängt.

      Dies ist hauptsächlich für Anwendungen gedacht, die die sudo-Berechtigungen bei der Installation ändern möchten. Wenn Sie alle zugehörigen Regeln in einer einzigen Datei im Verzeichnis /etc/sudoers.d ablegen, können Sie leicht erkennen, welche Berechtigungen welchen Konten zugeordnet sind, und Anmeldeinformationen einfach umkehren, ohne die Datei /etc/sudoers direkt bearbeiten zu müssen.

      Wie bei der Datei /etc/sudoers selbst sollten Sie Dateien im Verzeichnis /etc/sudoers.d immer mit visudo bearbeiten. Die Syntax zum Bearbeiten dieser Dateien lautet:

      • sudo visudo -f /etc/sudoers.d/file_to_edit

      So geben Sie einem Benutzer Sudo-Berechtigungen

      Die häufigste Operation, die Benutzer beim Verwalten von sudo-Berechtigungen ausführen möchten, besteht darin, einem neuen Benutzer allgemeinen sudo-Zugriff zu gewähren. Dies ist nützlich, wenn Sie einem Konto vollen Administratorzugriff auf das System gewähren möchten.

      Der einfachste Weg, dies auf einem System zu tun, das mit einer Allzweck-Verwaltungsgruppe wie dem Ubuntu-System in diesem Leitfaden eingerichtet ist, besteht darin, den betreffenden Benutzer zu dieser Gruppe hinzuzufügen.

      Unter Ubuntu 20.04 verfügt die sudo-Gruppe beispielsweise über vollständige Administratorrechte. Wir können einem Benutzer dieselben Berechtigungen gewähren, indem wir ihn der Gruppe wie folgt hinzufügen:

      • sudo usermod -aG sudo username

      Der Befehl gpasswd kann auch verwendet werden:

      • sudo gpasswd -a username sudo

      Beides wird dasselbe bewirken.

      Unter CentOS ist dies normalerweise die wheel-Gruppe anstelle der sudo-Gruppe:

      • sudo usermod -aG wheel username

      Oder unter Verwendung von gpasswd:

      • sudo gpasswd -a username wheel

      Wenn das Hinzufügen des Benutzers zur Gruppe unter CentOS nicht sofort funktioniert, müssen Sie möglicherweise die Datei /etc/sudoers bearbeiten, um den Gruppennamen zu kommentieren:

      /etc/sudoers

      . . .
      %wheel ALL=(ALL) ALL
      . . .
      

      So richten Sie benutzerdefinierte Regeln ein

      Nachdem wir uns mit der allgemeinen Syntax der Datei vertraut gemacht haben, erstellen wir einige neue Regeln.

      So erstellen Sie Alias-Dateien

      Die sudoers-Datei kann einfacher organisiert werden, indem Dinge mit verschiedenen Arten von „Aliases“ gruppiert werden.

      Zum Beispiel können wir drei verschiedene Benutzergruppen mit überlappender Mitgliedschaft erstellen:

      /etc/sudoers

      . . .
      User_Alias      GROUPONE = abby, brent, carl
      User_Alias      GROUPTWO = brent, doris, eric,
      User_Alias      GROUPTHREE = doris, felicia, grant
      . . .
      

      Gruppennamen müssen mit einem Großbuchstaben beginnen. Wir können dann Mitgliedern von GROUPTWO erlauben, die apt-Datenbank zu aktualisieren, indem wir eine Regel wie die folgende erstellen:

      /etc/sudoers

      . . .
      GROUPTWO    ALL = /usr/bin/apt-get update
      . . .
      

      Wenn Sie keinen Benutzer/keine Gruppe angeben, die wie oben ausgeführt werden soll, ist sudo standardmäßig der root-Benutzer.

      Wir können Mitgliedern von GROUPTHREE erlauben, den Computer herunterzufahren und neu zu starten, indem wir einen „Befehls-Alias“ erstellen und diesen in einer Regel für GROUPTHREE verwenden:

      /etc/sudoers

      . . .
      Cmnd_Alias      POWER = /sbin/shutdown, /sbin/halt, /sbin/reboot, /sbin/restart
      GROUPTHREE  ALL = POWER
      . . .
      

      Wir erstellen einen Befehls-Alias namens POWER, der Befehle zum Ausschalten und Neustarten des Computers enthält. Wir erlauben dann den Mitgliedern von GROUPTHREE, diese Befehle auszuführen.

      Wir können auch Aliase „Ausführen als“ erstellen, die den Teil der Regel ersetzen können, der den Benutzer angibt, der den Befehl ausführen soll:

      /etc/sudoers

      . . .
      Runas_Alias     WEB = www-data, apache
      GROUPONE    ALL = (WEB) ALL
      . . .
      

      Auf diese Weise kann jeder, der Mitglied von GROUPONE ist, Befehle als www-Datenbenutzer oder Apache-Benutzer ausführen.

      Denken Sie daran, dass spätere Regeln frühere Regeln überschreiben, wenn ein Konflikt zwischen beiden besteht.

      So sperren Sie Regeln

      Es gibt eine Reihe von Möglichkeiten, wie Sie mehr Kontrolle darüber erlangen können, wie sudo auf einen Anruf reagiert.

      Der dem mlocate-Paket zugeordnete Befehl updateb ist auf einem Einzelbenutzersystem relativ harmlos. Wenn wir Benutzern erlauben möchten, es mit root-Berechtigungen auszuführen, ohne ein Passwort eingeben zu müssen, können wir eine Regel wie die folgende festlegen:

      /etc/sudoers

      . . .
      GROUPONE    ALL = NOPASSWD: /usr/bin/updatedb
      . . .
      

      NOPASSWD ist ein „Tag“, das bedeutet, dass kein Passwort angefordert wird. Es hat einen Begleitbefehl namens PASSWD, der das Standardverhalten ist. Ein Tag ist für den Rest der Regel relevant, es sei denn, es wird später von seinem „Twin“ -Tag außer Kraft gesetzt.

      Zum Beispiel können wir eine Zeile wie diese haben:

      /etc/sudoers

      . . .
      GROUPTWO    ALL = NOPASSWD: /usr/bin/updatedb, PASSWD: /bin/kill
      . . .
      

      Ein weiteres hilfreiches Tag ist NOEXEC, mit dem in bestimmten Programmen gefährliches Verhalten verhindert werden kann.

      Beispielsweise können einige Programme, wie z. B. less, andere Befehle erzeugen, indem man dies über seine Benutzeroberfläche eingibt:

      !command_to_run
      

      Dies führt grundsätzlich jeden Befehl aus, den der Benutzer mit denselben Berechtigungen erteilt, unter denen less ausgeführt wird, was sehr gefährlich sein kann.

      Um dies einzuschränken, könnten wir eine Zeile wie die folgende verwenden:

      /etc/sudoers

      . . .
      username  ALL = NOEXEC: /usr/bin/less
      . . .
      

      Verschiedene Informationen

      Es gibt einige weitere Informationen, die beim Umgang mit sudo nützlich sein können.

      Wenn Sie in der Konfigurationsdatei einen Benutzer oder eine Gruppe als „Ausführen als“ angegeben haben, können Sie Befehle als diese Benutzer mithilfe der Flags -u bzw. -g ausführen:

      • sudo -u run_as_user command
      • sudo -g run_as_group command

      Praktischerweise speichert sudo Ihre Authentifizierungsdaten standardmäßig für einen bestimmten Zeitraum in einem Terminal. Dies bedeutet, dass Sie Ihr Passwort erst erneut eingeben müssen, wenn dieser Timer abgelaufen ist.

      Wenn Sie diesen Timer aus Sicherheitsgründen löschen möchten, wenn Sie mit dem Ausführen von Verwaltungsbefehlen fertig sind, können Sie Folgendes ausführen:

      Wenn Sie andererseits den Befehl sudo „vorbereiten“ möchten, damit Sie später nicht dazu aufgefordert werden, oder Ihren sudo-Mietvertrag erneuern möchten, können Sie jederzeit Folgendes eingeben:

      Sie werden aufgefordert, Ihr Passwort einzugeben, das für spätere sudo-Verwendungen zwischengespeichert wird, bis der sudo-Zeitrahmen abläuft.

      Wenn Sie sich nur fragen, welche Berechtigungen für Ihren Benutzernamen definiert sind, können Sie Folgendes eingeben:

      Dadurch werden alle Regeln in der Datei /etc/sudoers aufgelistet, die für Ihren Benutzer gelten. Dies gibt Ihnen eine gute Vorstellung davon, was Sie mit sudo als Benutzer tun dürfen oder nicht.

      Es gibt viele Fälle, in denen Sie einen Befehl ausführen und dieser fehlschlägt, weil Sie vergessen haben, ihm sudo voranzustellen. Um zu vermeiden, dass Sie den Befehl erneut eingeben müssen, können Sie eine Bash-Funktion nutzen, die „letzten Befehl wiederholen“ bedeutet:

      Das doppelte Ausrufezeichen wiederholt den letzten Befehl. Wir haben sudo vorangestellt, um den nicht privilegierten Befehl schnell in einen privilegierten Befehl umzuwandeln.

      Für ein bisschen Spaß können Sie Ihrer /etc/sudoers-Datei mit visudo die folgende Zeile hinzufügen:

      /etc/sudoers

      . . .
      Defaults    insults
      . . .
      

      Dies führt dazu, dass sudo eine dumme Beleidigung zurückgibt, wenn ein Benutzer ein falsches Passwort für sudo eingibt. Wir können sudo -k verwenden, um das vorherige zwischengespeicherte sudo-Passwort zu löschen und es auszuprobieren:

      Output

      [sudo] password for demo: # enter an incorrect password here to see the results Your mind just hasn't been the same since the electro-shock, has it? [sudo] password for demo: My mind is going. I can feel it.

      Zusammenfassung

      Sie sollten nun ein grundlegendes Verständnis für das Lesen und Ändern der sudoers-Datei und einen Überblick über die verschiedenen Methoden haben, mit denen Sie root-Berechtigungen erhalten können.

      Denken Sie daran, dass Superuser-Berechtigungen regulären Benutzern aus einem bestimmten Grund nicht gewährt werden. Es ist wichtig, dass Sie verstehen, was jeder Befehl tut, den Sie mit root-Berechtigungen ausführen. Übernehmen Sie die Verantwortung nicht leichtfertig. Erfahren Sie, wie Sie diese Tools am besten für Ihren Anwendungsfall verwenden und nicht benötigte Funktionen sperren können.

      [*]
      [*]Source link

      SOLID: Die ersten 5 Prinzipien des objektorientierten Designs


      Einführung

      SOLID ist ein Akronym für die ersten fünf Prinzipien des objektorientierten Designs (OOD) von Robert C. Martin (auch bekannt als Onkel Bob).

      Anmerkung: Obwohl diese Prinzipien auf verschiedene Programmiersprachen angewendet werden können, wird der in diesem Artikel enthaltene Beispielcode PHP verwendet.

      Diese Prinzipien legen Praktiken fest, die sich für die Entwicklung von Software mit Überlegungen zur Aufrechterhaltung und Erweiterung eignen, wenn das Projekt wächst. Die Übernahme dieser Praktiken kann auch zur Vermeidung von Code Smells, Refactoring von Code und agiler oder adaptiver Softwareentwicklung beitragen.

      SOLID steht für:

      In diesem Artikel werden Sie jedes Prinzip einzeln kennenlernen, um zu verstehen, wie SOLID Ihnen dabei helfen kann, ein besserer Entwickler zu werden.

      Single-Responsibility-Prinzip

      Das Single-Responsibility-Prinzip (SRP) besagt:

      Eine Klasse sollte einen und nur einen Grund haben, sich zu ändern, d. h. eine Klasse sollte nur eine Aufgabe haben.

      Betrachten Sie beispielsweise eine Anwendung, die eine Sammlung von Formen – Kreise und Quadrate – nimmt und die Summe der Fläche aller Formen in der Sammlung berechnet.

      Erstellen Sie zunächst die Formklassen und lassen Sie die Konstruktoren die erforderlichen Parameter einrichten.

      Für Quadrate müssen Sie die length einer Seite kennen:

      class Square
      {
          public $length;
      
          public function construct($length)
          {
              $this->length = $length;
          }
      }
      

      Für Kreise müssen Sie den radius kennen:

      class Circle
      {
          public $radius;
      
          public function construct($radius)
          {
              $this->radius = $radius;
          }
      }
      

      Erstellen Sie anschließend die Klasse AreaCalculator und schreiben Sie dann die Logik, um die Fläche aller bereitgestellten Formen zu summieren. Der Flächeninhalt eines Quadrats wird durch die Länge zum Quadrat berechnet. Der Flächeninhalt eines Kreises wird durch Pi mal Radius zum Quadrat berechnet.

      class AreaCalculator
      {
          protected $shapes;
      
          public function __construct($shapes = [])
          {
              $this->shapes = $shapes;
          }
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'Square')) {
                      $area[] = pow($shape->length, 2);
                  } elseif (is_a($shape, 'Circle')) {
                      $area[] = pi() * pow($shape->radius, 2);
                  }
              }
      
              return array_sum($area);
          }
      
          public function output()
          {
              return implode('', [
                '',
                    'Sum of the areas of provided shapes: ',
                    $this->sum(),
                '',
            ]);
          }
      }
      

      Um die Klasse AreaCalculator zu verwenden, müssen Sie die Klasse instanziieren und ein Array von Formen übergeben und die Ausgabe am Ende der Seite anzeigen.

      Hier ist ein Beispiel mit einer Sammlung von drei Formen:

      • Ein Kreis mit einem Radius von 2
      • Ein Quadrat mit einer Länge von 5
      • Ein zweites Quadrat mit einer Länge von 6
      $shapes = [
        new Circle(2),
        new Square(5),
        new Square(6),
      ];
      
      $areas = new AreaCalculator($shapes);
      
      echo $areas->output();
      

      Das Problem mit der Ausgabemethode ist, dass der AreaCalculator die Logik zur Ausgabe der Daten bearbeitet.

      Bedenken Sie ein Szenario, in dem die Ausgabe in ein anderes Format wie JSON konvertiert werden soll.

      Die gesamte Logik würde von der Klasse AreaCalculator bearbeitet werden. Dies würde gegen das Single-Responsibility-Prinzip verstoßen. Die Klasse AreaCalculator sollte nur mit der Summe der Flächen der bereitgestellten Formen befasst sein. Sie sollte sich nicht damit befassen, ob der Benutzer JSON oder HTML wünscht.

      Um dies zu beheben, können Sie eine separate Klasse SumCalculatorOutputter erstellen und diese neue Klasse verwenden, um die Logik zu bearbeiten, die Sie für die Ausgabe der Daten an den Benutzer benötigen:

      class SumCalculatorOutputter
      {
          protected $calculator;
      
          public function __constructor(AreaCalculator $calculator)
          {
              $this->calculator = $calculator;
          }
      
          public function JSON()
          {
              $data = [
                'sum' => $this->calculator->sum(),
            ];
      
              return json_encode($data);
          }
      
          public function HTML()
          {
              return implode('', [
                '',
                    'Sum of the areas of provided shapes: ',
                    $this->calculator->sum(),
                '',
            ]);
          }
      }
      

      Die Klasse SumCalculatorOutputter würde wie folgt funktionieren:

      $shapes = [
        new Circle(2),
        new Square(5),
        new Square(6),
      ];
      
      $areas = new AreaCalculator($shapes);
      $output = new SumCalculatorOutputter($areas);
      
      echo $output->JSON();
      echo $output->HTML();
      

      Jetzt wird die Logik, die Sie zur Ausgabe der Daten an den Benutzer benötigen, von der Klasse SumCalculatorOutputter bearbeitet.

      Das erfüllt das Single-Responsibility-Prinzip.

      Open-Closed-Prinzip

      Das Open-Closed-Prinzip (S.R.P.) besagt:

      Objekte oder Entitäten sollten offen für Erweiterungen, aber geschlossen für Änderungen sein.

      Das bedeutet, dass eine Klasse erweiterbar sein sollte, ohne die Klasse selbst zu modifizieren.

      Gehen wir noch einmal auf die Klasse AreaCalculator ein und konzentrieren uns auf die Methode sum:

      class AreaCalculator
      {
          protected $shapes;
      
          public function __construct($shapes = [])
          {
              $this->shapes = $shapes;
          }
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'Square')) {
                      $area[] = pow($shape->length, 2);
                  } elseif (is_a($shape, 'Circle')) {
                      $area[] = pi() * pow($shape->radius, 2);
                  }
              }
      
              return array_sum($area);
          }
      }
      

      Bedenken Sie ein Szenario, in dem der Benutzer die Summe sum zusätzlicher Formen wie Dreiecke, Fünfecke, Sechsecke usw. wünscht. Sie müssten diese Datei ständig bearbeiten und weitere if/else-Blöcke hinzufügen. Das würde das Open-Closed-Prinzip verletzen.

      Eine Möglichkeit, diese Methode sum zu verbessern, besteht darin, die Logik zur Berechnung der Fläche jeder Form aus der Klassenmethode AreaCalculator zu entfernen und sie an die Klasse jeder Form anzuhängen.

      Hier ist die in Square definierte Methode area:

      class Square
      {
          public $length;
      
          public function __construct($length)
          {
              $this->length = $length;
          }
      
          public function area()
          {
              return pow($this->length, 2);
          }
      }
      

      Und hier ist die in Circle definierte Methode area:

      class Circle
      {
          public $radius;
      
          public function construct($radius)
          {
              $this->radius = $radius;
          }
      
          public function area()
          {
              return pi() * pow($shape->radius, 2);
          }
      }
      

      Die Methode sum für AreaCalculator kann dann umgeschrieben werden als:

      class AreaCalculator
      {
          // ...
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  $area[] = $shape->area();
              }
      
              return array_sum($area);
          }
      }
      

      Jetzt können Sie eine andere Formklasse erstellen und diese bei der Berechnung der Summe übergeben, ohne den Code zu verändern.

      Es ergibt sich jedoch ein weiteres Problem. Woher wissen Sie, dass das an den AreaCalculator übergebene Objekt tatsächlich eine Form ist oder ob die Form eine Methode namens area aufweist?

      Die Codierung auf eine Schnittstelle ist ein integraler Bestandteil von SOLID.

      Erstellen Sie ein ShapeInterface, das area unterstützt:

      interface ShapeInterface
      {
          public function area();
      }
      

      Ändern Sie Ihre Formklassen, um das ShapeInterface mit implement zu implementieren.

      Hier ist die Aktualisierung für Square:

      class Square implements ShapeInterface
      {
          // ...
      }
      

      Und hier ist die Aktualisierung für Circle:

      class Circle implements ShapeInterface
      {
          // ...
      }
      

      In der Methode sum für AreaCalculator können Sie überprüfen, ob die bereitgestellten Formen tatsächlich Instanzen des ShapeInterface sind; andernfalls verwenden Sie „throw“ für eine Ausnahme:

       class AreaCalculator
      {
          // ...
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'ShapeInterface')) {
                      $area[] = $shape->area();
                      continue;
                  }
      
                  throw new AreaCalculatorInvalidShapeException();
              }
      
              return array_sum($area);
          }
      }
      

      Damit ist das Open-Closed-Prinzip erfüllt.

      Liskovsches Substitutionsprinzip

      Das Liskovsche Substitutionsprinzip besagt:

      Lassen Sie q(x) eine Eigenschaft sein, die für Objekte x von Typ T beweisbar ist. Dann soll q(y) für Objekte y von Typ S beweisbar sein, wobei S ein Untertyp von T ist.

      Das bedeutet, dass jede Unterklasse oder abgeleitete Klasse für ihre Basis- oder übergeordnete Klasse ersetzbar sein sollte.

      Bedenken Sie, aufbauend auf dem Beispiel der Klasse AreaCalculator, eine neue Klasse VolumeCalculator, die die Klasse AreaCalculator erweitert:

      class VolumeCalculator extends AreaCalculator
      {
          public function construct($shapes = [])
          {
              parent::construct($shapes);
          }
      
          public function sum()
          {
              // logic to calculate the volumes and then return an array of output
              return [$summedData];
          }
      }
      

      Erinnern Sie sich daran, dass die Klasse SumCalculatorOutputter dem ähnelt:

      class SumCalculatorOutputter {
          protected $calculator;
      
          public function __constructor(AreaCalculator $calculator) {
              $this->calculator = $calculator;
          }
      
          public function JSON() {
              $data = array(
                  'sum' => $this->calculator->sum();
              );
      
              return json_encode($data);
          }
      
          public function HTML() {
              return implode('', array(
                  '',
                      'Sum of the areas of provided shapes: ',
                      $this->calculator->sum(),
                  ''
              ));
          }
      }
      

      Wenn Sie versuchen würden, ein Beispiel wie dieses auszuführen:

      $areas = new AreaCalculator($shapes);
      $volumes = new VolumeCalculator($solidShapes);
      
      $output = new SumCalculatorOutputter($areas);
      $output2 = new SumCalculatorOutputter($volumes);
      

      Wenn Sie die Methode HTML auf dem Objekt $output2 aufrufen, erhalten Sie einen Fehler E_NOTICE, der Sie über eine Array-zu-String-Konvertierung informiert.

      Um dies zu beheben, geben Sie anstelle der Rückgabe eines Arrays aus der Summenmethode der Klasse VolumeCalculator $summedData zurück:

      class VolumeCalculator extends AreaCalculator
      {
          public function construct($shapes = [])
          {
              parent::construct($shapes);
          }
      
          public function sum()
          {
              // logic to calculate the volumes and then return a value of output
              return $summedData;
          }
      }
      

      Das $summedData können ein Float, Double oder Integer sein.

      Damit ist das Liskovsche Substitutionsprinzip erfüllt.

      Das Interface-Segregation-Prinzip

      Das Interface-Segregation-Prinzip besagt:

      Ein Client sollte nie gezwungen werden, eine Schnittstelle zu implementieren, die er nicht verwendet, oder Clients sollten nicht gezwungen werden, von Methoden abzuhängen, die sie nicht verwenden.

      Weiterhin aufbauend auf dem vorherigen Beispiel ShapeInterface, müssen Sie die neuen dreidimensionalen Formen Cuboid und Spheroid unterstützen, und diese Formen müssen auch das Volumen berechnen.

      Bedenken wir, was passieren würde, wenn Sie das ShapeInterface modifizieren würden, um einen weiteren Vertrag hinzuzufügen:

      interface ShapeInterface
      {
          public function area();
      
          public function volume();
      }
      

      Nun muss jede Form, die Sie erstellen, die Methode volume implementieren, aber Sie wissen, dass Quadrate flache Formen sind und kein Volumen haben, also würde diese Schnittstelle die Klasse Square zwingen, eine Methode zu implementieren, die sie nicht braucht.

      Dies würde das Interface-Segregation-Prinzip verletzen. Stattdessen könnten Sie eine andere Schnittstelle namens ThreeDimensionalShapeInterface erstellen, die den Vertrag volume hat und dreidimensionale Formen können diese Schnittstelle implementieren:

      interface ShapeInterface
      {
          public function area();
      }
      
      interface ThreeDimensionalShapeInterface
      {
          public function volume();
      }
      
      class Cuboid implements ShapeInterface, ThreeDimensionalShapeInterface
      {
          public function area()
          {
              // calculate the surface area of the cuboid
          }
      
          public function volume()
          {
              // calculate the volume of the cuboid
          }
      }
      

      Dies ist ein wesentlich besserer Ansatz, aber ein Fallstrick, auf den Sie achten müssen, wenn Sie diese Schnittstellen mit Typ-Hinweisen versehen. Anstatt ein ShapeInterface oder ein ThreeDimensionalShapeInterface zu verwenden, können Sie eine andere Schnittstelle erstellen, vielleicht ManageShapeInterface, und diese sowohl für die flachen als auch für die dreidimensionalen Formen implementieren.

      Auf diese Weise können Sie eine einzige API für die Verwaltung der Formen haben:

      interface ManageShapeInterface
      {
          public function calculate();
      }
      
      class Square implements ShapeInterface, ManageShapeInterface
      {
          public function area()
          {
              // calculate the area of the square
          }
      
          public function calculate()
          {
              return $this->area();
          }
      }
      
      class Cuboid implements ShapeInterface, ThreeDimensionalShapeInterface, ManageShapeInterface
      {
          public function area()
          {
              // calculate the surface area of the cuboid
          }
      
          public function volume()
          {
              // calculate the volume of the cuboid
          }
      
          public function calculate()
          {
              return $this->area();
          }
      }
      

      In der Klasse AreaCalculator können Sie den Aufruf für die Methode area durch calculate ersetzen und außerdem überprüfen, ob das Objekt eine Instanz des ManageShapeInterface und nicht des ShapeInterface ist.

      Das erfüllt das Interface-Segregation-Prinzip.

      Das Dependency-Inversion-Prinzip

      Das Dependency-Inversion-Prinzip besagt:

      Entitäten müssen von Abstraktionen abhängen, nicht von Konkretionen. Es besagt, dass das Modul auf hoher Ebene nicht vom Modul auf niedriger Ebene abhängen darf, sondern diese von Abstraktionen abhängen sollten.

      Dieses Prinzip ermöglicht die Entkopplung.

      Hier ist ein Beispiel für einen PasswordReminder der sich mit einer MySQL-Datenbank verbindet:

      class MySQLConnection
      {
          public function connect()
          {
              // handle the database connection
              return 'Database connection';
          }
      }
      
      class PasswordReminder
      {
          private $dbConnection;
      
          public function __construct(MySQLConnection $dbConnection)
          {
              $this->dbConnection = $dbConnection;
          }
      }
      

      Zuerst ist die MySQLConnection das Modul auf niedriger Ebene, während der PasswordReminder auf hoher Ebene angesiedelt ist, aber gemäß der Definition von D in SOLID, die besagt, von der Abstraktion abzuhängen und nicht von Konkretionen. Dieses obige Snippet verletzt dieses Prinzip, da die Klasse PasswordReminder gezwungen wird, von der Klasse MySQLConnection abzuhängen.

      Wenn Sie später die Datenbank-Engine ändern würden, müssten Sie auch die Klasse PasswordReminder bearbeiten, und das würde das Open-Close-Prinzip verletzen.

      Die Klasse PasswordReminder sollte sich nicht darum kümmern, welche Datenbank Ihre Anwendung verwendet. Um diese Probleme zu beheben, können Sie an eine Schnittstelle kodieren, da Module auf hoher Ebene und niedriger Ebene von der Abstraktion abhängen sollten:

      interface DBConnectionInterface
      {
          public function connect();
      }
      

      Die Schnittstelle hat eine Verbindungsmethode und die Klasse MySQLConnection implementiert diese Schnittstelle. Anstatt die Klasse MySQLConnection im Konstruktor von PasswordReminder, direkt zu typisieren, geben Sie stattdessen das DBConnectionInterface an, und unabhängig davon, welchen Datenbanktyp Ihre Anwendung verwendet, kann die Klasse PasswordReminder ohne Probleme eine Verbindung zur Datenbank herstellen und das Open-Close-Prinzip wird nicht verletzt.

      class MySQLConnection implements DBConnectionInterface
      {
          public function connect()
          {
              // handle the database connection
              return 'Database connection';
          }
      }
      
      class PasswordReminder
      {
          private $dbConnection;
      
          public function __construct(DBConnectionInterface $dbConnection)
          {
              $this->dbConnection = $dbConnection;
          }
      }
      

      Dieser Code verdeutlicht, dass sowohl die Module auf hoher Ebene als auch auf niedriger Ebene von der Abstraktion abhängen.

      Zusammenfassung

      In diesem Artikel wurden Ihnen die fünf Prinzipien von SOLID Code vorgestellt. Projekte, die sich an die SOLID-Prinzipien halten, können mit weniger Komplikationen mit anderen Mitarbeitern geteilt, erweitert, modifiziert, getestet und refraktorisiert werden.

      Lernen Sie weiter, indem Sie über andere Praktiken für die Agile und Adaptive Softwareentwicklung lesen.



      Source link

      Einrichten einer Node.js-Anwendung für die Produktion unter Ubuntu 20.04


      Einführung

      Node.js ist eine Open-Source-basierte JavaScript-Laufzeitumgebung für das Einrichten von Server- und Netzwerkanwendungen. Die Plattform läuft unter Linux, MacOS, FreeBSD und Windows. Sie können Node.js-Anwendungen zwar in der Befehlszeile ausführen können, doch werden Sie sie in diesem Tutorial als Dienst ausführen. Das bedeutet, dass sie bei erneutem Booten oder einem Absturz neu gestartet werden und zur Verwendung in einer Produktionsumgebung sicher sind.

      In diesem Tutorial werden Sie eine produktionsfähige Node.js-Umgebung auf einem einzelnen Ubuntu 20.04-Server einrichten. Dieser Server wird eine von PM2 verwaltete Node.js-Anwendung ausführen und Benutzern sicheren Zugriff auf die Anwendung über einen Nginx-Reverseproxy bereitstellen. Der Nginx-Server bietet HTTPS mit einem kostenlosen Zertifikat, das von Let’s Encrypt bereitgestellt wird.

      Voraussetzungen

      Dieser Leitfaden geht davon aus, dass Sie Folgendes haben:

      Wenn Sie die Voraussetzungen abgeschlossen haben, verfügen Sie über einen Server, der die Standardplatzhalterseite Ihrer Domäne unter https://example.com/ bereitstellt.

      Schritt 1 — Installieren von Node.js

      Beginnen wir zunächst mit der Installation der neuesten LTS-Version von Node.js unter Verwendung der NodeSource-Paketarchive.

      Installieren Sie zunächst das NodeSource-PPA, um Zugriff auf dessen Inhalt zu erhalten. Stellen Sie sicher, dass Sie sich in Ihrem Stammverzeichnis befinden, und verwenden Sie curl, um das Installationskript für die aktuellste LTS-Version von Node.js aus den Archiven abzurufen.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      Sie können den Inhalt dieses Skripts mit nano oder Ihrem bevorzugten Texteditor prüfen:

      Wenn Sie das Skript fertig geprüft haben, führen Sie es unter sudo aus:

      • sudo bash nodesource_setup.sh

      Das PPA wird Ihrer Konfiguration hinzugefügt und Ihr lokaler Paket-Cache automatisch aktualisiert. Nach Ausführung des Einrichtungsskripts aus Nodesource können Sie nun das Node.js-Paket installieren:

      Um zu prüfen, welche Version von Node.js Sie nach diesen ersten Schritten installiert haben, geben Sie Folgendes ein:

      Output

      v14.4.0

      Anmerkung: Bei der Installation aus dem NodeSource-PPA wird die ausführbare Node.js-Datei nodejs und nicht node genannt.

      Das nodejs-Paket enthält das binäre nodejs sowie npm, einen Paketmanager für Node-Module. Sie müssen npm also nicht separat installieren.

      npm verwendet eine Konfigurationsdatei in Ihrem Home Verzeichnis, um Aktualisierungen zu verfolgen. Sie wird erstellt, wenn Sie npm das erste Mal ausführen. Führen Sie diesen Befehl aus, um zu verifizieren, dass npm installiert ist, und um die Konfigurationsdatei zu erstellen:

      Output

      6.14.5

      Damit bestimmte npm Pakete funktionieren (z. B. jene, deren Code aus Source erstellt werden muss), müssen Sie das build-essential Paket installieren:

      • sudo apt install build-essential

      Jetzt haben Sie die notwendigen Tools, um mit npm Paketen zu arbeiten, deren Code aus Source erstellt werden muss.

      Mit installierter Node.js-Laufzeitumgebung können wir jetzt mit dem Schreiben einer Node.js-Anwendung fortfahren.

      Schritt 2 — Erstellen einer Node.js-Anwendung

      Lassen Sie uns eine Hello World-Anwendung schreiben, die in beliebigen HTTP-Anfragen „Hello World“ zurückgibt. Diese Beispielanwendung hilft Ihnen dabei, Node.js einzurichten. Sie können sie durch eine eigene Anwendung ersetzen – stellen Sie sicher, dass Sie Ihre Anwendung so ändern, dass an den richtigen IP-Adressen und Ports gelauscht wird.

      Erstellen wir zunächst eine Beispielanwendung namens hello.js:

      Fügen Sie in der Datei den folgenden Code hinzu:

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Speichern Sie die Datei und beenden Sie den Editor.

      Diese Node.js-Anwendung lauscht an der angegebenen Adresse (localhost) und Port (3000) und gibt “Hello World!” mit dem HTTP-Erfolgscode 200 zurück. Da wir an localhost lauschen, können sich Remoteclients nicht mit unserer Anwendung verbinden.

      Geben Sie zum Testen Ihrer Anwendung Folgendes ein:

      Sie erhalten die folgende Ausgabe:

      Output

      Server running at http://localhost:3000/

      Anmerkung: Durch Ausführung einer Node.js-Anwendung auf diese Weise werden weitere Befehle blockiert, bis der Abbruch der Anwendung durch Drücken von Strg+C erzwungen wird.

      Öffnen Sie zum Testen der Anwendung auf Ihrem Server eine weitere Terminalsitzung und stellen Sie unter Verwendung von curl mit localhost eine Verbindung her:

      • curl http://localhost:3000

      Wenn Sie die folgende Ausgabe erhalten, funktioniert die Anwendung ordnungsgemäß und lauscht an der richtigen Adresse und am richtigen Port:

      Output

      Hello World!

      Wenn Sie nicht die erwartete Ausgabe erhalten, stellen Sie sicher, dass Ihre Node.js-Anwendung ausgeführt wird und so konfiguriert ist, dass sie an der richtigen Adresse und am richtigen Port lauscht.

      Sobald Sie sicher sind, dass es funktioniert, erzwingen Sie den Abbruch der Anwendung (wenn Sie es nicht bereits getan haben), indem Sie Strg+C drücken.

      Schritt 3 – Installieren von PM2

      Als Nächstes installieren wir PM2, einen Prozessmanager für Node.js-Anwendungen. PM2 ermöglicht es, Anwendungen zu daemonisieren, damit sie im Hintergrund als Dienst ausgeführt werden.

      Verwenden Sie npm zum Installieren der neuesten Version von PM2 auf Ihrem Server:

      • sudo npm install pm2@latest -g

      Die Option -g weist npm an, das Modul global zu installieren, damit es systemweit verfügbar ist.

      Verwenden wir zunächst den Befehl pm2 start zum Ausführen Ihrer Anwendung hello.js im Hintergrund:

      Dadurch wird Ihre Anwendung auch der Prozessliste von PM2 hinzugefügt, die bei jeder Ausführung einer Anwendung ausgegeben wird:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Wie oben angegeben, weist PM2 automatisch einen App-Namen (basierend auf dem Dateinamen ohne die Erweiterung .js) und eine PM2-ID zu. Außerdem pflegt PM2 weitere Informationen, wie z. B. die PID des Prozesses, den aktuellen Status und die Speicherauslastung.

      Anwendungen, die unter PM2 ausgeführt werden, werden automatisch neu gestartet, wenn eine Anwendung abstürzt oder ihr Abbruch erzwungen wird. Wir können jedoch mit dem Unterbefehl startup einen zusätzlichen Schritt hinzufügen, damit die Anwendung beim Systemstart gestartet wird. Dieser Unterbefehl generiert und konfiguriert ein Startskript zum Starten von PM2 und den verwalteten Prozessen beim Booten von Servern:

      Die letzte Zeile der resultierenden Ausgabe enthält einen Befehl zum Ausführen mit Superuser-Berechtigungen, damit PM2 beim Booten gestartet wird:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Führen Sie den Befehl aus der Ausgabe aus, wobei Sie anstelle von sammy Ihren Benutzernamen verwenden:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Als weiteren Schritt können wir die PM2-Prozessliste und entsprechende Umgebungen speichern:

      Sie haben nun eine systemd-Einheit erstellt, die beim Booten pm2 für Ihren Benutzer ausführt. Diese pm2-Instanz gibt wiederum hello.js zurück.

      Starten Sie den Dienst mit systemctl:

      • sudo systemctl start pm2-sammy

      Wenn an diesem Punkt ein Fehler auftritt, müssen Sie möglicherweise neu starten, was Sie mit sudo reboot erreichen können.

      Überprüfen Sie den Status der systemd-Einheit:

      • systemctl status pm2-sammy

      Für eine ausführliche Übersicht zu systemd lesen Sie bitte Systemd Essentials: Arbeiten mit Diensten, Einheiten und dem Journal.

      Neben den von uns abgedeckten Befehlen bietet PM2 viele Unterbefehle, mit denen Sie Daten über Ihre Anwendung verwalten oder suchen können.

      Stoppen Sie eine Anwendung mit diesem Befehl (geben Sie den App-Namen oder die ID von PM2 an):

      Starten Sie eine Anwendung neu:

      • pm2 restart app_name_or_id

      Listen Sie die aktuell von PM2 verwalteten Anwendungen auf:

      Erhalten Sie Informationen zu einer bestimmten Anwendung mithilfe des App-Namens:

      Der PM2-Prozessmonitor kann mit dem Unterbefehl monit aufgerufen werden. Dadurch werden der Anwendungsstatus sowie die CPU-und Speicherauslastung angezeigt:

      Beachten Sie, dass beim Ausführen von pm2 ohne Argumente auch eine Hilfeseite mit Beispielnutzung angezeigt wird.

      Nachdem Ihre Node.js-Anwendung ausgeführt und von PM2 verwaltet wird, können wir nun den Reverseproxy einrichten.

      Schritt 4 — Einrichten von Nginx als Reverseproxy-Server

      Ihre Anwendung wird ausgeführt und lauscht an localhost. Sie müssen jedoch einen Weg finden, damit Ihre Benutzer darauf zugreifen können. Dazu werden wir den Nginx-Webserver als Reverseproxy einrichten.

      Im Tutorial zu den Voraussetzungen richten Sie Ihre Nginx-Konfiguration in der Datei /etc/nginx/sites-available/example.com ein. Öffnen Sie diese Datei zur Bearbeitung:

      • sudo nano /etc/nginx/sites-available/example.com

      Innerhalb des server-Blocks sollten Sie einen vorhandenen location /-Block haben. Ersetzen Sie den Inhalt dieses Blocks durch die folgende Konfiguration. Wenn Ihre Anwendung so konfiguriert ist, dass sie an einem anderen Port lauscht, aktualisieren Sie den markierten Teil mit der richtigen Portnummer:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Dadurch wird der Server so konfiguriert, dass er auf Anfragen an sein Stammverzeichnis antwortet. Angenommen, unser Server ist unter example.com verfügbar. Durch Aufrufen von https://example.com/ über einen Webbrowser würde die Anfrage an hello.js gesendet werden, wobei an Port 3000 bei localhost gelauscht wird.

      Sie können dem gleichen Serverblock zusätzliche location-Blöcke hinzufügen, um Zugriff auf andere Anwendungen auf dem gleichen Server zu gewähren. Wenn Sie beispielsweise eine andere Node.js-Anwendung an Port 3001 ausführen würden, könnten Sie diesen location-Block hinzufügen, um Zugriff darauf über https://example.com/app2 zu gewähren:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Sobald Sie die location-Blöcke für Ihre Anwendungen hinzugefügt haben, speichern Sie die Datei und verlassen Sie den Editor.

      Stellen Sie sicher, dass Sie keine Syntaxfehler gemacht haben, indem Sie Folgendes eingeben:

      Starten Sie Nginx neu:

      • sudo systemctl restart nginx

      Wenn Ihre Node.js-Anwendung ausgeführt wird und Ihre Anwendungs- und Nginx-Konfigurationen korrekt sind, sollten Sie nun über den Nginx-Reverseproxy auf Ihre Anwendung zugreifen können. Probieren Sie es aus, indem Sie die URL Ihres Servers (seine öffentliche IP-Adresse oder seinen Domänennamen) aufrufen.

      Zusammenfassung

      Herzlichen Glückwunsch! Ihre Node.js-Anwendung wird nun hinter einem Nginx-Reverseproxy auf einem Ubuntu 20.04-Server ausgeführt. Diese Reverseproxy-Einrichtung ist flexibel genug, um Ihren Benutzern Zugriff auf andere Anwendungen oder statische Webinhalte zu bieten, die Sie freigeben möchten.



      Source link