Tutorial

So automatisieren Sie die Produktionsbereitstellung von Node.js mit Shipit unter CentOS 7

CentOSNode.jsGitJavaScriptAutomated Setups

Der Autor wählte die Electronic Frontier Foundation, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

Einführung

Shipit ist ein universelles Automatisierungs- und Bereitstellungswerkzeug für Node.js-Entwickler. Es bietet einen Aufgabenablauf, der auf dem populären Orchestrator-Paket, der Anmeldung und den interaktiven SSH-Befehlen über OpenSSH und einer erweiterbaren API basiert. Entwickler können Shipit zur Automatisierung von Erstellungs- und Bereitstellungs-Workflows für eine breite Palette von Node.js-Anwendungen verwenden.

Der Shipit-Workflow ermöglicht Entwicklern nicht nur die Konfiguration von Aufgaben, sondern auch die Angabe der Reihenfolge, in der sie ausgeführt werden; ob sie synchron oder asynchron und in welcher Umgebung sie ausgeführt werden sollen.

In diesem Tutorial werden Sie Shipit installieren und konfigurieren, um eine Node.js-Anwendung aus Ihrer lokalen Entwicklungsumgebung in Ihrer Produktivumgebung bereitzustellen. Sie werden Shipit verwenden, um Ihre Anwendung bereitzustellen und den Remote-Server zu konfigurieren, indem Sie:

  • die Dateien Ihrer Node.js-Anwendung von Ihrer lokalen Umgebung in die Produktivumgebung übertragen (mit rsync, git und ssh).
  • die Abhängigkeiten Ihrer Anwendung (Knotenmodule) installieren.
  • die auf dem Remote-Server laufenden Node.js-Prozesse mit PM2 konfigurieren und verwalten.

Voraussetzungen

Bevor Sie dieses Tutorial beginnen, benötigen Sie Folgendes:

Anmerkung: Windows-Benutzer müssen das Windows-Subsystem für Linux installieren, um die Befehle in diesem Leitfaden auszuführen.

Schritt 1 – Einrichten des Remote-Repositorys

Shipit erfordert ein Git-Repository zur Synchronisierung zwischen dem lokalen Entwicklungsrechner und dem Remote-Server. In diesem Schritt erstellen Sie ein Remote-Repository auf Github.com. Obwohl jeder Anbieter etwas anders ist, sind die Befehle in gewisser Weise übertragbar.

Um ein Repository zu erstellen, öffnen Sie Github.com in Ihrem Webbrowser und melden Sie sich an. Sie werden feststellen, dass in der oberen rechten Ecke einer beliebigen Seite ein +-Symbol vorhanden ist. Klicken Sie auf + und klicken Sie dann auf New Repository.

Github-new-repository

Geben Sie einen kurzen, einprägsamen Namen für Ihr Repository ein, z. B. hello-world. Beachten Sie, dass der Name, den Sie hier wählen, als der Projektordner repliziert wird, von dem aus Sie auf Ihrem lokalen Rechner arbeiten werden.

Github-repository-name

Fügen Sie optional eine Beschreibung Ihres Repositorys hinzu.

Github-repository-description

Legen Sie die Sichtbarkeit Ihres Repository –entweder öffentlich oder privat – nach Ihren Wünschen fest.

Stellen Sie sicher, dass das Repository mit einem .gitignore initialisiert wird, wählen Sie Node aus der Dropdown-Liste Add gitgnore aus. Dieser Schritt ist wichtig, um zu vermeiden, dass unnötige Dateien (wie der Ordner node_modules) zu Ihrem Repository hinzugefügt werden. 

Github-gitignore-node

Klicken Sie auf die Schaltfläche Create repository.

Das Repository muss jetzt von Github.com zu Ihrem lokalen Rechner geklont werden.

Öffnen Sie Ihr Terminal und navigieren Sie zu dem Ort, an dem Sie alle Node.js-Projektdateien speichern möchten. Beachten Sie, dass bei diesem Vorgang ein Unterordner innerhalb des aktuellen Verzeichnisses erstellt wird. Führen Sie den folgenden Befehl aus, um das Repository auf Ihren lokalen Rechner zu klonen:

  • git clone https://github.com/your-github-username/your-github-repository-name.git

Sie müssen your-githug-username und your-github-repository-name ersetzen, um Ihren Github-Benutzernamen und den zuvor angegebenen Repository-Namen widerzuspiegeln. 

Anmerkung: Wenn Sie die Zweifaktor-Authentifizierung (2FA) auf Github.com aktiviert haben, müssen Sie beim Zugriff auf Github über die Befehlszeile anstelle Ihres Passworts ein persönliches Zugriffstoken oder einen SSH-Schlüssel verwenden. Die Github-Hilfeseite zum Thema 2FA bietet weitere Informationen.

Sie sehen eine Ausgabe, die der nachfolgenden ähnelt:

Output
Cloning into 'your-github-repository-name'... remote: Enumerating objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3 Unpacking objects: 100% (3/3), done.

Wechseln Sie in das Repository, indem Sie den folgenden Befehl ausführen:

  • cd your-github-repository-name

Innerhalb des Repositorys befinden sich eine einzige Datei und ein einziger Ordner, beides Dateien, die von Git zur Verwaltung des Repositorys verwendet werden. Sie können dies wie folgt überprüfen:

  • ls -la

Sie sehen eine Ausgabe, die der folgenden ähnelt:

Output
total 8 0 drwxr-xr-x 4 asciant staff 128 22 Apr 07:16 . 0 drwxr-xr-x 5 asciant staff 160 22 Apr 07:16 .. 0 drwxr-xr-x 13 asciant staff 416 22 Apr 07:16 .git 8 -rw-r--r-- 1 asciant staff 914 22 Apr 07:16 .gitignore

Nachdem Sie nun ein funktionierendes git-Repository konfiguriert haben, erstellen Sie die Datei shipit.js, die Ihren Bereitstellungsprozess verwaltet.

Schritt 2 – Integration von Shipit in ein Node.js-Projekt

In diesem Schritt erstellen Sie ein Beispielprojekt Node.js und fügen dann die Shipit-Pakete hinzu. Dieses Tutorial bietet eine Beispielanwendung – den Node.js Web-Server, der HTTP-Anfragen akzeptiert und mit Hello World im Klartext antwortet. Um die Anwendung zu erstellen, führen Sie den folgenden Befehl aus:

  • nano hello.js

Fügen Sie den folgenden Beispiel-Anwendungscode zu hello.js hinzu (aktualisiert die Variable APP_PRIVATE_IP_ADDRESS auf die IP-Adresse Ihres App-Servers im privaten Netzwerk):

hello.js
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8080, 'APP_PRIVATE_IP_ADDRESS');
console.log('Server running at http://APP_PRIVATE_IP_ADDRESS:8080/');

Erstellen Sie jetzt Ihre package.json-Datei für Ihre Anwendung:

  • npm init -y

Dieser Befehl erzeugt eine package.json-Datei, die Sie zur Konfiguration Ihrer Node.js-Anwendung verwenden werden. Im nächsten Schritt fügen Sie mit der npm-Befehlszeilenschnittstelle Abhängigkeiten zu dieser Datei hinzu.

Output
Wrote to ~/hello-world/package.json: { "name": "hello-world", "version": "1.0.0", "description": "", "main": index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

Als Nächstes installieren Sie die erforderlichen npm-Pakete mit folgendem Befehl:

  • npm install --save-dev shipit-cli shipit-deploy shipit-shared

Verwenden Sie hier das Flag --save-dev, da die Shipit-Pakete nur auf Ihrem lokalen Rechner benötigt werden. Sie sehen eine Ausgabe, die der folgenden ähnelt:

Output
+ shipit-shared@4.4.2 + shipit-cli@4.2.0 + shipit-deploy@4.1.4 updated 4 packages and audited 21356 packages in 11.671s found 62 low severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details

Dadurch wurden auch die drei Pakete zu Ihrer package.json-Datei als Entwicklungsabhängigkeiten hinzugefügt: 

package.json
. . .
  "devDependencies": {
    "shipit-cli": "^4.2.0",
    "shipit-deploy": "^4.1.4",
    "shipit-shared": "^4.4.2"
  },
. . .

Wenn Ihre lokale Umgebung konfiguriert ist, können Sie nun mit der Vorbereitung des Remote-App-Servers für Shipit-basierte Bereitstellungen fortfahren.

Schritt 3 – Vorbereitung des Remote-Anwendungsservers

In diesem Schritt verwenden Sie ssh, um sich mit Ihrem App-Server zu verbinden und Ihre Remote-Abhängigkeit rsync zu installieren. Rsync ist ein Dienstprogramm zur effizienten Übertragung und Synchronisierung von Dateien zwischen lokalen und vernetzten Computern, indem die Änderungszeiten und Größen von Dateien verglichen werden.

Shipit verwendet rsync zur Übertragung und Synchronisierung von Dateien zwischen Ihrem lokalen Computer und dem Remote-App-Server. Sie werden keine Befehle direkt an rsync erteilen; Shipit übernimmt das für Sie.

Anmerkung: Mit So richten Sie eine Node.js-Anwendung für die Produktion unter CentOS 7 ein haben Sie die zwei Server App und Web erstellt. Diese Befehle sollten nur auf App ausgeführt werden.

Verbinden Sie Ihren Remote-App-Server über ssh:

  • ssh deployer@your_app_server_ip

Installieren Sie rsync auf Ihrem Server, indem Sie den folgenden Befehl ausführen:

  • sudo yum install rsync

Bestätigen Sie die Installation mit:

  • rsync --version

In der Ausgabe dieses Befehls sehen Sie eine ähnliche Zeile:

Output
rsync version 3.1.2 protocol version 31 . . .

Sie können Ihre ssh-Sitzung durch Eingabe von exit beenden.

Wenn rsync installiert und in der Befehlszeile verfügbar ist, können Sie mit den Bereitstellungsaufgaben und ihrer Beziehung zu den Ereignissen fortfahren.

Schritt 4 – Konfigurieren und Ausführen von Bereitstellungsaufgaben

Sowohl Ereignisse als auch Aufgaben sind Schlüsselkomponenten von Shipit-Bereitstellungen, und es ist wichtig zu verstehen, wie sie die Bereitstellung Ihrer Anwendung ergänzen. Die von Shipit ausgelösten Ereignisse stellen bestimmte Punkte im Lebenszyklus der Bereitstellung dar. Ihre Aufgaben werden als Reaktion auf diese Ereignisse ausgeführt, basierend auf der Reihenfolge des Shipit-Lebenszyklus.

Ein gängiges Beispiel, bei dem dieses Aufgaben-/Ereignissystem in einer Node.js-Anwendung nützlich ist, ist die Installation der Abhängigkeiten der Anwendung (node_modules) auf dem Remote-Server. Später in diesem Schritt lassen Sie Shipit auf das Ereignis updated (das nach der Übertragung der Anwendungsdateien ausgegeben wird) lauschen und eine Aufgabe ausführen, um die Abhängigkeiten der Anwendung (npm install) auf dem Remote-Server zu installieren. 

Zum Lauschen auf Ereignisse und Ausführen von Aufgaben benötigt Shipit eine Konfigurationsdatei, die Informationen über Ihren Remote-Server enthält (den App-Server) und die Ereignis-Listener und die von diesen Aufgaben auszuführenden Befehle registriert. Diese Datei befindet sich auf Ihrem lokalen Entwicklungsrechner im Verzeichnis Ihrer Node.js-Anwendung.

Um zu beginnen, erstellen Sie diese Datei, einschließlich Informationen über Ihren Remote-Server, die Ereignis-Listener, die Sie abonnieren möchten, und einige Definitionen Ihrer Aufgaben. Erstellen Sie shipitfile.js in Ihrem Anwendungs-Stammverzeichnis auf Ihrem lokalen Rechner, indem Sie den folgenden Befehl ausführen:

  • nano shipitfile.js

Nachdem Sie nun eine Datei erstellt haben, muss sie mit den anfänglichen Umgebungsinformationen, die Shipit benötigt, gefüllt werden. Dies ist in erster Linie der Standort Ihres Remote Git-Repositorys und vor allem die öffentliche IP-Adresse und das SSH-Benutzerkonto Ihres App-Servers.

Fügen Sie diese Anfangskonfiguration hinzu und aktualisieren Sie die hervorgehobenen Zeilen, um sie an Ihre Umgebung anzupassen:

shipitfile.js
module.exports = shipit => {
  require('shipit-deploy')(shipit);
  require('shipit-shared')(shipit);

  const appName = 'hello';

  shipit.initConfig({
    default: {
      deployTo: '/home/sammy/your-domain',
      repositoryUrl: 'https://git-provider.tld/YOUR_GIT_USERNAME/YOUR_GIT_REPO_NAME.git',
      keepReleases: 5,
      shared: {
        overwrite: true,
        dirs: ['node_modules']
      }
    },
    production: {
      servers: 'sammy@YOUR_APP_SERVER_PUBLIC_IP'
    }
  });

  const path = require('path');
  const ecosystemFilePath = path.join(
    shipit.config.deployTo,
    'shared',
    'ecosystem.config.js'
  );

  // Our listeners and tasks will go here

};

Die Aktualisierung der variables in Ihrer shipit.initConfig-Methode stellt Shipit eine für Ihre Bereitstellung spezifische Konfiguration bereit. Diese stellen für Shipit Folgendes dar:

  • deployTo: ist das Verzeichnis, in dem Shipit den Code Ihrer Anwendung auf dem Remote-Server bereitstellt. Hier verwenden Sie den Ordner /home/ für einen Benutzer ohne Rootberechtigung und mit sudo-Berechtigungen (/home/sammy), da er sicher ist und Probleme mit den Berechtigungen vermieden werden. Die Komponente /your-domain ist eine Namenskonvention zur Unterscheidung des Ordners von anderen Ordnern im Home-Ordner des Benutzers.
  • repositoryUrl: ist die URL zu dem vollständigen Git-Repository. Shipit verwendet diese URL, um sicherzustellen, dass die Projektdateien vor der Bereitstellung synchronisiert sind.
  • keepReleases: ist die Anzahl der Versionen, die auf dem Remote-Server aufbewahrt werden sollen. Ein Release ist ein mit einem Datumsstempel versehener Ordner, der die Dateien Ihrer Anwendung zum Zeitpunkt der Freigabe enthält. Diese können für das Rollback einer Bereitstellung nützlich sein.
  • shared: ist eine mit keepReleases korrespondierende Konfiguration, die die gemeinsame Nutzung von Verzeichnissen (shared) zwischen Versionen ermöglicht. In diesem Fall haben wir einen einzigen Ordner node_modules, der von allen Versionen gemeinsam genutzt wird.
  • production: stellt einen Remote-Server dar, auf dem Ihre Anwendung bereitgestellt wird. In diesem Fall haben Sie einen einzigen Server (App-Server), den Sie production nennen, wobei die Konfiguration servers: Ihrem SSH user und der public ip address entspricht. Der Name production entspricht dem Shipit Bereitstellungsbefehl, der gegen Ende dieses Tutorials verwendet wird (npx shipit server name deploy oder in Ihrem Fall npx shipit production deploy).

Weitere Informationen zu dem Objekt Shipit-Bereitstellungskonfiguration finden Sie im Shiptit Github-Repository.

Bevor Sie mit der Aktualisierung Ihrer shipitfile.js fortfahren, lassen Sie uns das folgende Beispielcode-Snippet durchsehen, um die Shipit-Aufgaben zu verstehen:

Example event listener
shipit.on('deploy', () => { shipit.start('say-hello'); }); shipit.blTask('say-hello', async () => { shipit.local('echo "hello from your local computer"') });

Dies ist eine Beispielaufgabe, die die shipit.on-Methode verwendet, um das Ereignis deploy zu abonnieren. Diese Aufgabe wartet auf die Auslösung des Ereignisses deploy durch den Shipit-Lebenszyklus, und wenn das Ereignis empfangen wird, führt die Aufgabe die Methode shipit.start aus, die Shipit anweist, die say-hello-Aufgabe zu starten.

Die Methode shipit.on nimmt zwei Parameter an, den Namen des Ereignisses, auf das zu lauschen ist, und die Rückruffunktion, die beim Empfang des Ereignisses ausgeführt werden soll.

Unter der Methodendeklaration shipit.on wird die Aufgabe mit der Methode shipit.blTask definiert. Dadurch wird eine neue Shipit-Aufgabe erstellt, die andere Aufgaben während ihrer Ausführung blockiert (es handelt sich um eine synchrone Aufgabe). Die Methode shipit.blTask benötigt auch zwei Parameter, den Namen der Aufgabe, die sie definiert, und eine Rückruffunktion, die ausgeführt wird, wenn die Aufgabe durch shipit.start ausgelöst wird.

Innerhalb der Rückruffunktion dieser Beispielaufgabe (say-hello) führt die Methode shipit.local einen Befehl auf dem lokalen Rechner aus. Der lokale Befehl gibt "hello from your local computer" in die Terminalausgabe aus.

Wenn Sie einen Befehl auf dem Remote-Server ausführen wollten, würden Sie die Methode shipit.remote verwenden. Die beiden Methoden, shipit.local und shipit.remote, stellen eine API zur Verfügung, um Befehle entweder lokal oder als Teil einer Bereitstellung per Fernzugriff auszugeben.

Aktualisieren Sie nun die Datei shipitfile.js, um Ereignis-Listener einzubeziehen und den Shipit-Lebenszyklus mit shipit.on zu abonnieren. Fügen Sie die Ereignis-Listener zu Ihrer shipitfile.js hinzu, indem Sie sie nach dem Kommentar-Platzhalter aus der Anfangskonfiguration // Our tasks will go here einfügen:

shipitfile.js
. . .
  shipit.on('updated', () => {
    shipit.start('npm-install', 'copy-config');
  });

  shipit.on('published', () => {
    shipit.start('pm2-server');
  });

Diese beiden Methoden lauschen auf die Ereignisse updated und published, die als Teil des Shipit-Bereitstellungslebenszyklus ausgesendet werden. Wenn das Ereignis empfangen wird, initiieren sie jeweils Aufgaben mit der Methode shipit.start, ähnlich wie bei der Beispielaufgabe.

Nachdem Sie nun die Listener eingeplant haben, fügen Sie die entsprechende Aufgabe hinzu. Fügen Sie die folgende Aufgabe zu Ihrer shipitfile.js hinzu, indem Sie sie nach den Ereignis-Listenern einfügen:

shipitfile.js
. . .
shipit.blTask('copy-config', async () => {

const fs = require('fs');

const ecosystem = `
module.exports = {
apps: [
  {
    name: '${appName}',
    script: '${shipit.releasePath}/hello.js',
    watch: true,
    autorestart: true,
    restart_delay: 1000,
    env: {
      NODE_ENV: 'development'
    },
    env_production: {
      NODE_ENV: 'production'
    }
  }
]
};`;

  fs.writeFileSync('ecosystem.config.js', ecosystem, function(err) {
    if (err) throw err;
    console.log('File created successfully.');
  });

  await shipit.copyToRemote('ecosystem.config.js', ecosystemFilePath);
});

Sie deklarieren zunächst eine Aufgabe namens copy-config. Diese Aufgabe erstellt eine lokale Datei namens ecosystem.config.js und kopiert diese Datei dann auf Ihren Remote-App-Server. PM2 verwendet diese Datei, um Ihre Node.js-Anwendung zu verwalten. Sie stellt PM2 die erforderlichen Dateipfadinformationen zur Verfügung, um sicherzustellen, dass Ihre zuletzt bereitgestellten Dateien ausgeführt werden. Später im Build-Prozess erstellen Sie eine Aufgabe, die PM2 mit ecosystem.config.js als Konfiguration ausführt.

Wenn Ihre Anwendung Umgebungsvariablen benötigt (wie z. B. einen Datenbank-Verbindungszeichenfolge), können Sie diese entweder lokal in env: oder auf dem Remote-Server in env_production: auf dieselbe Weise deklarieren, wie Sie die Variable NODE_ENV in diesen Objekten festlegen.

Fügen Sie nach der Aufgabe copy-config Ihrer shipitfile.js-Datei die nächste Aufgabe hinzu:

shipitfile.js
. . .
shipit.blTask('npm-install', async () => {
  shipit.remote(`cd ${shipit.releasePath} && npm install --production`);
});

Als Nächstes deklarieren Sie eine Aufgabe namens npm-install. Diese Aufgabe verwendet ein Remote-Bash-Terminal (über shipit.remote), um die Abhängigkeiten der Anwendung (npm-Pakete) zu installieren.

Fügen Sie nach der Aufgabe npm-install Ihrer shipitfile.js-Datei die letzte Aufgabe hinzu:

shipitfile.js
. . .
shipit.blTask('pm2-server', async () => {
  await shipit.remote(`pm2 delete -s ${appName} || :`);
  await shipit.remote(
    `pm2 start ${ecosystemFilePath} --env production --watch true`
  );
});

Abschließend deklarieren Sie eine Aufgabe namens pm2-server. Diese Aufgabe verwendet ebenfalls ein Remote-Bash-Terminal, um PM2 zunächst durch den Befehl delete an der Verwaltung Ihrer vorherigen Bereitstellung zu hindern und dann eine neue Instanz Ihres Node.js-Servers zu starten, die die Datei ecosystem.config.js als Variable bereitstellt. Sie teilen PM2 auch mit, dass es Umgebungsvariablen aus dem Block production Ihrer Anfangskonfiguration verwenden soll, und fordern PM2 auf, die Anwendung zu überwachen und bei einem Absturz neu zu starten.

Die vollständige Datei shipitfile.js:

shipitfile.js
module.exports = shipit => {
  require('shipit-deploy')(shipit);
  require('shipit-shared')(shipit);

  const appName = 'hello';

  shipit.initConfig({
    default: {
      deployTo: '/home/deployer/example.com',
      repositoryUrl: 'https://git-provider.tld/YOUR_GIT_USERNAME/YOUR_GIT_REPO_NAME.git',
      keepReleases: 5,
      shared: {
        overwrite: true,
        dirs: ['node_modules']
      }
    },
    production: {
      servers: 'deployer@YOUR_APP_SERVER_PUBLIC_IP'
    }
  });

  const path = require('path');
  const ecosystemFilePath = path.join(
    shipit.config.deployTo,
    'shared',
    'ecosystem.config.js'
  );

  // Our listeners and tasks will go here
  shipit.on('updated', async () => {
    shipit.start('npm-install', 'copy-config');
  });

  shipit.on('published', async () => {
    shipit.start('pm2-server');
  });

  shipit.blTask('copy-config', async () => {
    const fs = require('fs');
    const ecosystem = `
module.exports = {
  apps: [
    {
      name: '${appName}',
      script: '${shipit.releasePath}/hello.js',
      watch: true,
      autorestart: true,
      restart_delay: 1000,
      env: {
        NODE_ENV: 'development'
      },
      env_production: {
        NODE_ENV: 'production'
      }
    }
  ]
};`;

    fs.writeFileSync('ecosystem.config.js', ecosystem, function(err) {
      if (err) throw err;
      console.log('File created successfully.');
    });

    await shipit.copyToRemote('ecosystem.config.js', ecosystemFilePath);
  });

  shipit.blTask('npm-install', async () => {
    shipit.remote(`cd ${shipit.releasePath} && npm install --production`);
  });

  shipit.blTask('pm2-server', async () => {
    await shipit.remote(`pm2 delete -s ${appName} || :`);
    await shipit.remote(
      `pm2 start ${ecosystemFilePath} --env production --watch true`
    );
  });
};

Speichern und beenden Sie die Datei, wenn Sie fertig sind.

Wenn Ihre shipitfile.js konfiguriert ist, die Ereignis-Listener und die zugehörigen Aufgaben abgeschlossen sind, können Sie mit der Bereitstellung auf dem App-Server fortfahren.

Schritt 5 – Bereitstellen Ihrer Anwendung

In diesem Schritt werden Sie Ihre Anwendung remote bereitstellen und testen, ob die Bereitstellung Ihre Anwendung für das Internet verfügbar gemacht hat.

Da Shipit die Projektdateien aus dem Remote-Git-Repository klont, müssen Sie Ihre lokalen Node.js-Anwendungsdateien von Ihrem lokalen Rechner zu Github übertragen. Navigieren Sie zum Anwendungsverzeichnis Ihres Node.js-Projekts (wo sich die Dateien hello.js und shiptitfile.js befinden) und führen Sie den folgenden Befehl aus:

  • git status

Der Befehl git status zeigt den Status des Arbeitsverzeichnisses und des Stagingbereichs an. Damit können Sie sehen, welche Änderungen bereitgestellt wurden, welche nicht, und welche Dateien nicht von Git verfolgt werden. Ihre Dateien werden nicht verfolgt und erscheinen in der Ausgabe rot:

Output
On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) hello.js package-lock.json package.json shipitfile.js nothing added to commit but untracked files present (use "git add" to track)

Sie können diese Dateien mit dem folgenden Befehl zu Ihrem Repository hinzufügen:

  • git add --all

Dieser Befehl erzeugt keine Ausgabe, obwohl die Dateien bei einer erneuten Ausführung von git status grün und mit einem Hinweis darauf erscheinen würden, dass es Änderungen gibt, die übernommen werden müssen.

Sie können einen Commit erstellen, indem Sie den folgenden Befehl ausführen:

  • git commit -m "Our first commit"

Die Ausgabe dieses Befehls liefert einige Git-spezifische Informationen über die Dateien.

Output
[master c64ea03] Our first commit 4 files changed, 1948 insertions(+) create mode 100644 hello.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 shipitfile.js

Jetzt müssen Sie nur noch den Commit mittels Push in das Remote-Repository übertragen, damit Shipit während der Bereitstellung Ihren App-Server klonen kann. Führen Sie den folgenden Befehl aus:

  • git push origin master

Die Ausgabe enthält Informationen über die Synchronisation mit dem Remote-Repository:

Output
Enumerating objects: 7, done. Counting objects: 100% (7/7), done. Delta compression using up to 8 threads Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 15.27 KiB | 7.64 MiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To github.com:Asciant/hello-world.git e274312..c64ea03 master -> master

Um Ihre Anwendung bereitzustellen, führen Sie den folgenden Befehl aus:

  • npx shipit production deploy

Die Ausgabe dieses Befehls (die zu groß ist, um sie in seiner Gesamtheit anzuzeigen) liefert Einzelheiten über die ausgeführten Aufgaben und das Ergebnis der spezifischen Funktion. Die folgende Ausgabe für die Aufgabe pm2-server zeigt, dass die Anwendung Node.js gestartet wurde:

Output
Running 'deploy:init' task... Finished 'deploy:init' after 432 μs . . . Running 'pm2-server' task... Running "pm2 delete -s hello || :" on host "centos-ap-app.asciant.com". Running "pm2 start /home/deployer/example.com/shared/ecosystem.config.js --env production --watch true" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com [PM2][WARN] Node 4 is deprecated, please upgrade to use pm2 to have all features @centos-ap-app.asciant.com [PM2][WARN] Applications hello not running, starting... @centos-ap-app.asciant.com [PM2] App [hello] launched (1 instances) @centos-ap-app.asciant.com ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────────┬──────────┐ @centos-ap-app.asciant.com │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ @centos-ap-app.asciant.com ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────────┼──────────┤ @centos-ap-app.asciant.com │ hello │ 0 │ 1.0.0 │ fork │ 4177 │ online │ 0 │ 0s │ 0% │ 4.5 MB │ deployer │ enabled │ @centos-ap-app.asciant.com └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────────┴──────────┘ @centos-ap-app.asciant.com Use `pm2 show <id|name>` to get more details about an app Finished 'pm2-server' after 5.27 s Running 'deploy:clean' task... Keeping "5" last releases, cleaning others Running "(ls -rd /home/deployer/example.com/releases/*|head -n 5;ls -d /home/deployer/example.com/releases/*)|sort|uniq -u|xargs rm -rf" on host "centos-ap-app.asciant.com". Finished 'deploy:clean' after 1.81 s Running 'deploy:finish' task... Finished 'deploy:finish' after 222 μs Finished 'deploy' [ deploy:init, deploy:fetch, deploy:update, deploy:publish, deploy:clean, deploy:finish ]

Um Ihre Anwendung aus der Perspektive eines Benutzers zu sehen, können Sie die URL Ihrer Webseite your-domain in Ihren Browser eingeben, um auf Ihren Web-Server zuzugreifen. Dadurch wird die Node.js-Anwendung auf dem App-Server, auf dem Ihre Dateien bereitgestellt wurden, über Reverse-Proxy bereitgestellt.

Sie sehen die Begrüßung Hello World.

Anmerkung: Nach der ersten Bereitstellung wird Ihr Git-Repository eine neu erstellte Datei namens ecosystem.config.js verfolgen. Da diese Datei bei jeder Bereitstellung neu erstellt wird und kompilierte Anwendungsgeheimnisse enthalten kann, sollte sie vor dem nächsten git-Commit zur Datei .gitignore im Anwendungs-Stammverzeichnis auf Ihrem lokalen Rechner hinzugefügt werden.

.gitignore
. . .
# ecosystem.config
ecosystem.config.js

Sie haben Ihre Node.js-Anwendung auf Ihrem App-Server bereitgestellt, der auf Ihre neue Bereitstellung verweist. Jetzt, wo alles läuft, können Sie zur Überwachung Ihrer Anwendungsprozesse übergehen.

Schritt 6 – Überwachung Ihrer Anwendung

PM2 ist ein ausgezeichnetes Werkzeug zur Verwaltung Ihrer Remote-Prozesse, bietet aber auch Funktionen zur Überwachung der Leistung dieser Anwendungsprozesse.

Stellen Sie mit diesem Befehl über SSH eine Verbindung zu Ihrem Remote-App-Server her:

  • ssh deployer@your_app_server_ip

Um spezifische Informationen zu den von PM2 verwalteten Prozessen zu erhalten, führen Sie Folgendes aus:

  • pm2 list

Sie sehen eine Ausgabe, die der nachfolgenden ähnelt:

Output
┌─────────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬──────┬───────────┬──────────┬──────────┐ │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├─────────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼──────┼───────────┼──────────┼──────────┤ │ hello │ 0 │ 0.0.1 │ fork │ 3212 │ online │ 0 │ 62m │ 0.3% │ 45.2 MB │ deployer │ enabled │ └─────────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴──────┴───────────┴──────────┴──────────┘

Sie sehen eine Zusammenfassung der Informationen, die PM2 gesammelt hat. Um detaillierte Informationen zu sehen, können Sie Folgendes ausführen:

  • pm2 show hello

Die Ausgabe erweitert die durch den Befehl pm2 list bereitgestellten zusammenfassenden Informationen. Sie liefert auch Informationen über eine Reihe von Zusatzbefehlen und gibt die Speicherorte der Protokolldateien an:

Output
Describing process with id 0 - name hello ┌───────────────────┬─────────────────────────────────────────────────────────────┐ │ status │ online │ │ name │ hello │ │ version │ 1.0.0 │ │ restarts │ 0 │ │ uptime │ 82s │ │ script path │ /home/deployer/example.com/releases/20190531213027/hello.js │ │ script args │ N/A │ │ error log path │ /home/deployer/.pm2/logs/hello-error.log │ │ out log path │ /home/deployer/.pm2/logs/hello-out.log │ │ pid path │ /home/deployer/.pm2/pids/hello-0.pid │ │ interpreter │ node │ │ interpreter args │ N/A │ │ script id │ 0 │ │ exec cwd │ /home/deployer │ │ exec mode │ fork_mode │ │ node.js version │ 4.2.3 │ │ node env │ production │ │ watch & reload │ ✔ │ │ unstable restarts │ 0 │ │ created at │ 2019-05-31T21:30:48.334Z │ └───────────────────┴─────────────────────────────────────────────────────────────┘ Revision control metadata ┌──────────────────┬────────────────────────────────────────────────────┐ │ revision control │ git │ │ remote url │ N/A │ │ repository root │ /home/deployer/example.com/releases/20190531213027 │ │ last update │ 2019-05-31T21:30:48.559Z │ │ revision │ 62fba7c8c61c7769022484d0bfa46e756fac8099 │ │ comment │ Our first commit │ │ branch │ master │ └──────────────────┴────────────────────────────────────────────────────┘ Divergent env variables from local env ┌───────────────────────────┬───────────────────────────────────────┐ │ XDG_SESSION_ID │ 15 │ │ HOSTNAME │ N/A │ │ SELINUX_ROLE_REQUESTED │ │ │ TERM │ N/A │ │ HISTSIZE │ N/A │ │ SSH_CLIENT │ 44.222.77.111 58545 22 │ │ SELINUX_USE_CURRENT_RANGE │ │ │ SSH_TTY │ N/A │ │ LS_COLORS │ N/A │ │ MAIL │ /var/mail/deployer │ │ PATH │ /usr/local/bin:/usr/bin │ │ SELINUX_LEVEL_REQUESTED │ │ │ HISTCONTROL │ N/A │ │ SSH_CONNECTION │ 44.222.77.111 58545 209.97.167.252 22 │ └───────────────────────────┴───────────────────────────────────────┘ . . .

PM2 bietet auch ein In-Terminal-Überwachungstool, auf das wie folgt zugegriffen werden kann:

  • pm2 monit

Die Ausgabe dieses Befehls ist ein interaktives Dashboard, in dem pm2 Prozessinformationen, Protokolle, Metriken und Metadaten in Echtzeit zur Verfügung stellt. Dieses Dashboard kann bei der Überwachung von Ressourcen und Fehlerprotokollen helfen:

Output
┌─ Process list ────────────────┐┌─ Global Logs ─────────────────────────────────────────────────────────────┐ │[ 0] hello Mem: 22 MB ││ │ │ ││ │ │ ││ │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘ ┌─ Custom metrics (http://bit.l─┐┌─ Metadata ────────────────────────────────────────────────────────────────┐ │ Heap Size 10.73 ││ App Name hello │ │ Heap Usage 66.14 ││ Version N/A │ │ Used Heap Size 7.10 ││ Restarts 0 │ │ Active requests 0 ││ Uptime 55s │ │ Active handles 4 ││ Script path /home/asciant/hello.js │ │ Event Loop Latency 0.70 ││ Script args N/A │ │ Event Loop Latency p95 ││ Interpreter node │ │ ││ Interpreter args N/A │ └───────────────────────────────┘└───────────────────────────────────────────────────────────────────────────┘

Wenn Sie wissen, wie Sie Ihre Prozesse mit PM2 überwachen können, können Sie fortfahren und lernen, wie Shipit Ihnen bei dem Rollback einer vorherigen funktionierenden Bereitstellung helfen kann.

Beenden Sie ihre ssh-Sitzung auf Ihrem App-Server, indem Sie exit ausführen.

Schritt 7 – Rollback einer fehlerhaften Bereitstellung

Bei der Bereitstellung werden gelegentlich unvorhergesehene Fehler oder Probleme aufgedeckt, die zum Ausfall Ihrer Website führen. Die Entwickler und Betreuer von Shipit haben dies vorausgesehen und Ihnen die Möglichkeit gegeben, ein Rollback zu einer vorherigen (funktionierenden) Bereitstellung Ihrer Anwendung durchzuführen.

Um sicherzustellen, dass Ihre PM2-Konfiguration erhalten bleibt, fügen Sie shipitfile.js einen weiteren Ereignis-Listener für das Ereignis rollback hinzu:

shipitfile.js
. . .
  shipit.on('rollback', () => {
    shipit.start('npm-install', 'copy-config');
  });

Sie fügen dem Ereignis rollback einen Listener hinzu, um Ihre Aufgaben npm-install und copy-config auszuführen. Dies ist erforderlich, da das Ereignis updated im Gegensatz zum Ereignis published beim Rollback einer Bereitstellung nicht durch den Shipit-Lebenszyklus ausgeführt wird. Das Hinzufügen dieses Ereignis-Listeners stellt sicher, dass Ihr PM2-Prozessmanager selbst im Falle eines Rollbacks auf die letzte Bereitstellung verweist.

Dieser Prozess ähnelt der Bereitstellung, mit einer kleinen Befehlsänderung. Um ein Rollback auf eine frühere Bereitstellung zu versuchen, können Sie Folgendes ausführen:

  • npx shipit production rollback

Wie der Befehl deploy stellt rollback Details zum Rollback-Prozess und zu den ausgeführten Aufgaben bereit:

Output
Running 'rollback:init' task... Get current release dirname. Running "if [ -h /home/deployer/example.com/current ]; then readlink /home/deployer/example.com/current; fi" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com releases/20190531213719 Current release dirname : 20190531213719. Getting dist releases. Running "ls -r1 /home/deployer/example.com/releases" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com 20190531213719 @centos-ap-app.asciant.com 20190531213519 @centos-ap-app.asciant.com 20190531213027 Dist releases : ["20190531213719","20190531213519","20190531213027"]. Will rollback to 20190531213519. Finished 'rollback:init' after 3.96 s Running 'deploy:publish' task... Publishing release "/home/deployer/example.com/releases/20190531213519" Running "cd /home/deployer/example.com && if [ -d current ] && [ ! -L current ]; then echo "ERR: could not make symlink"; else ln -nfs releases/20190531213519 current_tmp && mv -fT current_tmp current; fi" on host "centos-ap-app.asciant.com". Release published. Finished 'deploy:publish' after 1.8 s Running 'pm2-server' task... Running "pm2 delete -s hello || :" on host "centos-ap-app.asciant.com". Running "pm2 start /home/deployer/example.com/shared/ecosystem.config.js --env production --watch true" on host "centos-ap-app.asciant.com". @centos-ap-app.asciant.com [PM2][WARN] Node 4 is deprecated, please upgrade to use pm2 to have all features @centos-ap-app.asciant.com [PM2][WARN] Applications hello not running, starting... @centos-ap-app.asciant.com [PM2] App [hello] launched (1 instances) @centos-ap-app.asciant.com ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────────┬──────────┐ @centos-ap-app.asciant.com │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ @centos-ap-app.asciant.com ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────────┼──────────┤ @centos-ap-app.asciant.com │ hello │ 0 │ 1.0.0 │ fork │ 4289 │ online │ 0 │ 0s │ 0% │ 4.5 MB │ deployer │ enabled │ @centos-ap-app.asciant.com └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────────┴──────────┘ @centos-ap-app.asciant.com Use `pm2 show <id|name>` to get more details about an app Finished 'pm2-server' after 5.55 s Running 'deploy:clean' task... Keeping "5" last releases, cleaning others Running "(ls -rd /home/deployer/example.com/releases/*|head -n 5;ls -d /home/deployer/example.com/releases/*)|sort|uniq -u|xargs rm -rf" on host "centos-ap-app.asciant.com". Finished 'deploy:clean' after 1.82 s Running 'rollback:finish' task... Finished 'rollback:finish' after 615 μs Finished 'rollback' [ rollback:init, deploy:publish, deploy:clean, rollback:finish ]

Über die Konfiguration keepReleases: 5 in shipitfile.js haben Sie Shipit so konfiguriert, dass 5 Versionen behalten werden. Shipit verfolgt diese Versionen intern, um sicherzustellen, dass es in der Lage ist, bei Bedarf ein Rollback durchzuführen. Shipit bietet auch eine praktische Möglichkeit, die Releases zu identifizieren, indem es ein Verzeichnis erstellt, das als Zeitstempel benannt wird (JJJJMMTTHHmmss - Beispiel: /home/deployer/your-domain/releases/20190420210548).

Wenn Sie den Rollback-Prozess weiter anpassen wollten, können Sie auf Ereignisse lauschen, die für den Rollback-Vorgang spezifisch sind. Diese Ereignisse können Sie dann zur Ausführung von Aufgaben verwenden, die Ihren Rollback-Prozess ergänzen. Sie können dazu auf die Ereignisliste in der Aufschlüsselung des Shipit-Lebenszyklus zurückgreifen und die Aufgaben/Listener innerhalb Ihrer shipitfile.js konfigurieren.

Die Fähigkeit zum Rollback bedeutet, dass Sie Ihren Benutzern immer eine funktionierende Version Ihrer Anwendung zur Verfügung stellen können, selbst wenn eine Bereitstellung unerwartete Fehler/Probleme mit sich bringt.

Zusammenfassung

In diesem Tutorial haben Sie einen Workflow konfiguriert, mit dem Sie eine hochgradig anpassbare Alternative zu „Platform as a Service“ erstellen können, und das alles von zwei Servern aus. Dieser Workflow ermöglicht eine kundenspezifische Bereitstellung und Konfiguration, die Prozessüberwachung mit PM2, die Möglichkeit zur Skalierung und dem Hinzufügen von Diensten oder zusätzlichen Servern oder Umgebungen zu der Bereitstellung, wenn dies erforderlich ist.

Wenn Sie daran interessiert sind, Ihre Node.js-Fähigkeiten weiterzuentwickeln, sehen Sie sich den Inhalt von DigitalOcean Node.js sowie die Serie So codieren Sie in Node.js an.

Creative Commons License