Wir bei True Engineering haben den Prozess der kontinuierlichen Bereitstellung von Updates auf die Server des Kunden eingerichtet und möchten diese Erfahrung teilen.
Zu Beginn haben wir ein Online-System für den Kunden entwickelt und in seinem eigenen Kubernetes-Cluster bereitgestellt. Nun ist unsere hoch performante Lösung auf die Plattform des Kunden umgezogen, für die wir den vollständig automatisierten Prozess der Continuous Deployment eingerichtet haben. Dadurch haben wir die time-to-market – die Lieferung von Änderungen in die Produktionsumgebung – beschleunigt.
In diesem Artikel werden wir alle Schritte des Prozesses der Continuous Deployment (CD) oder der Bereitstellung von Updates auf der Plattform des Kunden beschreiben:
- wie dieser Prozess beginnt,
- Synchronisation mit dem Git-Repository des Kunden,
- Bau des Backends und Frontends,
- automatische Bereitstellung der Anwendung in der Testumgebung,
- automatische Bereitstellung in Prod.
Im Prozess werden wir Details zur Konfiguration teilen.

1. Start CD
Das Continuous Deployment beginnt damit, dass der Entwickler Änderungen in den Release-Branch unseres Git-Repositories hochlädt.
Unsere Anwendung basiert auf einer Microservices-Architektur, und alle ihre Komponenten werden in einem Repository gespeichert. Dadurch werden alle Microservices gesammelt und installiert, selbst wenn sich nur einer von ihnen geändert hat.
Wir haben die Arbeit über ein einziges Repository aus mehreren Gründen organisiert:
- Entwicklungsfreundlichkeit – die Anwendung wird aktiv weiterentwickelt, daher kann sofort am gesamten Code gearbeitet werden.
- Ein einheitlicher CI/CD-Pipeline, die gewährleistet, dass die Anwendung als Einheit alle Tests durchläuft und in die Produktionsumgebung des Kunden geliefert wird.
- Verwirrung bezüglich der Versionen wird ausgeschlossen – wir müssen keinen Versionsplan der Microservices aufbewahren und für jeden Microservice seine eigene Konfiguration in Helm-Skripten beschreiben.
2. Synchronisation mit dem Git-Repository des Kunden
Die vorgenommenen Änderungen werden automatisch mit dem Git-Repository des Kunden synchronisiert. Dort ist der Bau der Anwendung eingerichtet, der nach dem Update des Branches gestartet wird, sowie das Deployment in Prod. Beide Prozesse finden in ihrem Umfeld aus dem Git-Repository statt.
Wir können nicht direkt mit dem Repository des Kunden arbeiten, da wir eigene Umgebungen für die Entwicklung und das Testen benötigen. Zu diesem Zweck verwenden wir unser eigenes Git-Repository, das mit ihrem Git-Repository synchronisiert ist. Sobald der Entwickler Änderungen in den entsprechenden Branch unseres Repositories hochlädt, sendet GitLab diese Änderungen sofort an den Kunden.

Danach muss ein Build erstellt werden. Dieser besteht aus mehreren Phasen: dem Build des Backends und Frontends, dem Testen und der Bereitstellung in der Produktion.
3. Build des Backends und Frontends
Der Build des Backends und Frontends sind zwei parallele Aufgaben, die im GitLab Runner durchgeführt werden. Die Konfiguration des ursprünglichen Builds befindet sich im selben Repository.
.
GitLab Runner holt den Code aus dem benötigten Repository, erstellt mit dem Build-Befehl die Java-Anwendung und sendet sie in das Docker-Registry. Hier bauen wir Backend und Frontend, erzeugen Docker-Images, die wir im Repository des Kunden ablegen. Zur Verwaltung der Docker-Images verwenden wir .
Wir synchronisieren die Versionen unserer Images mit der Versionsnummer des Releases, das in Docker bereitgestellt wird. Für einen reibungslosen Ablauf haben wir einige Einstellungen vorgenommen:
1. Zwischen der Testumgebung und der Produktionsumgebung werden die Container nicht neu erstellt. Wir haben Parameterisierung vorgenommen, damit derselbe Container ohne Neuaufbau mit allen Einstellungen, Umgebungsvariablen und Services sowohl in der Testumgebung als auch in der Produktion arbeiten kann.
2. Um die Anwendung über Helm zu aktualisieren, muss die Versionsnummer angegeben werden. Unser Build des Backends, Frontends und die Aktualisierung der Anwendung sind drei verschiedene Aufgaben, daher ist es wichtig, überall dieselbe Version der Anwendung zu verwenden. Für diese Aufgabe verwenden wir Daten aus der Git-Historie, da sich die Konfiguration des K8S-Clusters und der Anwendung im selben Git-Repository befindet.
Die Versionsnummer der Anwendung erhalten wir aus den Ergebnissen der Ausführung des Befehls
git describe --tags --abbrev=7.
4. Automatisierte Bereitstellung aller Änderungen in der Testumgebung (UAT)
Der nächste Schritt in diesem Build-Skript ist die automatische Aktualisierung des K8S-Clusters. Dies erfolgt unter der Voraussetzung, dass die gesamte Anwendung gebaut wurde und alle Artefakte im Docker Registry veröffentlicht sind. Danach wird die Aktualisierung der Testumgebung gestartet.
Die Aktualisierung des Clusters wird mit Hilfe von . Wenn hierbei etwas schiefgeht, rollt Helm automatisch und selbstständig alle Änderungen zurück. Seine Arbeit muss nicht überwacht werden.
Wir liefern zusammen mit dem Build die Konfiguration des K8S-Clusters. Daher wird im nächsten Schritt diese aktualisiert: configMaps, Deployments, Services, Secrets und alle anderen K8S-Konfigurationen, die wir geändert haben.
Danach startet Helm die RollOut-Aktualisierung der Anwendung in der Testumgebung. Bevor die Anwendung in der Produktion bereitgestellt wird. Dies geschieht, damit die Benutzer die von uns bereitgestellten Geschäftsfunktionen manuell überprüfen können, die wir in die Testumgebung hochgeladen haben.
5. Automatische Bereitstellung aller Änderungen in der Produktion
Um das Update in die Produktionsumgebung bereitzustellen, müssen Sie nur einen Knopf in GitLab drücken - und die Container werden sofort in die Produktionsumgebung geliefert.
Dasselbe Anwendung kann ohne Neuaufbau in verschiedenen Umgebungen - Test und Produktion - arbeiten. Wir verwenden dieselben Artefakte, ohne etwas in der Anwendung zu ändern, während die Parameter von außen vorgegeben werden.
Die flexible Parametrisierung der Anwendungsparameter hängt von der Umgebung ab, in der die Anwendung ausgeführt wird. Wir haben alle Umgebungsparameter nach außen verlagert: alles wird über die K8S-Konfiguration und die Helm-Parameter parametriert. Wenn Helm den Build in der Testumgebung bereitstellt, werden Testparameter angewendet, und in der Produktionsumgebung - Produktionsparameter.
Die schwierigste Aufgabe war es, alle verwendeten Dienste und Variablen zu parametrieren, die von der Umgebung abhängen, und sie in Umgebungsvariablen und die Beschreibung-Konfiguration der Umgebungsparameter für Helm zu überführen.
In den Anwendungsparametern werden Umgebungsvariablen verwendet. Ihre Werte werden in den Containern mit Hilfe von K8S configmap angegeben, das mit Go-Vorlagen templatiert wird. Zum Beispiel kann die Festlegung einer Umgebungsvariablen für den Domainnamen so erfolgen:
APP_EXTERNAL_DOMAIN: {{ (pluck .Values.global.env .Values.app.properties.app_external_domain | first) }}
.Values.global.env – in dieser Variablen wird der Name der Umgebung (prod, stage, UAT) gespeichert.
.Values.app.properties.app_external_domain – in dieser Variablen geben wir in der Datei .Values.yaml die benötigte Domain an.
Beim Update der Anwendung erstellt Helm die configmap.yaml-Dateien aus Vorlagen und füllt den Wert APP_EXTERNAL_DOMAIN mit dem erforderlichen Wert abhängig von der Umgebung, in der das Update der Anwendung gestartet wird. Diese Variable wird bereits im Container gesetzt. Der Zugriff darauf erfolgt über die Anwendung, daher hat jede Umgebung der Anwendung einen unterschiedlichen Wert für diese Variable.
In Bezug auf die kürzliche Einführung in Spring Cloud gibt es nun Unterstützung für K8S, einschließlich der Arbeit mit configMaps: . Da sich das Projekt aktiv entwickelt und sich grundlegend verändert, können wir es derzeit nicht in der Produktion verwenden. Wir überwachen jedoch seinen Zustand aktiv und verwenden es in DEV-Konfigurationen. Sobald es stabil ist, werden wir auf die Verwendung von Umgebungsvariablen umschalten.
Insgesamt
Also, Continuous Deployment ist eingerichtet und funktioniert. Alle Updates erfolgen mit einem einzigen Knopfdruck. Die Bereitstellung von Änderungen in die Produktionsumgebung ist automatisch. Und, was wichtig ist, die Updates unterbrechen nicht den Betrieb des Systems.

Pläne für die Zukunft: automatische Migration der Datenbank
Wir haben über ein Upgrade der Datenbank nachgedacht und die Möglichkeit, diese Änderungen zurückzusetzen. Immerhin arbeiten gleichzeitig zwei verschiedene Versionen der Anwendung: die alte läuft, während die neue hochgefahren wird. Die alte werden wir nur abschalten, wenn wir sicher sind, dass die neue Version funktioniert. Die Migration der Datenbank sollte es ermöglichen, mit beiden Versionen der Anwendung zu arbeiten.
Deshalb können wir nicht einfach den Namen einer Spalte oder andere Daten ändern. Aber wir können eine neue Spalte erstellen, die Daten aus der alten Spalte kopieren und Trigger schreiben, die bei Datenaktualisierungen gleichzeitig die Daten in die andere Spalte kopieren und aktualisieren. Nach dem erfolgreichen Deployment der neuen Version der Anwendung, nach einer sogenannten Post Launch Support-Phase, können wir die alte Spalte und den nicht mehr benötigten Trigger löschen.
Wenn die neue Version der Anwendung nicht korrekt funktioniert, können wir auf die vorherige Version zurücksetzen, einschließlich der vorherigen Version der Datenbank. Kurz gesagt, unsere Änderungen ermöglichen es, gleichzeitig mit mehreren Versionen der Anwendung zu arbeiten.
Wir planen, die Automatisierung der Datenbankmigration über einen K8S-Job einzurichten und in den CD-Prozess einzubinden. Und wir werden diese Erfahrung definitiv auf Habr teilen.
Quelle: habr.com
