dark

Bewährte Praktiken für CI/CD Pipelines

Diese Praktiken sollen eine Vorstellung davon vermitteln, wie einige Probleme in modernen CI/CD-Pipelines gelöst und was vermieden werden sollte. Verwenden Sie diese Muster als Richtlinie beim Implementieren Ihrer eigenen Pipelines.


Best Practice 1 – Stellen Sie alles unter Quellcodeverwaltung

Alles, was am Anwendungslebenszyklus teilnimmt, sollte in die Quellcodeverwaltung eingecheckt werden. Dies beinhaltet, ist aber darauf nicht beschränkt:

  1. Quellcode
  2. Build Skripte
  3. Pipeline-Definition
  4. Konfigurationswerte
  5. Tests und Testdaten
  6. Datenbankschemata
  7. Datenbankaktualisierungensskripte
  8. Definition der Infrastruktur Skripte
  9. Aufräume/ Installation/ Spülen von Skripten
  10. Zugehörige Dokumentation


Best Practices 2 – Erstellen Sie ein einzelnes Paket/ Binär/ Container für alle Umgebungen

Hinsichtlich der Konfiguration gibt es zwei Ansätze:

  1. Das binäre Artefakt/der Container hat alle darin eingebetteten Konfigurationen und ändert die aktive Konfiguration entsprechend der laufenden Umgebung (einfach zu starten, aber nicht sehr flexibel. Wir empfehlen diesen Ansatz nicht).
  2. Der Container hat überhaupt keine Konfiguration. Es ruft die benötigte Konfiguration während der Laufzeit bei Bedarf ab, indem es einen Erkennungsmechanismus wie eine Schlüssel/Wert-Datenbank, ein Dateisystem-Volume, einen Diensterkennungsmechanismus usw. verwendet (der empfohlene Ansatz).

Best Practice 3 – Artefakte, keine Git-Commits, sollten innerhalb einer Pipeline reisen


Best Practice 4 – Verwenden Sie kurzlebige Branches für jedes Feature


Best Practice 5 – Ein grundlegender Build sollte einen einzigen Schritt umfassen


Best Practice 6 – Basic Build sind schnell (5-10 Minuten)

Ein schneller Build ist ein großer Vorteil sowohl für Entwickler als auch für Betreiber/ Systemadministratoren.

  1. Bibliotheksabhängigkeiten sollten aus einem internen Proxy-Repository statt aus dem Internet abgerufen werden
  2. Vermeiden Sie die Verwendung von Codegeneratoren, sofern nicht anders erforderlich
  3. Teilen Sie Ihre Unit-(schnell) und Integrationstests (langsam) auf und verwenden Sie Unit-Tests nur für den Basis-Build
  4. Optimieren Sie Ihre Container-Images, um das Docker-Layer-Caching voll auszuschöpfen

Tipp: Schnellere Builds zu erhalten, ist auch einer der Gründe, die Sie untersuchen sollten, wenn Sie zu Microservices wechseln.


Best Practice 7 – Speichern/cachen Sie Ihre Abhängigkeiten


Best Practice 8 – Automatisieren Sie alle Ihre Tests


Best Practice 9 – Machen Sie Ihren Test schnell

Eine Folge des vorherigen Abschnitts ist auch die schnelle Ausführung von Tests. Wenn Testsuiten in Delivery-Pipelines integriert werden sollen, sollten sie wirklich schnell sein. Idealerweise sollte die Testzeit nicht größer sein als die Verpackungs-/Kompilierungszeit, was bedeutet, dass die Tests nach fünf Minuten und nicht mehr als 15 Minuten abgeschlossen sein sollten.

Die schnelle Testausführung gibt Entwicklern die Gewissheit, dass die Funktion, die sie gerade übernommen haben, keine Regressionen aufweist und sicher in die nächste Workflow-Phase befördert werden kann. Eine Laufzeit von zwei Stunden ist für Entwickler katastrophal, da sie unmöglich so lange warten können, nachdem sie ein Feature festgeschrieben haben.

Wenn der Testzeitraum lang ist, gehen Entwickler einfach zu ihrer nächsten Aufgabe über und ändern ihren Gedankenkontext. Sobald die Testergebnisse vorliegen, ist es viel schwieriger, Probleme mit einer Funktion zu beheben, an der Sie nicht aktiv arbeiten.

Leider stammt die meiste Zeit des Wartens auf Tests, Schritte von ineffektiven Testpraktiken und fehlenden Optimierungen. Der übliche Faktor eines langsamen Tests ist Code, der auf das Eintreten eines Ereignisses „schläft“ oder „wartet“, wodurch der Test länger läuft, als er laufen sollte. Alle diese Schlafanweisungen sollten entfernt werden und der Test sollte einer ereignisgesteuerten Architektur folgen (d. h. auf Ereignisse reagieren, anstatt darauf zu warten, dass etwas passiert).

Die Erstellung von Testdaten ist ein weiterer Bereich, in dem Tests die meisten ihrer Daten ausgeben. Der Testdatenerstellungscode sollte zentralisiert und wiederverwendet werden. Wenn ein Test eine lange Einrichtungsphase hat, testet er vielleicht zu viele Dinge oder muss in nicht verwandten Diensten gelockert werden.
Zusammenfassend lässt sich sagen, dass Testsuiten schnell sein sollten (5-10 Minuten) und große Tests, die Stunden benötigen, umgestaltet und neu gestaltet werden sollten.


Best Practice 10 – Jeder Test bereinigt seine Nebenwirkungen automatisch

Im Allgemeinen können Sie Ihre Unit-Tests in zwei weitere Kategorien einteilen (abgesehen von Unit/Integration oder langsam und schnell), und dies hat mit ihren Nebenwirkungen zu tun:

  • Tests ohne Nebenwirkungen. Sie lesen nur Informationen aus externen Quellen, verändern nie etwas und können ohne Komplikationen beliebig oft (oder sogar parallel) ausgeführt werden.
  • Tests mit Nebenwirkungen. Dies sind die Tests, die Daten in Ihre Datenbank schreiben, Daten an externe Systeme übergeben, Ausgabevorgänge für Ihre Abhängigkeiten ausführen und so weiter.

Die erste Kategorie (Nur-Lese-Tests) ist einfach zu handhaben, da sie keiner besonderen Wartung bedürfen. Die Wartung der zweiten Kategorie (Lese-/Schreibtests) ist jedoch komplexer, da Sie sicherstellen müssen, dass Sie ihre Aktionen bereinigen, sobald die Tests abgeschlossen sind. Dazu gibt es zwei Ansätze:

  • Lassen Sie alle Tests laufen und bereinigen Sie dann die Aktionen aller am Ende des Testanzugs
  • Lassen Sie jeden Test nach seiner Ausführung selbst bereinigen (empfohlener Ansatz).


Best Practice 11 – Verwenden Sie mehrere Testsuiten

Das Testen geschieht nicht nur in einem einzigen Schritt innerhalb einer CI/CD-Pipeline. Testen ist ein kontinuierlicher Prozess, der alle Phasen einer Pipeline berührt.
Dies bedeutet, dass in jeder gut konzipierten Anwendung mehrere Testtypen vorhanden sein sollten. Einige der häufigsten Beispiele sind:

  • Wirklich schnelle Unit-Tests, die sich mit großen Regressionen befassen und sehr schnell fertig sind
  • Längere Integrationstests, die nach komplexeren Szenarien suchen (z. B. Transaktionen oder Sicherheit)
  • Stress und Belastung testen
  • Vertragsprüfung für API-Änderungen von verwendeten externen Diensten
  • Rauchtests, die in der Produktion durchgeführt werden können, um eine Freigabe zu überprüfen
  • UI-Tests, die die Benutzererfahrung testen

Best Practice 12 – Erstellen Sie Testumgebungen nach Bedarf

Dies zwingt viele Organisationen dazu, eine Reihe von Testumgebungen (z. B. QA1, QA2, QA3) zu erstellen, damit mehrere Entwickler ihre Funktionen parallel testen können. Diese Technik ist immer noch nicht ideal, weil:

  • Maximal N Entwickler können ihr Feature (genauso viele Umgebungen) parallel testen.
  • Testumgebungen verbrauchen ständig Ressourcen (auch wenn sie nicht verwendet werden)
  • Der statische Charakter von Umgebungen erfordert, dass auch diese bereinigt und aktualisiert werden müssen. Dies bedeutet zusätzlichen Wartungsaufwand für das Team, das für Testumgebungen verantwortlich ist.


Best Practice 13 – Testsuiten gleichzeitig ausführen

Die Vorteile dynamischer Testumgebungen können nicht genug betont werden:

  • Jeder Entwickler kann isoliert testen, ohne Konflikte mit dem, was andere Entwickler tun.
  • Sie zahlen für die Ressourcen von Testumgebungen nur, während Sie sie nutzen
  • Da die Testumgebungen am Ende verworfen werden, muss nichts gewartet oder aufgeräumt werden.
    Dynamische Testumgebungen können für Teams glänzen, die einen unregelmäßigen Entwicklungsplan haben (z. B. wenn am Ende eines Sprints zu viele Features in Flight sind).


Best Practice 14 – Security Scanning ist Teil des Prozesses

Sicherheit ist ein fortlaufender Prozess. Eine Anwendung sollte gleichzeitig mit der Entwicklung auf Schwachstellen überprüft werden. Dies bedeutet, dass Sicherheitsscans Teil des Pre-Merge-Prozesses sein sollten (d. h. als eine der Prüfungen eines Pull-Requests). Das Lösen von Sicherheitsproblemen in einem fertigen Softwarepaket ist viel schwieriger als während der Entwicklung.
Sicherheitsscans sollten auch die entsprechende Tiefe haben. Sie müssen mindestens Folgendes überprüfen:

  1. Der Container oder die zugrunde liegende Laufzeit, in der die Anwendung ausgeführt wird.
  2. Der Rechenknoten und das Betriebssystem, das die Anwendung hostet.


Best Practice 15 – Qualitätsscannen/Codeüberprüfungen sind Teil des Prozesses

Ähnlich wie Sicherheitsscans sollten Codescans Teil des täglichen Entwicklerbetriebs
sein. Das beinhaltet:

  1. Statische Analyse des Codes für vom Unternehmen genehmigten Stil/Formatierung
  2. Statische Analyse des Codes auf Sicherheitsprobleme, versteckte Fehler
  3. Laufzeitanalyse des Codes auf Fehler und andere Probleme


Best Practice 16 – Datenbankaktualisierungen haben ihren eigenen Lebenszyklus

Datenbanken (und andere unterstützende Systeme wie Nachrichtenwarteschlangen, Caches, Diensterkennungslösungen usw.) sollten wie jedes andere Softwareprojekt behandelt werden.
Das heißt:

  • Ihre Konfiguration und ihr Inhalt sollten in der Versionskontrolle gespeichert werden
  • Alle zugehörigen Skripte, Wartungsaktionen und Upgrade-/Downgrade-Anweisungen sollten sich ebenfalls in der Versionskontrolle befinden
    • Konfigurationsänderungen sollten wie jede andere Softwareänderung genehmigt werden (Durchlaufen der automatisierten Analyse, Pull-Request-Überprüfung, Sicherheitsscans, Einheitentests usw.)
  • Dedizierte Pipelines sollten für das Installieren/Upgrade/Rollback jeder neuen Version der Datenbank verantwortlich sein


Best Practice 17 – Datenbankaktualisierungen sind automatisiert

Mehrere Organisationen haben hervorragende Pipelines für den Anwendungscode, schenken der Automatisierung von Datenbankaktualisierungen jedoch nur sehr wenig Aufmerksamkeit. Dem Umgang mit Datenbanken sollte die gleiche Bedeutung (wenn nicht sogar mehr) beigemessen werden wie der Anwendung selbst.
Das bedeutet, dass Sie Datenbanken ähnlich wie Anwendungscode automatisieren sollten:

  1. Speichern Sie Datenbank-Änderungssätze in der Quellcodeverwaltung.
  2. Erstellen Sie Pipelines, die Ihre Datenbank automatisch aktualisieren, wenn ein neuer Änderungssatz erstellt wird.
  3. Haben Sie dynamische temporäre Umgebungen für Datenbanken, in denen Änderungssätze überprüft werden, bevor sie hauptsächlich zusammengeführt werden.
  4. Führen Sie Codeüberprüfungen und andere Qualitätsprüfungen für Datenbank-Änderungssätze durch.
  5. Haben Sie eine Strategie für Rollbacks nach einem fehlgeschlagenen Datenbank-Upgrade.


Best Practice 18 – Führen Sie schrittweise Datenbank-Upgrades durch

Anwendungs-Rollbacks sind hinlänglich bekannt, und wir sind jetzt an einem Punkt angelangt, an dem wir über spezielle Tools verfügen, die Rollbacks nach einer fehlgeschlagenen Anwendungsbereitstellung durchführen. Und mit progressiven
Bereitstellungsmethoden wie Canaries und Blue/Green Deployments können wir die Ausfallzeiten noch weiter minimieren Progressive Bereitstellungstechniken funktionieren nicht bei Datenbanken (wegen des inhärenten Zustands), aber wir können die Datenbank-Upgrades planen und evolutionäre Datenbank-Designprinzipien übernehmen.

Wenn Sie beispielsweise eine Spalte umbenennen möchten, anstatt einfach einen Änderungssatz zu erstellen, der die Spalte umbenennt und ein einzelnes Datenbank-Upgrade durchführt, folgen Sie stattdessen einem Zeitplan schrittweiser Aktualisierungen wie unten:

  1. Datenbank-Änderungssatz, der nur eine neue Spalte mit dem neuen
    Namen hinzufügt (und vorhandene Daten aus der alten Spalte kopiert).
    Der Anwendungscode schreibt/liest immer noch aus der alten Spalte.
  2. Anwendungsupgrade, bei dem der Anwendungscode jetzt in beide Spalten schreibt, aber aus der neuen Spalte liest.
  3. Anwendungsupgrade, bei dem der Anwendungscode nur in die neue Spalte
    schreibt/liest.
  4. Datenbank-Upgrade, das die alte Spalte entfernt.


Best Practice 19 – Alle Bereitstellungen dürfen nur über die CD-Plattform erfolgen
(und niemals von Workstations)


Best Practice 20 – Verwenden Sie progressive Bereitstellungsmuster

Wir haben bereits in Best Practice 18 über Datenbankbereitstellungen gesprochen und darüber, wie jedes Datenbank-Upgrade vorwärts- und rückwärtskompatibel sein sollte. Dieses Muster geht Hand in Hand mit einem progressiven Abgabemuster auf der Anwendungsseite.
Herkömmliche Bereitstellungen folgen einem Alles-oder-nichts-Ansatz, bei dem alle Anwendungsinstanzen zur nächsten Version der Software übergehen . Dies ist ein sehr einfacher Bereitstellungsansatz, macht Rollbacks jedoch zu einem herausfordernden Prozess.

Sie sollten stattdessen ansehen:

  1. Blue/Green Deployments, bei denen ein ganzer Satz neuer Instanzen der neuen Version bereitgestellt wird, die alte Version aber für einfache Rollbacks erhalten bleibt.
  2. Canary-Releases, bei denen nur eine Teilmenge der Anwendungsinstanzen auf die neue Version umgestellt wird. Die meisten Benutzer werden weiterhin zur vorherigen Version weitergeleitet.


Best Practice 21 – Metriken und Protokolle können eine fehlerhafte Bereitstellung erkennen

Der richtige Ansatz ist die Übernahme von Anwendungs- (und Infrastruktur-) Metriken. Das beinhaltet:

  1. Detaillierte Protokolle für Anwendungsereignisse
  2. Metriken, die Schlüsselfunktionen der Anwendung zählen und überwachen
  3. Ablaufverfolgungsinformationen, die ein tiefes Verständnis dafür bieten können, was eine einzelne Anfrage bewirkt

Die Auswahl der zu überwachenden Ereignisse und der Platzierung von Protokollen ist ein komplexer Prozess. Bei großen Anwendungen ist es am besten, eine schrittweise Neudefinition der Schlüsselmetriken gemäß früheren Bereitstellungen zu befolgen. Der vorgeschlagene Arbeitsablauf ist der folgende:

  1. Platzieren Sie Protokolle und Metriken zu Ereignissen, von denen Sie vermuten, dass sie eine fehlgeschlagene Bereitstellung anzeigen.
  2. Führen Sie mehrere Bereitstellungen durch und prüfen Sie, ob Ihre Metriken die fehlgeschlagenen erkennen können.
  3. Wenn Sie eine fehlgeschlagene Bereitstellung sehen, die in Ihren Metriken nicht erkannt wurde, bedeutet dies, dass sie nicht ausreichen. Passen Sie Ihre Metriken entsprechend an, damit Sie das nächste Mal, wenn eine Bereitstellung auf die gleiche Weise fehlschlägt, tatsächlich im Voraus wissen.

Zu oft konzentrieren sich Entwicklungsteams auf „Eitelkeits“-Metriken, d. h. Metriken, die auf dem Papier gut aussehen, aber nichts über eine fehlgeschlagene Bereitstellung aussagen.


Best Practice 22 – Automatische Rollbacks sind vorhanden

Dies ist eine Fortsetzung des vorherigen Best Practice. Wenn Sie bereits über gute Metriken verfügen (die den Erfolg einer Bereitstellung überprüfen können), können Sie sie auf die nächste Stufe bringen, indem Sie automatisierte Rollbacks haben, die von ihnen abhängen.
Viele Organisationen haben großartige Metriken, verwenden sie aber nur manuell:

  1. Ein Entwickler sieht sich vor der Bereitstellung einige wichtige Metriken an
  2. Die Bereitstellung wird ausgelöst
  3. Der Entwickler sieht sich die Metriken ad hoc an, um zu sehen, was mit der Bereitstellung passiert ist

Obwohl diese Technik sehr beliebt ist, ist sie alles andere als effektiv. Je nach Komplexität der Anwendung kann der Zeitaufwand für das Beobachten von Metriken 1-2 Stunden betragen, damit die Auswirkungen des Einsatzes Zeit haben, sichtbar zu werden.

Es ist nicht ungewöhnlich, dass Bereitstellungen nach 6-24 Stunden als „fehlgeschlagen“ markiert werden, entweder weil niemand auf die korrekten Metriken geachtet hat oder weil die Leute einfach Warnungen und Fehler ignoriert haben, weil sie dachten, dass dies nicht auf die Bereitstellung zurückzuführen ist.

Dies ist der heilige Gral des Deployments, da es den menschlichen Faktor vollständig aus der Gleichung entfernt und einen Schritt in Richtung Continuous Deployment
(anstelle von Continuous Delivery) darstellt.
Mit diesem Ansatz:

  1. Können Sie Deployments zu jedem beliebigen Zeitpunkt durchführen und wissen, dass die Metriken mit der gleichen Aufmerksamkeit geprüft werden, auch wenn es 3 Uhr morgens ist.
  2. Können Sie frühe Regressionen punktgenau abfangen.
  3. Werden Rollbacks (in der Regel eine anstrengende Aktion) nun von der Bereitstellungsplattform abgewickelt, was den Zugang zum Bereitstellungsprozess für nicht-technisches Personal erleichtert.


Best Practice 23 – Inszenierung entspricht Produktion

Wir haben in Best Practice 12 erklärt, dass Sie dynamische Umgebungen zum Testen einzelner Funktionen für Entwickler verwenden sollten. Dies gibt Ihnen die Gewissheit, dass jede Funktion für sich genommen korrekt ist, bevor Sie sie in der Produktion bereitstellen.
Es ist auch üblich, eine einzelne Staging-Umgebung (auch als Vorproduktion bezeichnet) zu haben, die als letztes Gateway vor der Produktion fungiert. Diese spezielle Umgebung sollte so produktionsnah wie möglich sein, damit alle Konfigurationsfehler und Diskrepanzen schnell entdeckt werden können, bevor die Anwendungsbereitstellung in die reale Produktionsumgebung verschoben wird.

Leider behandeln die meisten Unternehmen die Staging-Umgebung anders als die Produktionsumgebung. Eine von der Produktion getrennte Staging-Umgebung zu haben, ist eine umständliche Praxis, da Sie sie manuell pflegen und sicherstellen müssen, dass sie auch alle Updates erhält, die die Produktion erreichen (nicht nur in Bezug auf Anwendungen,
sondern auch auf Konfigurationsänderungen).
Zwei weitere effektive Möglichkeiten zur Verwendung einer Staging-Umgebung sind die folgenden:

  1. Erstellen Sie bei jeder Bereitstellung nach Bedarf eine Staging-Umgebung, indem Sie die Produktionsumgebung klonen.
  2. Verwendung als Inszenierung eines speziellen Teils der Produktion (manchmal als Schattenproduktion bezeichnet).

Pierre Gronau ist seit über 25 Jahren für namhafte Unternehmen als Senior IT-Berater mit umfangreicher Projekterfahrung tätig. Zu seinen Kompetenzfeldern gehören Server-Virtualisierungen, Informationssicherheit, IT-Compliance, moderne Cloud- und Automationslösungen.


Total
0
Shares
Previous Post

Executive Order und der Solarwinds Hack – Was bedeutet das für uns?

Related Posts