Dieser Beitrag ist eines von vier Tutorials, die Ihnen helfen, Konzepte aus Microservices März 2023 in die Praxis umzusetzen: Beginnen Sie mit der Bereitstellung von Microservices :
Alle Apps müssen konfiguriert werden, aber die Überlegungen beim Konfigurieren eines Microservices sind möglicherweise nicht dieselben wie bei einer monolithischen App . Wir können uns Faktor 3 ( Konfiguration in der Umgebung speichern ) der Zwölf-Faktoren-App ansehen, um eine Anleitung zu erhalten, die für beide App-Typen gilt, aber diese Anleitung kann für Microservices-Apps angepasst werden. Insbesondere können wir die Art und Weise anpassen, wie wir die Dienstkonfiguration definieren, die Konfiguration einem Dienst bereitstellen und einen Dienst als Konfigurationsparameter für andere Dienste verfügbar machen, die möglicherweise davon abhängen.
Um ein konzeptionelles Verständnis dafür zu erhalten, wie Sie Faktor 3 für Microservices anpassen können – insbesondere die Best Practices für Konfigurationsdateien, Datenbanken und Service Discovery – lesen Sie Best Practices für die Konfiguration von Microservices-Apps in unserem Blog. Dieser Beitrag ist eine großartige Möglichkeit, dieses Wissen in die Praxis umzusetzen.
Notiz: Unsere Absicht in diesem Tutorial besteht darin, einige Kernkonzepte zu veranschaulichen und nicht zu zeigen, wie Microservices richtig in der Produktion eingesetzt werden. Obwohl eine echte „Microservices“-Architektur verwendet wird, gibt es einige wichtige Einschränkungen:
Dieses Tutorial veranschaulicht, wie sich Faktor-3-Konzepte auf Microservices-Apps anwenden lassen. In vier Herausforderungen erkunden Sie einige gängige Konfigurationsmuster für Microservices und stellen mithilfe dieser Muster einen Dienst bereit und konfigurieren ihn:
In Challenge 1 und Challenge 2 erkunden Sie das erste Muster, bei dem es darum geht, wo Sie die Konfiguration für eine Microservices-App finden. Es gibt drei typische Speicherorte:
Das Tutorial verwendet vier Technologien:
Sehen Sie sich dieses Video an, um einen Überblick über das Tutorial zu erhalten. Die Schritte stimmen nicht genau mit diesem Beitrag überein, aber sie helfen beim Verständnis der Konzepte.
Um das Tutorial in Ihrer eigenen Umgebung abzuschließen, benötigen Sie:
Bash
(allerdings werden sämtliche Codes und Befehle bereitgestellt und erklärt, sodass Sie auch mit begrenzten Kenntnissen erfolgreich sein können)Node.js 19.x oder höher
asdf
installieren, um genau dieselbe Node.js-Version zu erhalten, die in den Containern verwendet wird.curl
(auf den meisten Systemen bereits installiert)Erstellen Sie in Ihrem Home-Verzeichnis das Verzeichnis „Microservices-March“ und klonen Sie die GitHub-Repositorys für dieses Tutorial dort hinein. (Sie können auch einen anderen Verzeichnisnamen verwenden und die Anweisungen entsprechend anpassen.)
Notiz: Im gesamten Tutorial wird die Eingabeaufforderung in der Linux-Befehlszeile weggelassen, um das Kopieren und Einfügen der Befehle in Ihr Terminal zu erleichtern. Die Tilde ( ~
) steht für Ihr Home-Verzeichnis.
mkdir ~/microservices-marchcd ~/microservices-march
git-Klon https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start
git-Klon https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start
Wechseln Sie zum Plattform -Repository und starten Sie Docker Compose:
cd platformdocker komponieren -d --build
Dadurch werden sowohl RabbitMQ als auch Consul gestartet, die in nachfolgenden Herausforderungen verwendet werden.
-d
weist Docker Compose an, sich von den Containern zu trennen, wenn diese gestartet wurden (andernfalls bleiben die Container an Ihr Terminal angeschlossen).--build
weist Docker Compose an, beim Start alle Images neu zu erstellen. Dadurch wird sichergestellt, dass die von Ihnen ausgeführten Bilder bei allen möglichen Dateiänderungen auf dem neuesten Stand bleiben.Wechseln Sie zum Messenger -Repository und starten Sie Docker Compose:
cd ../messengerdocker komponieren -d --build
Dadurch wird die PostgreSQL-Datenbank für den Messenger -Dienst gestartet, die wir im weiteren Verlauf des Tutorials als Messenger-Datenbank bezeichnen werden.
Bei dieser Herausforderung richten Sie die Konfiguration am ersten der drei Orte ein, die wir uns im Tutorial ansehen werden: der Anwendungsebene. ( Herausforderung 2 veranschaulicht den zweiten und dritten Standort, Bereitstellungsskripte und externe Quellen.)
Die Zwölf‑Faktor‑App schließt die Konfiguration auf Anwendungsebene ausdrücklich aus, da eine solche Konfiguration zwischen unterschiedlichen Bereitstellungsumgebungen (die die Zwölf‑Faktor‑App als „Bereitstellungen“ bezeichnet) nicht geändert werden muss. Der Vollständigkeit halber behandeln wir dennoch alle drei Typen. Die Art und Weise, wie Sie beim Entwickeln, Erstellen und Bereitstellen eines Dienstes mit den einzelnen Kategorien umgehen, ist unterschiedlich.
Der Messenger- Dienst ist in Node.js geschrieben, mit dem Einstiegspunkt in app/index.mjs im Messenger- Repo. Diese Zeile der Datei:
app.use(express.json());
ist ein Beispiel für eine Konfiguration auf Anwendungsebene. Es konfiguriert das Express-Framework so, dass Anforderungstexte vom Typ „application/json“
in JavaScript-Objekte deserialisiert werden.
Diese Logik ist eng mit Ihrem Anwendungscode verknüpft und ist nicht das, was die Zwölf-Faktor-App als „Konfiguration“ betrachtet. Bei Software hängt jedoch alles von Ihrer Situation ab, nicht wahr?
In den nächsten beiden Abschnitten ändern Sie diese Zeile, um zwei Beispiele für die Konfiguration auf Anwendungsebene zu implementieren.
In diesem Beispiel legen Sie die maximale Größe eines Anforderungstexts fest, der vom Nachrichtendienst akzeptiert wird. Diese Größenbeschränkung wird durch das Limit
-Argument der Funktion express.json festgelegt, wie in der Express-API-Dokumentation beschrieben. Hier fügen Sie der oben beschriebenen Konfiguration der JSON-Middleware des Express-Frameworks das Limit-
Argument hinzu.
Öffnen Sie app/index.mjs in Ihrem bevorzugten Texteditor und ersetzen Sie:
app.verwenden(express.json())
mit:
app.use(express.json({ Grenze: "20b" }));
Wechseln Sie im App-Terminal (das Sie beim Einrichten verwendet haben) in das App -Verzeichnis und starten Sie den Messenger- Dienst:
cd app npm install node index.mjs messenger_service lauscht auf Port 4000
Starten Sie eine zweite, separate Terminalsitzung (die durch nachfolgende Anweisungen das Client-Terminal aufgerufen wird) und senden Sie eine POST-
Anfrage an den Messenger- Dienst. Die Fehlermeldung zeigt an, dass die Anforderung erfolgreich verarbeitet wurde, da der Anforderungstext unter dem in Schritt 1 festgelegten 20-Byte -Limit lag, der Inhalt der JSON-Nutzlast jedoch falsch ist:
curl -d '{ "text": "hallo" }' -H "Inhaltstyp: application/json" -X POST http://localhost:4000/conversations ... { "Fehler": "Die Konversation muss 2 eindeutige Benutzer haben" }
Senden Sie einen etwas längeren Nachrichtentext (erneut im Client-Terminal). Die Ausgabe ist wesentlich umfangreicher als in Schritt 3, einschließlich einer Fehlermeldung, die darauf hinweist, dass der Anforderungstext diesmal größer als 20 Bytes ist:
curl -d '{ "text": "Hallo Welt" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations ... \”PayloadTooLargeError: Anforderungsentität zu groß"
In diesem Beispiel wird convict
verwendet, eine Bibliothek, mit der Sie ein vollständiges Konfigurationsschema in einer einzigen Datei definieren können. Darüber hinaus werden zwei Leitlinien aus Faktor 3 der Zwölf-Faktoren-App veranschaulicht:
JSON_BODY_LIMIT
) festgelegt wird, anstatt im App-Code fest codiert zu sein.Das Beispiel richtet auch einige „Grundlagen“ ein, die Sie in Herausforderung 2 nutzen werden: Das Messenger- Bereitstellungsskript, das Sie in dieser Herausforderung erstellen, legt die Umgebungsvariable JSON_BODY_LIMIT
fest, die Sie hier in den App-Code einfügen, als Beispiel für die in einem Bereitstellungsskript angegebene Konfiguration.
Öffnen Sie die Convict-
Konfigurationsdatei app/config/config.mjs und fügen Sie nach dem Schlüssel amqpport
Folgendes als neuen Schlüssel hinzu:
jsonBodyLimit: { doc: `Die maximale Größe (einschließlich Einheit), die von der
JSON-Middleware analysiert wird. Die Unit-Analyse erfolgt durch die
Bibliothek https://www.npmjs.com/package/bytes.
Beispiel: "100kb"`,
Format: Zeichenfolge,
Standard: null,
Umgebung: "JSON_BODY_LIMIT",
},
Die Convict
-Bibliothek kümmert sich um die Analyse der Umgebungsvariable JSON_BODY_LIMIT
, wenn Sie diese verwenden, um in Schritt 3 unten in der Befehlszeile die maximale Textgröße festzulegen:
String
).jsonBodyLimit
Ersetzen Sie in app/index.mjs :
app.use(express.json({ Grenze: "20b" }));
mit
app.use(express.json({ limit: config.get("jsonBodyLimit") }));
Drücken Sie im App-Terminal (wo Sie den Messenger- Dienst in Schritt 2 von Beispiel 1 gestartet haben) Strg +C
, um den Dienst zu stoppen. Starten Sie es dann erneut und verwenden Sie die Umgebungsvariable JSON_BODY_LIMIT
, um die maximale Textgröße auf 27 Byte festzulegen:
^cJSON_BODY_LIMIT=27b Knotenindex.mjs
Dies ist ein Beispiel für die Änderung der Konfigurationsmethode, wenn dies für Ihren Anwendungsfall sinnvoll ist – Sie sind von der Festcodierung eines Werts (in diesem Fall einer Größenbeschränkung) im App-Code zum Festlegen mit einer Umgebungsvariablen übergegangen, wie von der Zwölf-Faktor-App empfohlen.
Wie oben erwähnt, wird in Herausforderung 2 die Verwendung der Umgebungsvariablen JSON_BODY_LIMIT
zu einem Beispiel für den zweiten Konfigurationsort, wenn Sie das Bereitstellungsskript des Messenger -Dienstes verwenden, um die Umgebungsvariable festzulegen, anstatt sie in der Befehlszeile festzulegen.
Wiederholen Sie im Client-Terminal den Curl
-Befehl aus Schritt 4 von Beispiel 1 (mit dem größeren Anforderungstext). Da Sie die Größenbeschränkung nun auf 27 Byte erhöht haben, überschreitet der Anforderungstext die Beschränkung nicht mehr und Sie erhalten die Fehlermeldung, die angibt, dass die Anforderung verarbeitet wurde, der Inhalt der JSON-Nutzlast jedoch falsch ist:
curl -d '{ "text": "Hallo Welt" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations { "error": "Die Konversation muss 2 eindeutige Benutzer haben" }
Sie können das Client-Terminal bei Bedarf schließen. Alle Befehle im weiteren Verlauf des Tutorials geben Sie im App-Terminal ein.
Drücken Sie im App-Terminal Strg+C
, um den Messenger -Dienst zu stoppen (Sie haben den Dienst in diesem Terminal oben in Schritt 3 gestoppt und neu gestartet).
^c
Stoppen Sie die Messenger-Datenbank . Sie können die angezeigte Fehlermeldung ignorieren, da das Netzwerk weiterhin von den im Plattform- Repository definierten Infrastrukturelementen verwendet wird. Führen Sie diesen Befehl im Stammverzeichnis des Messenger -Repos aus.
Docker Compose Down … Netzwerk mm_2023 konnte nicht entfernt werden …
Auf den ersten Blick könnte man dies so interpretieren: „Checken Sie die Konfiguration nicht in die Quellcodeverwaltung ein.“ Bei dieser Herausforderung implementieren Sie ein allgemeines Muster für Microservices-Umgebungen, das diese Regel scheinbar verletzt, sie in Wirklichkeit jedoch einhält und gleichzeitig wertvolle Prozessverbesserungen bietet, die für Microservices-Umgebungen von entscheidender Bedeutung sind.
Bei dieser Herausforderung erstellen Sie Bereitstellungsskripte, um die Funktionalität von Infrastruktur als Code und Bereitstellungsmanifesten nachzuahmen, die die Konfiguration eines Mikrodienstes bereitstellen. Sie ändern die Skripte , um externe Konfigurationsquellen zu verwenden, legen ein Geheimnis fest und führen dann die Skripte aus , um Dienste und ihre Infrastruktur bereitzustellen.
Sie erstellen die Bereitstellungsskripte in einem neu erstellten Infrastrukturverzeichnis im Messenger- Repo. Ein Verzeichnis mit dem Namen „Infrastruktur“ (oder einer Variante dieses Namens) ist ein gängiges Muster in modernen Microservice-Architekturen und wird zum Speichern von Dingen wie den folgenden verwendet:
Zu den Vorteilen dieses Musters gehören:
Wie bereits erwähnt, besteht unsere Absicht mit diesem Tutorial nicht darin, zu zeigen, wie ein echtes System eingerichtet wird, und die Skripte, die Sie in dieser Herausforderung einsetzen, ähneln keinem echten Produktionssystem. Sie veranschaulichen vielmehr einige Kernkonzepte und Probleme, die durch eine toolspezifische Konfiguration beim Umgang mit der Bereitstellung einer Microservices-bezogenen Infrastruktur gelöst werden, während sie gleichzeitig die Skripte auf die geringstmögliche Menge an spezifischen Tools abstrahieren.
Erstellen Sie im App-Terminal ein Infrastrukturverzeichnis im Stammverzeichnis des Messenger- Repositorys und erstellen Sie Dateien, die die Bereitstellungsskripte für den Messenger- Dienst und die Messenger-Datenbank enthalten. Abhängig von Ihrer Umgebung müssen Sie den chmod
-Befehlen möglicherweise das Präfix sudo
voranstellen:
mkdir Infrastrukturcd Infrastruktur
touch messenger-deploy.sh
chmod +x messenger-deploy.sh
touch messenger-db-deploy.sh
chmod +x messenger-db-deploy.sh
Öffnen Sie messenger-deploy.sh in Ihrem bevorzugten Texteditor und fügen Sie Folgendes hinzu, um ein erstes Bereitstellungsskript für den Messenger -Dienst zu erstellen:
#!/bin/bashset -e JSON_BODY_LIMIT=20b docker run \ --rm \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ Messenger
Dieses Skript ist zu diesem Zeitpunkt noch nicht vollständig, veranschaulicht jedoch einige Konzepte:
-e
im Docker
-Run
-Befehl, um zur Laufzeit Umgebungsvariablen in den Container einzufügen.Es mag überflüssig erscheinen, den Wert von Umgebungsvariablen auf diese Weise festzulegen, aber es bedeutet – unabhängig davon, wie komplex dieses Bereitstellungsskript wird –, dass Sie einen kurzen Blick auf den Anfang des Skripts werfen und verstehen können, wie Konfigurationsdaten für die Bereitstellung bereitgestellt werden.
Obwohl ein echtes Bereitstellungsskript den Befehl „Docker
Run“
möglicherweise nicht explizit aufruft, soll dieses Beispielskript die Kernprobleme verdeutlichen, die durch etwas wie ein Kubernetes-Manifest gelöst werden. Wenn Sie ein Container-Orchestrierungssystem wie Kubernetes verwenden, startet eine Bereitstellung einen Container und die aus Ihren Kubernetes-Konfigurationsdateien abgeleitete Anwendungskonfiguration wird diesem Container zur Verfügung gestellt. Daher können wir diese Beispielbereitstellungsdatei als Minimalversion eines Bereitstellungsskripts betrachten, das dieselbe Rolle spielt wie rahmenspezifische Bereitstellungsdateien, z. B. Kubernetes-Manifeste.
In einer echten Entwicklungsumgebung könnten Sie diese Datei in die Quellcodeverwaltung einchecken und einer Codeüberprüfung unterziehen. Dadurch erhält der Rest Ihres Teams die Möglichkeit, Ihre Einstellungen zu kommentieren. So können Vorfälle vermieden werden, bei denen falsch konfigurierte Werte zu unerwartetem Verhalten führen. Beispielsweise weist in diesem Screenshot ein Teammitglied zu Recht darauf hin, dass ein Limit von 20 Bytes für eingehende JSON-Anforderungstexte (festgelegt mit JSON_BODY_LIMIT
) zu niedrig ist.
In diesem Teil der Herausforderung richten Sie den dritten Speicherort für die Konfiguration eines Mikrodienstes ein: eine externe Quelle, die zum Bereitstellungszeitpunkt abgefragt wird. Das dynamische Registrieren von Werten und deren Abrufen aus einer externen Quelle zum Bereitstellungszeitpunkt ist eine weitaus bessere Vorgehensweise als das Festcodieren von Werten, die ständig aktualisiert werden müssen und Fehler verursachen können. Eine Erläuterung hierzu finden Sie in unserem Blog unter „Best Practices zum Konfigurieren von Microservices-Apps“ .
An dieser Stelle laufen im Hintergrund zwei Infrastrukturkomponenten, die vom Messenger -Dienst benötigte Zusatzdienste bereitstellen:
Das Convict
-Schema für den Messenger- Dienst in app/config/config.mjs definiert die erforderlichen Umgebungsvariablen, die diesen Teilen der externen Konfiguration entsprechen. In diesem Abschnitt richten Sie diese beiden Komponenten ein, um die Konfiguration bereitzustellen, indem Sie die Werte der Variablen an einem allgemein zugänglichen Ort festlegen, sodass sie beim Bereitstellen vom Messenger -Dienst abgefragt werden können.
Die erforderlichen Verbindungsinformationen für RabbitMQ und die Messenger-Datenbank werden im Consul Key/Value (KV)-Speicher registriert, einem gemeinsamen Speicherort, auf den alle Dienste bei ihrer Bereitstellung zugreifen können. Der Consul KV-Speicher ist kein Standardspeicherort für diese Art von Daten, wird in diesem Tutorial jedoch der Einfachheit halber verwendet.
Ersetzen Sie den Inhalt von infrastructure/messenger-deploy.sh (erstellt in Schritt 2 des vorherigen Abschnitts ) durch Folgendes:
#!/bin/bashset -e # Diese Konfiguration erfordert ein neues Commit zum Ändern von NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Postgres-Datenbankkonfiguration durch Abrufen von Informationen aus # dem System POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true) PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true) PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true) # RabbitMQ-Konfiguration durch Abrufen aus dem System AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true) AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true) docker run \ --rm \ -e NODE_ENV="${NODE_ENV} " \ -e PORT="${PORT} " \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ -e PGUSER="${POSTGRES_USER} " \ -e PGPORT="${PGPORT} " \ -e PGHOST="${PGHOST} " \ -e AMQPPORT="${AMQPPORT} " \ -e AMQPHOST="${AMQPHOST} " \ Messenger
Dieses Skript veranschaulicht zwei Konfigurationstypen:
NODE_ENV
) und den Port ( PORT
) fest und ändert JSON_BODY_LIMIT
auf 100 KB, ein realistischerer Wert als 20 Bytes.POSTGRES_USER
, PGPORT
, PGHOST
, AMQPHOST
und AMQPPORT
aus dem Consul KV-Speicher ab. Sie legen die Werte der Umgebungsvariablen im Consul KV Store in den folgenden zwei Schritten fest.Öffnen Sie messenger-db-deploy.sh und fügen Sie Folgendes hinzu, um ein erstes Bereitstellungsskript für die Messenger-Datenbank zu erstellen:
#!/bin/bashset -e PORT=5432 POSTGRES_USER=postgres docker run \ -d \ --rm \ --name messenger-db \ -v db-data:/var/lib/postgresql/data/pgdata \ -e POSTGRES_USER="${POSTGRES_USER} " \ -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " \ -e PGDATA=/var/lib/postgresql/data/pgdata \ --network mm_2023 \ postgres:15.1 # Details zur Datenbank mit Consul registrieren curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \ -H "Content-Type: application/json" \ -d "${PORT} " curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \ -H "Content-Type: application/json" \ -d 'messenger-db' # Dies entspricht dem Flag "--name" oben # (dem Hostnamen) curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \ -H "Content-Type: application/json" \ -d "${POSTGRES_USER} "
Zusätzlich zur Definition einer Konfiguration, die vom Nachrichtendienst zum Bereitstellungszeitpunkt abgefragt werden kann, veranschaulicht das Skript dieselben zwei Konzepte wie das ursprüngliche Skript für den Nachrichtendienst aus „Skripts für die erste Bereitstellung erstellen“ ):
-e
aus, um Umgebungsvariablen zur Laufzeit in den Container einzufügen. Dadurch wird auch der Name des laufenden Containers auf messenger-db festgelegt. Dieser wird zum Hostnamen der Datenbank im Docker-Netzwerk, das Sie beim Starten des Plattformdienstes in Schritt 2 der Einrichtung erstellt haben.Bei einer echten Bereitstellung ist es normalerweise das Plattformteam (oder ein ähnliches Team), das die Bereitstellung und Wartung eines Dienstes wie RabbitMQ im Plattform -Repo übernimmt, so wie Sie dies für die Messenger-Datenbank im Messenger -Repo tun. Das Plattformteam stellt dann sicher, dass der Standort dieser Infrastruktur für die davon abhängigen Dienste erkennbar ist. Legen Sie für die Zwecke des Tutorials die RabbitMQ-Werte selbst fest:
curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Inhaltstyp: application/json" \
-d "rabbitmq" \
http://localhost:8500/v1/kv/amqp-host
curl -X PUT --silent --output /dev/null --show-error --fail \
-H "Inhaltstyp: application/json" \
-d "5672" \
http://localhost:8500/v1/kv/amqp-port
(Sie fragen sich vielleicht, warum amqp
zum Definieren von RabbitMQ-Variablen verwendet wird – das liegt daran, dass AMQP das von RabbitMQ verwendete Protokoll ist.)
In den Bereitstellungsskripten für den Messenger- Dienst fehlt nur eine (kritische) Angabe – das Passwort für die Messenger-Datenbank !
Notiz: Die Verwaltung von Geheimnissen ist nicht der Schwerpunkt dieses Tutorials, daher wird das Geheimnis der Einfachheit halber in Bereitstellungsdateien definiert. Tun Sie dies niemals in einer realen Umgebung (Entwicklung, Test oder Produktion). Dies stellt ein enormes Sicherheitsrisiko dar .
Um mehr über die richtige Verwaltung von Geheimnissen zu erfahren, sehen Sie sich Einheit 2, Microservices Secrets Management 101 von Microservices vom März 2023 an. (Spoiler: Ein Tool zur Geheimnisverwaltung ist die einzige wirklich sichere Methode zum Speichern von Geheimnissen).
Ersetzen Sie den Inhalt von infrastructure/messenger-db-deploy.sh durch Folgendes, um das geheime Kennwort für die Messenger-Datenbank im Consul KV-Speicher zu speichern:
#!/bin/bashset -e PORT=5432 POSTGRES_USER=postgres # HINWEIS: Tun Sie dies niemals bei einem Einsatz in der realen Welt. Speichern Sie Passwörter # nur in einem verschlüsselten Geheimnisspeicher.
POSTGRES_PASSWORD=postgres docker run \ --rm \ --name messenger-db-primary \ -d \ -v db-data:/var/lib/postgresql/data/pgdata \ -e POSTGRES_USER="${POSTGRES_USER} " \ -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " \ -e PGDATA=/var/lib/postgresql/data/pgdata \ --network mm_2023 \ postgres:15.1 echo "Schlüssel messenger-db-port registrieren\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \ -H "Content-Type: application/json" \ -d "${PORT} " echo "Register key messenger-db-host\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \ -H "Content-Type: application/json" \ -d 'messenger-db-primary' # Dies entspricht dem Flag "--name" oben, # was für unser Setup den Hostnamen bedeutet echo "Register key messenger-db-application-user\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \ -H "Content-Type: application/json" \ -d "${POSTGRES_USER} " echo "Schlüssel messenger-db-password-never-do-this registrieren\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \ -H "Content-Type: application/json" \ -d "${POSTGRES_PASSWORD} " printf "\nRegistrierung der Postgres-Details bei Consul abgeschlossen\n"
Ersetzen Sie den Inhalt von infrastructure/messenger-deploy.sh durch Folgendes, um das geheime Passwort der Messenger-Datenbank aus dem Consul KV-Speicher abzurufen:
#!/bin/bashset -e # Diese Konfiguration erfordert ein neues Commit zum Ändern von NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Postgres-Datenbankkonfiguration durch Abrufen von Informationen aus # dem System POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true) PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true) PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true) # HINWEIS: Tun Sie dies niemals bei einem Einsatz in der realen Welt. Speichern Sie Passwörter # nur in einem verschlüsselten Geheimnisspeicher.
PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true) # RabbitMQ-Konfiguration durch Abrufen aus dem System AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true) AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true) docker run \ --rm \ -d \ -e NODE_ENV="${NODE_ENV} " \ -e PORT="${PORT} " \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ -e PGUSER="${POSTGRES_USER} " \ -e PGPORT="${PGPORT} " \ -e PGHOST="${PGHOST} " \ -e PGPASSWORD="${PGPASSWORD} " \ -e AMQPPORT="${AMQPPORT} " \ -e AMQPHOST="${AMQPHOST} " \ --network mm_2023 \ messenger
Wechseln Sie in das App -Verzeichnis im Messenger- Repo und erstellen Sie das Docker-Image für den Messenger -Dienst:
cd ../appdocker build -t messenger.
Stellen Sie sicher, dass nur die Container ausgeführt werden, die zum Plattformdienst gehören:
docker ps --format '{{.Names}}' Konsul-Server Konsul-Client rabbitmq
Wechseln Sie zum Stammverzeichnis des Messenger -Repository und stellen Sie die Messenger-Datenbank und den Messenger- Dienst bereit:
cd …/infrastructure/messenger-db-deploy.sh
./infrastructure/messenger-deploy.sh
Das Skript messenger-db-deploy.sh startet die Messenger-Datenbank und registriert die entsprechenden Informationen beim System (in diesem Fall beim Consul KV Store).
Das Skript messenger-deploy.sh startet dann die Anwendung und ruft die von messenger-db-deploy.sh registrierte Konfiguration aus dem System ab (wiederum dem Consul KV Store).
Hinweis: Wenn der Start eines Containers fehlschlägt, entfernen Sie den zweiten Parameter des Docker
-Run
-Befehls (die Zeile -d
\
) im Bereitstellungsskript und führen Sie das Skript erneut aus. Der Container startet dann im Vordergrund, was bedeutet, dass seine Protokolle im Terminal angezeigt werden und das Problem möglicherweise identifizieren. Wenn Sie das Problem behoben haben, stellen Sie die Zeile -d
\
wieder her, damit der eigentliche Container im Hintergrund ausgeführt wird.
Senden Sie eine einfache Integritätsprüfungsanforderung an die Anwendung, um zu überprüfen, ob die Bereitstellung erfolgreich war:
curl localhost:4000/Gesundheit curl: (7) Die Verbindung zum Localhost-Port 4000 konnte nach 11 ms nicht hergestellt werden: Verbindung abgelehnt
Ups, Fehler! Wie sich herausstellt, fehlt noch immer ein kritischer Konfigurationsteil und der Messenger- Dienst wird dem weiteren System nicht angezeigt. Es läuft problemlos im mm_2023 -Netzwerk, aber auf dieses Netzwerk kann nur von Docker aus zugegriffen werden.
Stoppen Sie den laufenden Container zur Vorbereitung der Erstellung eines neuen Images in der nächsten Herausforderung:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
In einer Produktionsumgebung stellen Sie Dienste im Allgemeinen nicht direkt bereit. Stattdessen folgen Sie einem gängigen Microservices-Muster und platzieren einen Reverse-Proxy-Dienst vor Ihrem Hauptdienst.
Bei dieser Herausforderung machen Sie den Messenger- Dienst der Außenwelt zugänglich, indem Sie die Diensterkennung einrichten: die Registrierung neuer Dienstinformationen und die dynamische Aktualisierung dieser Informationen beim Zugriff durch andere Dienste. Dazu nutzen Sie diese Technologien:
Weitere Informationen zur Diensterkennung finden Sie im Abschnitt „Einen Dienst als Konfiguration verfügbar machen“ in den Best Practices zum Konfigurieren von Microservices-Apps in unserem Blog.
Die Datei app/consul/index.mjs im Messenger- Repo enthält den gesamten Code, der erforderlich ist, um den Messenger- Dienst beim Start bei Consul zu registrieren und ihn beim ordnungsgemäßen Herunterfahren abzumelden. Es stellt eine Funktion bereit, „register“
, die alle neu bereitgestellten Dienste im Dienstregister von Consul registriert.
Öffnen Sie app/index.mjs in Ihrem bevorzugten Texteditor und fügen Sie nach den anderen Importanweisungen
den folgenden Codeausschnitt hinzu, um die Registerfunktion
aus app/consul/index.mjs zu importieren:
importiere { registriere dich als registerConsul } von "./consul/index.mjs";
Ändern Sie dann den Abschnitt SERVER
START
am Ende des Skripts wie gezeigt, um registerConsul()
aufzurufen, nachdem die Anwendung gestartet wurde:
/* ================== SERVERSTART ================== */ app.listen(port, async () => { console.log(`messenger_service lauscht auf Port${port} `); registerConsul(); }); Standard-App exportieren;
Öffnen Sie das Convict
-Schema in app/config/config.mjs und fügen Sie die folgenden Konfigurations-Werte nach dem Schlüssel „jsonBodyLimit“
hinzu, den Sie in Schritt 1 von Beispiel 2 hinzugefügt haben.
consulServiceName: { dokument: „Der Name, unter dem der Dienst bei Consul registriert ist. Wenn nicht angegeben, ist der Dienst nicht registriert",
Format: "*",
Standard: null,
Umgebung: "CONSUL_SERVICE_NAME",
},
consulHost: {
dokument: „Der Host, auf dem der Consul-Client ausgeführt wird“,
Format: Zeichenfolge,
Standard: „consul-client“,
Umgebung: "CONSUL_HOST",
},
consulPort: {
dokument: „Der Port für den Consul-Client“,
Format: „Port“,
Standard: 8500,
Umgebung: "CONSUL_PORT",
},
Dies konfiguriert den Namen, unter dem ein neuer Dienst registriert wird, und definiert den Hostnamen und den Port für den Consul-Client. Im nächsten Schritt ändern Sie das Bereitstellungsskript für den Messenger -Dienst, um diese neuen Consul-Verbindungs- und Dienstregistrierungsinformationen einzuschließen.
Öffnen Sie infrastructure/messenger-deploy.sh und ersetzen Sie den Inhalt durch Folgendes, um die Consul-Verbindungs- und Dienstregistrierungsinformationen, die Sie im vorherigen Schritt festgelegt haben, in die Messenger- Dienstkonfiguration aufzunehmen:
#!/bin/bashset -e # Diese Konfiguration erfordert ein neues Commit zum Ändern von NODE_ENV=Produktion PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger" # Consul-Host und -Port sind in jedem Host enthalten, da wir # Consul nicht abfragen können, bis wir sie kennen CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Postgres-Datenbankkonfiguration durch Abrufen von Informationen aus # dem System POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-application-user?raw=true") PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true") PGHOST=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-host?raw=true") # HINWEIS: Tun Sie dies niemals bei einem Einsatz in der realen Welt. Speichern Sie Passwörter # nur in einem verschlüsselten Geheimnisspeicher.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true") # RabbitMQ-Konfiguration durch Abrufen aus dem System AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true") AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true") docker run \ --rm \ -d \ -e NODE_ENV="${NODE_ENV} " \ -e PORT="${PORT} " \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ -e PGUSER="${POSTGRES_USER} " \ -e PGPORT="${PGPORT} " \ -e PGHOST="${PGHOST} " \ -e PGPASSWORD="${PGPASSWORD} " \ -e AMQPPORT="${AMQPPORT} " \ -e AMQPHOST="${AMQPHOST} " \ -e CONSUL_HOST="${CONSUL_HOST} " \ -e CONSUL_PORT="${CONSUL_PORT} " \ -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME} " \ --network mm_2023 \ messenger
Die wichtigsten Dinge, die zu beachten sind, sind:
CONSUL_SERVICE_NAME
teilt der Messenger -Dienstinstanz mit, welchen Namen sie bei der Registrierung bei Consul verwenden soll.CONSUL_HOST
und CONSUL_PORT
sind für den Consul-Client bestimmt, der an dem Speicherort ausgeführt wird, an dem das Bereitstellungsskript ausgeführt wird.Notiz: In einer realen Bereitstellung ist dies ein Beispiel für eine Konfiguration, die zwischen den Teams vereinbart werden muss – das für Consul verantwortliche Team muss die Umgebungsvariablen CONSUL_HOST
und CONSUL_PORT
in allen Umgebungen bereitstellen, da ein Dienst Consul ohne diese Verbindungsinformationen nicht abfragen kann.
Wechseln Sie im App-Terminal in das App -Verzeichnis, stoppen Sie alle laufenden Instanzen des Messenger -Dienstes und erstellen Sie das Docker-Image neu, um den neuen Dienstregistrierungscode einzubinden:
cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
docker build -t messenger .
Navigieren Sie in einem Browser zu http://localhost:8500, um die Consul-Benutzeroberfläche in Aktion zu sehen (obwohl noch nichts Interessantes passiert).
Führen Sie im Stammverzeichnis des Messenger- Repositorys das Bereitstellungsskript aus, um eine Instanz des Messenger -Dienstes zu starten:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
Klicken Sie in der Consul-Benutzeroberfläche im Browser in der Kopfleiste auf „Dienste“ , um zu überprüfen, ob ein einzelner Messenger- Dienst ausgeführt wird.
Führen Sie das Bereitstellungsskript noch einige Male aus, um weitere Instanzen des Messenger -Dienstes zu starten. Überprüfen Sie in der Consul-Benutzeroberfläche, ob sie ausgeführt werden.
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
Der nächste Schritt besteht darin, NGINX Open Source als Reverse-Proxy und Load Balancer hinzuzufügen, um eingehenden Datenverkehr an alle laufenden Messenger- Instanzen weiterzuleiten.
Wechseln Sie im App-Terminal zum Stammverzeichnis des Messenger -Repos und erstellen Sie ein Verzeichnis namens „Load-Balancer“ und drei Dateien:
mkdir load-balancercd load-balancer
berühren Sie nginx.ctmpl
berühren Sie consul-template-config.hcl
berühren Sie Dockerfile
Die Docker-Datei definiert den Container, in dem die NGINX- und Consul-Vorlagen ausgeführt werden. Die Consul-Vorlage verwendet die anderen beiden Dateien, um die NGINX-Upstreams dynamisch zu aktualisieren, wenn sich der Messenger- Dienst in seinem Dienstregister ändert (Dienstinstanzen starten oder fallen).
Öffnen Sie die in Schritt 1 erstellte Datei nginx.ctmpl und fügen Sie den folgenden NGINX-Konfigurationsausschnitt hinzu, den die Consul-Vorlage verwendet, um die NGINX-Upstream-Gruppe dynamisch zu aktualisieren:
Upstream Messenger-Dienst { {{- Bereich Dienst "Messenger" }}
Server {{ .Adresse }}:{{ .Port }};
{{- Ende }}
}
Server {
abhören 8085;
Servername lokaler Host;
Standort / {
Proxy-Passwort http://Messenger-Dienst;
Add-Header Upstream-Host $upstream_addr;
}
}
Dieses Snippet fügt die IP-Adresse und Portnummer jeder bei Consul registrierten Messenger- Dienstinstanz zur NGINX-Upstream-Gruppe messenger_service hinzu. NGINX leitet eingehende Anfragen per Proxy an den dynamisch definierten Satz von Upstream-Dienstinstanzen weiter.
Öffnen Sie die in Schritt 1 erstellte Datei consul-template-config.hcl und fügen Sie die folgende Konfiguration hinzu:
consul { Adresse = "consul-client:8500"
Wiederholung {
aktiviert = true
Versuche = 12
Backoff = "250 ms"
}
}
Vorlage {
Quelle = "/usr/templates/nginx.ctmpl"
Ziel = "/etc/nginx/conf.d/default.conf"
Berechtigungen = 0600
Befehl = "wenn [ -e /var/run/nginx.pid ]; dann nginx -s neu laden; sonst nginx; fi"
}
Diese Konfiguration für die Consul-Vorlage weist sie an, die Quellvorlage
(den im vorherigen Schritt erstellten NGINX-Konfigurationsausschnitt) erneut zu rendern, sie am angegebenen Ziel
zu platzieren und schließlich den angegebenen Befehl
auszuführen (der NGINX anweist, seine Konfiguration neu zu laden).
In der Praxis bedeutet dies, dass jedes Mal, wenn eine Dienstinstanz in Consul registriert, aktualisiert oder abgemeldet wird, eine neue Datei default.conf erstellt wird. Anschließend lädt NGINX seine Konfiguration ohne Ausfallzeiten neu und stellt so sicher, dass NGINX über einen aktuellen und fehlerfreien Satz von Servern ( Messenger -Dienstinstanzen) verfügt, an die es Datenverkehr senden kann.
Öffnen Sie die in Schritt 1 erstellte Dockerfile -Datei und fügen Sie den folgenden Inhalt hinzu, der den NGINX-Dienst erstellt. (Für dieses Tutorial ist es nicht notwendig, das Docker-File zu verstehen, der Code ist jedoch zu Ihrer Information inline dokumentiert.)
VON nginx:1.23.1 ARG CONSUL_TEMPLATE_VERSION=0.30.0 # Legen Sie eine Umgebungsvariable für den Speicherort des Consul - Clusters fest. Standardmäßig wird versucht, in consul-client:8500 aufzulösen.
# Dies ist das Verhalten, wenn Consul als Container im
# demselben Host und mit diesem NGINX-Container verknüpft (mit dem Alias
# Konsul, natürlich). Diese Umgebungsvariable kann aber auch beim Starten des Containers überschrieben werden, wenn wir eine Auflösung zu einer anderen Adresse wünschen.
ENV CONSUL_URL consul-client:8500 # Laden Sie die angegebene Version der Consul-Vorlage herunter ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION} /consul-vorlage_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip /tmp RUN apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ && unzip /tmp/consul-template_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip -d /usr/local/bin \ && rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip KOPIEREN consul-template-config.hcl ./consul-template-config.hcl KOPIEREN nginx.ctmpl /usr/templates/nginx.ctmpl EXPOSE 8085 STOPSIGNAL SIGQUIT CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]
Erstellen Sie ein Docker-Image:
Docker-Build -t Messenger-lb.
Wechseln Sie zum Stammverzeichnis des Messenger- Verzeichnisses und erstellen Sie eine Datei mit dem Namen messenger-load-balancer-deploy.sh als Bereitstellungsdatei für den NGINX-Dienst (genau wie für die übrigen Dienste, die Sie im Laufe des Tutorials bereitgestellt haben). Abhängig von Ihrer Umgebung müssen Sie dem chmod
-Befehl möglicherweise das Präfix sudo
voranstellen:
cd ..
touch Infrastruktur/messenger-load-balancer-deploy.sh
chmod +x Infrastruktur/messenger-load-balancer-deploy.sh
Öffnen Sie messenger-load-balancer-deploy.sh und fügen Sie den folgenden Inhalt hinzu:
#!/bin/bashset -e # Consul-Host und -Port sind in jedem Host enthalten, da wir # Consul nicht abfragen können, bis wir sie kennen CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " docker run \ --rm \ -d \ --name messenger-lb \ -e CONSUL_URL="${CONSUL_HOST} :${CONSUL_PORT} " \ -p 8085:8085 \ --network mm_2023 \ messenger-lb
Nachdem Sie nun alles eingerichtet haben, stellen Sie den NGINX-Dienst bereit:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh
Prüfen Sie, ob Sie von extern auf den Messenger- Dienst zugreifen können:
curl -X GET http://localhost:8085/health OK
Es klappt! NGINX führt nun einen Lastenausgleich über alle erstellten Instanzen des Messenger- Dienstes durch. Sie können dies daran erkennen, dass im Header „X-Forwarded-For“
dieselben IP-Adressen des Messenger- Dienstes angezeigt werden wie in der Consul-Benutzeroberfläche in Schritt 8 des vorherigen Abschnitts.
Große Anwendungen nutzen häufig „Job Runner“ mit kleinen Arbeitsprozessen, die für einmalige Aufgaben wie die Änderung von Daten verwendet werden können (Beispiele sind Sidekiq und Celery ). Diese Tools erfordern häufig zusätzliche unterstützende Infrastruktur wie Redis oder RabbitMQ . In diesem Fall verwenden Sie den Messenger- Dienst selbst als „Job Runner“, um einmalige Aufgaben auszuführen. Dies ist sinnvoll, da das System bereits sehr klein ist, vollständig mit der Datenbank und anderen Teilen der Infrastruktur, von denen es abhängt, interagieren kann und vollständig getrennt von der Anwendung ausgeführt wird, die den Datenverkehr bereitstellt.
Dies bietet drei Vorteile:
In dieser Herausforderung erkunden Sie, wie ein Artefakt geändert werden kann, um eine neue Rolle zu erfüllen, indem Sie einige Datenbankkonfigurationswerte ändern und die Messenger- Datenbank migrieren, um die neuen Werte zu verwenden und ihre Leistung zu testen .
Für eine reale Produktionsbereitstellung könnten Sie zwei unterschiedliche Benutzer mit unterschiedlichen Berechtigungen erstellen: einen „Anwendungsbenutzer“ und einen „Migratorbenutzer“. Der Einfachheit halber verwenden Sie in diesem Beispiel den Standardbenutzer als Anwendungsbenutzer und erstellen einen Migrator-Benutzer mit Superuser-Berechtigungen. In einer realen Situation lohnt es sich, mehr Zeit darauf zu verwenden, zu entscheiden, welche spezifischen Mindestberechtigungen jeder Benutzer basierend auf seiner Rolle benötigt.
Erstellen Sie im App-Terminal einen neuen PostgreSQL-Benutzer mit Superuser-Berechtigungen:
echo "ERSTELLE BENUTZER messenger_migrator MIT SUPERUSER-PASSWORT 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres
Öffnen Sie das Datenbankbereitstellungsskript ( infrastructure/messenger-db-deploy.sh ) und ersetzen Sie dessen Inhalt, um die Anmeldeinformationen des neuen Benutzers hinzuzufügen.
Notiz: Nehmen wir uns die Zeit, es noch einmal zu wiederholen: Geben Sie bei einer Bereitstellung in der Praxis NIEMALS Geheimnisse wie Datenbankanmeldeinformationen in einem Bereitstellungsskript oder an einem anderen Ort als einem Tool zur Geheimnisverwaltung ein. Einzelheiten finden Sie in Einheit 2: Microservices Secrets Management 101 von Microservices, März 2023.
#!/bin/bash set -e PORT=5432 POSTGRES_USER=postgres # HINWEIS: Tun Sie dies niemals bei einem Einsatz in der realen Welt. Speichern Sie Passwörter # nur in einem verschlüsselten Geheimnisspeicher. # Da wir uns in diesem Tutorial auf andere Konzepte konzentrieren, # legen wir das Passwort hier der Einfachheit halber auf diese Weise fest.
POSTGRES_PASSWORD=postgres # Migrationsbenutzer POSTGRES_MIGRATOR_USER=messenger_migrator # HINWEIS: Wie oben beschrieben: Tun Sie dies niemals bei einer echten Bereitstellung.
POSTGRES_MIGRATOR_PASSWORD=Migrator-Passwort docker run \ --rm \ --name messenger-db-primary \ -d \ -v db-data:/var/lib/postgresql/data/pgdata \ -e POSTGRES_USER="${POSTGRES_USER} " \ -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " \ -e PGDATA=/var/lib/postgresql/data/pgdata \ --network mm_2023 \ postgres:15.1 echo "Schlüssel messenger-db-port registrieren\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \ -H "Content-Type: application/json" \ -d "${PORT} " echo "Register key messenger-db-host\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \ -H "Content-Type: application/json" \ -d 'messenger-db-primary' # Dies entspricht dem Flag "--name" oben, # was für unser Setup den Hostnamen bedeutet echo "Register key messenger-db-application-user\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \ -H "Content-Type: application/json" \ -d "${POSTGRES_USER} " curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \ -H "Inhaltstyp: Application/json" \ -d "${POSTGRES_PASSWORD} " echo "Schlüssel messenger-db-application-user registrieren\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-user \ -H "Content-Type: application/json" \ -d "${POSTGRES_MIGRATOR_USER} " curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this \ -H "Inhaltstyp: Application/json" \ -d "${POSTGRES_MIGRATOR_PASSWORD} " printf "\nRegistrierung der Postgres-Details bei Consul abgeschlossen\n"
Diese Änderung fügt den Migrator-Benutzer lediglich zum Benutzersatz hinzu, der in Consul festgelegt wird, nachdem die Datenbank bereitgestellt wurde.
Erstellen Sie im Infrastrukturverzeichnis eine neue Datei mit dem Namen messenger-db-migrator-deploy.sh (auch hier müssen Sie dem chmod
-Befehl möglicherweise das Präfix sudo
voranstellen):
touch-Infrastruktur/messenger-db-migrator-deploy.shchmod +x Infrastruktur/messenger-db-migrator-deploy.sh
Öffnen Sie messenger-db-migrator-deploy.sh und fügen Sie Folgendes hinzu:
#!/bin/bashset -e # Diese Konfiguration erfordert ein neues Commit zum Ändern von NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger-migrator" # Consul-Host und -Port sind in jedem Host enthalten, da wir # Consul nicht abfragen können, bis wir sie kennen CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Holen Sie sich den Migrator-Benutzernamen und das Passwort POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-user?raw=true") PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true") PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true) # HINWEIS: Tun Sie dies niemals bei einem Einsatz in der realen Welt. Speichern Sie Passwörter # nur in einem verschlüsselten Geheimnisspeicher.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true") # RabbitMQ-Konfiguration durch Abrufen vom System AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true") AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true") docker run \--rm \ -d \ --name messenger-migrator \ -e NODE_ENV="${NODE_ENV} " \ -e PORT="${PORT} " \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ -e PGUSER="${POSTGRES_USER} " \ -e PGPORT="${PGPORT} " \ -e PGHOST="${PGHOST} " \ -e PGPASSWORD="${PGPASSWORD} " \ -e AMQPPORT="${AMQPPORT} " \ -e AMQPHOST="${AMQPHOST} " \ -e CONSUL_HOST="${CONSUL_HOST} " \ -e CONSUL_PORT="${CONSUL_PORT} " \ -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME} " \ --network mm_2023 \ messenger
Dieses Skript ist in seiner endgültigen Form dem Skript infrastructure/messenger-deploy.sh sehr ähnlich, das Sie in Schritt 3 von „Consul einrichten“ erstellt haben. Der Hauptunterschied besteht darin, dass CONSUL_SERVICE_NAME
messenger-migrator
statt messenger
ist und PGUSER
dem Superuser „migrator“ entspricht, den Sie oben in Schritt 1 erstellt haben.
Es ist wichtig, dass der CONSUL_SERVICE_NAME
messenger-migrator
ist. Wenn es auf Messenger
eingestellt wäre, würde NGINX diesen Dienst automatisch in die Rotation aufnehmen, um API-Aufrufe zu empfangen, und er ist nicht dafür gedacht, irgendeinen Datenverkehr abzuwickeln.
Das Skript stellt eine kurzlebige Instanz in der Rolle des Migrators bereit. Dadurch wird verhindert, dass etwaige Migrationsprobleme die Datenverkehrsabwicklung durch die wichtigsten Messenger- Dienstinstanzen beeinträchtigen.
Stellen Sie die PostgreSQL-Datenbank erneut bereit. Da Sie in diesem Tutorial Bash
-Skripte verwenden, müssen Sie den Datenbankdienst stoppen und neu starten. In einer Produktionsanwendung führen Sie stattdessen normalerweise einfach ein Infrastructure-as-Code -Skript aus, um nur die Elemente hinzuzufügen, die sich geändert haben.
Docker stoppt Messenger-DB-PrimaryCONSUL_HOST = Consul-Client CONSUL_PORT = 8500 ./Infrastructure/Messenger-DB-Deploy.sh
Stellen Sie den PostgreSQL-Datenbankmigrationsdienst bereit:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh
Überprüfen Sie, ob die Instanz wie erwartet ausgeführt wird:
docker ps --format "{{.Names}}" ... Messenger-Migrator
Sie können in der Consul-Benutzeroberfläche auch überprüfen, ob sich der Datenbank-Migrator-Dienst ordnungsgemäß bei Consul als Messenger-Migrator registriert hat (er registriert sich auch hier nicht unter dem Messenger -Namen, da er keinen Datenverkehr verarbeitet):
Nun zum letzten Schritt: Führen Sie die Datenbankmigrationsskripte aus! Diese Skripte ähneln keinen echten Datenbankmigrationsskripten, sie verwenden jedoch den Messenger-Migrator- Dienst, um datenbankspezifische Skripte auszuführen. Sobald die Datenbank migriert wurde, stoppen Sie den Messenger-Migrator -Dienst:
docker exec -i -e PGDATABASE=postgres -e CREATE_DB_NAME=messenger messenger-migrator Knotenskripte/create-db.mjsdocker exec -i messenger-migrator Knotenskripte/create-schema.mjs
docker exec -i messenger-migrator Knotenskripte/create-seed-data.mjs
docker stop messenger-migrator
Nachdem Sie die Messenger -Datenbank in ihr endgültiges Format migriert haben, können Sie den Messenger- Dienst endlich in Aktion beobachten! Dazu führen Sie einige grundlegende Curl
-Abfragen für den NGINX-Dienst aus (Sie haben NGINX unter „NGINX einrichten“ als Einstiegspunkt des Systems konfiguriert).
Einige der folgenden Befehle verwenden die jq-
Bibliothek , um die JSON-Ausgabe zu formatieren. Sie können es nach Bedarf installieren oder bei Bedarf aus der Befehlszeile weglassen.
Erstellen Sie eine Konversation:
curl -d '{"Teilnehmer-IDs": [1, 2]}' -H "Inhaltstyp: application/json" -X POST 'http://localhost:8085/conversations' { "Konversation": { "ID": "1", "eingefügt bei": " JJJJ - MM - TT T06:41:59.000Z" } }
Senden Sie von einem Nutzer mit der ID 1 eine Nachricht an die Konversation:
curl -d '{"Inhalt": "Dies ist die erste Nachricht"}' -H "Benutzer-ID: 1" -H "Inhaltstyp: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq { "Nachricht": { "ID": "1", "Inhalt": "Dies ist die erste Nachricht", "index": 1, "Benutzer-ID": 1, "Benutzername": „James Blanderphone“, „conversation_id“: 1, "eingefügt bei": " JJJJ - MM - TT T06:42:15.000Z" } }
Antworten Sie mit einer Nachricht von einem anderen Nutzer (mit ID 2):
curl -d '{"Inhalt": "Dies ist die zweite Nachricht"}' -H "Benutzer-ID: 2" -H "Inhaltstyp: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq { "Nachricht": { "ID": "2", "Inhalt": "Dies ist die zweite Nachricht", "index": 2, "Benutzer-ID": 2, "Benutzername": "Normalavian Ropetoter", "Konversations-ID": 1, "eingefügt bei": " JJJJ - MM - TT T06:42:25.000Z" } }
Nachrichten abrufen:
curl -X GET 'http://localhost:8085/conversations/1/messages' | jq { "Nachrichten": [ { "ID": "1", "Inhalt": "Dies ist die erste Nachricht", "user_id": "1", "Kanal-ID": "1", "Index": "1", "eingefügt bei": " JJJJ - MM - TT T06:42:15.000Z", "Benutzername": „James Blanderphone“ }, { „id“: "2", "Inhalt": "Dies ist die zweite Nachricht", "user_id": "2", "Kanal-ID": "1", "Index": "2", "eingefügt bei": " JJJJ - MM - TT T06:42:25.000Z", "Benutzername": "Normalavianischer Ropetoter" } ] }
Sie haben im Laufe dieses Tutorials eine beträchtliche Anzahl von Containern und Bildern erstellt! Verwenden Sie die folgenden Befehle, um alle Docker-Container und -Images zu entfernen, die Sie nicht behalten möchten.
So entfernen Sie alle laufenden Docker-Container:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))docker rm $(docker stop messenger-db-primary)
docker rm $(docker stop messenger-lb)
So entfernen Sie die Plattformdienste :
# Aus dem Plattform-Repository Docker Compose Down
So entfernen Sie alle im Tutorial verwendeten Docker-Images:
Docker RMI MessengerDocker RMI Messenger-lb
Docker RMI Postgres:15.1
Docker RMI Hashicorp/Consul:1.14.4
Docker RMI Rabbitmq:3.11.4-Management-Alpine
Sie denken vielleicht: „Das scheint eine Menge Arbeit zu sein, um etwas Einfaches einzurichten“ – und Sie haben Recht! Der Wechsel zu einer auf Microservices ausgerichteten Architektur erfordert sorgfältige Vorgehensweisen bei der Strukturierung und Konfiguration der Services. Trotz aller Komplexität haben Sie einige solide Fortschritte erzielt:
Um Ihre Microservices-Ausbildung fortzusetzen, sehen Sie sich Microservices März 2023 an. Einheit 2, „Microservices Secrets Management 101“ , bietet einen umfassenden, aber benutzerfreundlichen Überblick über die Geheimnisverwaltung in Microservices-Umgebungen.
„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."