Continuous Delivery: Notizen zu den Grundlagen
Continuous Delivery ist ein Prozess in der Software-Entwicklung, um das Ziel zu erreichen, dass man theoretisch jederzeit den aktuellen Stand der Software ausrollen kann oder dies sogar automatisch macht. Dazu muss natürlich sichergestellt sein, dass der jeweilige Software-Stand die funktionalen und nicht-funktionalen Anforderungen erfüllt.
Für meine Arbeit an meinem eigenen CI/CD-Programm habe ich mir das Buch Continuous Delivery von Jez Humble und David Farley gekauft. In einigen Blog-Einträgen möchte ich jetzt während des Lesens meine wichtigsten Erkenntnisse aus dem Buch und soweit möglich auch meine eigenen Erfahrungen festhalten. Dieser Blog-Eintrag hält die Notizen aus dem ersten Kapitel (The Problem of Delivering Software) fest.
Wozu Continuous Delivery?
Wenn ich mir neue Technologien in der Software-Entwicklung ansehe, versuche ich mir inzwischen immer folgende Fragen zu stellen:
- Was ist das Ziel dieser Technologie? Was will sie verbessern?
- Welche Vorteile bietet sie auf dem Weg zu diesem Ziel?
- Welche Nachteile muss man dafür in Kauf nehmen?
Speziell die Betrachtung der Nachteile finde ich wichtig, weil diese in vielen Blog-Artikeln zu neuen Trends nicht thematisiert werden.
Jez Humble und David Farley beschreiben das Ziel von Continuous Delivery in ihrem gleichnamigen Buch folgendermaßen:
We want to find ways to deliver high-quality, valuable software in an efficient, fast and reliable manner
(Continuous Delivery by Jez Humble and David Farley, p. 12)
Hochwertig (high-quality) definieren sie dabei als eine Software, die zwar nicht perfekt sein muss, aber dennoch die Anforderung des Kunden an Funktionalität und sonstige Anforderungen wie z.B. Geschwindigkeit erfüllt. Nützlich (valuable) wird die Software ihrer Ansicht nach erst dann, wenn der Kunde oder Benutzer sie auch bei sich einsetzen kann. Ansonsten ist es nur eine Idee, die aber noch keinen Mehrwert bietet.
Continuous Delivery ist nun ein Ansatz, den Release-Prozess soweit zu standardisieren und zu automatisieren, dass man automatisch beim Check-In den gesamten Prozess von der Produktänderung bis zum Release durchspielen kann. Das fertige Release soll dann automatisiert auf verschiedenen Ziel-Umgebungen wie z.B. Staging oder Production eingerichtet werden können. Ggfs. kommen auch einzelne Schritte zum Einsatz, die erst von Hand freigegeben werden müssen. So kann es z.B. gewünscht sein, dass das Release erst final freigegeben wird, wenn ein manueller Test durch eine Nutzergruppe durchgeführt und bestanden wurde. Dies soll aber auch mit nur einem Knopfdruck geschehen, sodass sich keine Fehler durch manuelle Aktionen einschleichen können.
Vorteile
Der Vorteil von Continuous Delivery im Release-Prozess ist also einerseits die Verringerung von Fehleranfälligkeit durch manuelle Aktionen und andererseits die Erhöhung der Release-Frequenz, indem jeder Check-In in der Versionsverwaltung automatisch zu einem neuen potentiellen Release führt. Insgesamt führt dies auch zu einer besseren Nachvollziehbarkeit, weil sichergestellt ist, dass die Schritte zum Programm-Release genau nach dem vorgegebenen Automatismus durchgeführt wurden.
Durch die automatische Testausführung kann man fehlgeschlagene Tests eindeutig auf spezifische Änderungen am Code zurückführen. Bei einem traditionellen Setup muss man hier darauf vertrauen, dass Entwickler die Tests vor dem Check-In selbständig durchführen. Ansonsten kann ein Entwickler fehlerhaften Code einchecken und dies wird erst später beim nächsten Release festgestellt.
Nachteile
Natürlich hat jede eingeführte Technologie auch Nachteile. Beispielsweise
kann die Verwendung von Continuous Delivery in einer Server- oder
Cloud-Umgebung dazu führen, dass sich Code
nicht mehr lokal kompilieren lässt. Dies
liegt natürlich nicht per se an Continuous Delivery, aber daran, dass der Code
in einem entfernten System automatisch gebaut wird und das Team daher
vernachlässigt, den Kompiliervorgang auch lokal funktionsfähig zu halten.
Speziell führt meiner Erfahrung nach ein zentralisierter Ansatz auch dazu, dass
der Build-Prozess an sich komplexer wird. Wird der Build-Prozess von vielen
Benutzern auf vielen verschiedenen Systemen verwendet, so muss man diesen
einfach startbar halten (z.B. das typische ./configure && make
). Wird er
dagegen nur an einem zentralen Rechner ausgeführt, kann er beliebig komplex
werden und funktioniert am zentralen System immer noch (aber lokal nicht mehr
oder nur schwer).
Continuous Delivery führt natürlich auch zu einem höheren Bedarf an System-Ressourcen, weil bei jedem Check-In alle Tests ausgeführt werden und ein Release erstellt wird. In einem traditionellen Umfeld hingegen wird vielleicht aus jedem hundersten Check-In ein Release gebaut.
Delivery Pipeline
Grundsätzlich basiert die Struktur von Continuous Delivery auf einer Delivery Pipeline, also einer Abfolge von Schritten von der Änderung bis zum Release. Jeder dieser Schritte kann einen Fehler auslösen und das Release damit verhindern. Dies kann beispielsweise der Fall sein, wenn Testfälle nicht bestanden werden und somit die korrekte Funktionalität des Programms infrage steht. Humble und Farley schlagen auch vor, dass manche Schritte in der Pipeline Warnungen auslösen können, die man übergeben kann. Beispielsweise könnte ihrem Beispiel zufolge ein Performance-Verlust über das festgelegte Ziel hinaus erlaubt sein, wenn ein Release einen kritischen Fehler behebt und sofort freigegeben werden sollte.
Rückmeldungen aus der Pipeline an das Projektteam sollten so schnell wie möglich erfolgen. Deshalb sollte man schnelle und unbedingt notwendige Tests an den Anfang der Pipeline stellen, während langsamere und optionale Tests weiter hinten in die Pipeline kommen. So ist sichergestellt, dass kritischere Fehler früher zurückgemeldet werden.
In der Pipeline kann es Schritte geben, die sich nicht automatisieren lassen. Diese sollten meines Erachtens trotzdem in die Pipeline mit eingebaut werden in einer Form, in welcher die verantwortlichen Teams ihre Freigabe durch einen Knopfdruck erteilen können. Nicht automatisierbar sind laut Humble und Farley beispielsweise Demonstrationen für den Kunden oder Nutzbarkeitstests mit Menschen. Automatisierbare Schritte können sein:
- Kompilierung des Programms
- Installation des Programms
- Konfiguration des Programms
- Upgrade/Downgrade der Datenbank (Downgrade im Falle eines Rollbacks des Releases)
- Konfiguration des Servers (z.B. Netzwerk)
Versionsverwaltung
Die Continuous-Delivery-Pipeline wird durch den Check-In in der Versionsverwaltung angestoßen. Damit Continuous Delivery gut funktioniert, sollte grundsätzlich alles, was zur Projekteinrichtung benötigt wird, in der Versionsverwaltung liegen. Damit ist nicht nur der Code des Programms gemeint, sondern alles, was zu einer Änderung an einer für das Deployment relevanten Komponente führen kann. Im Buch Continuous Delivery wurden vier Elemente festgehalten, welche Einfluss auf ein erfolgreiches Release haben. Jede Änderung an einer dieser Komponenten sollte eine Ausführung der Delivery Pipeline zur Folge haben:
- Code
- Konfiguration
- Host-Umgebung
- Daten
Das heißt, dass wir idealerweise alle diese Dinge in einer Versionsverwaltung halten sollten (Daten z.B. über Git-LFS oder Git-Annex). Im Detail könnten das folgende Dateien sein:
- Code
- Build Scripts (z.B.
Makefile
oder Ähnliches) - Konfiguration des Programms für jede Ziel-Umgebung
- Setup-Scripts für das Programm
- Scripts zum Aufsetzen der Umgebung oder deklarative Definition (z.B.
bash
-Skripts oderansible
) - Daten für das Programm über Git-LFS oder Git-Annex
Wie man im zweiten Kapitel des Buchs erfährt, empfehlen die Autoren hierbei durchaus verschiedene Speicherorte. So sollte laut Kapitel 2 der Programmcode von den Konfigurationswerten getrennt sein.
Das Ziel sollte sein, dass ein neuer Mitarbeiter im Projekt mit einem einfachen Befehl eine komplette Umgebung aufsetzen und loslegen kann:
git clone https://example.com/myrepo.git
cd myrepo && make dev
Fazit
Letztenendes kommt es meines Erachtens nicht darauf an, wie man seinen Prozess nennt. Ein Unternehmen oder Projekt kann Continuous Delivery einsetzen und trotzdem einen unverständlichen und lokal nicht mehr ausführbaren Build-Prozess entwickeln. Wichtig erscheint mir zunächst einmal, dass man sich klar macht, welche Änderungen am System Einfluss auf das Release haben (Code, Konfiguration, Umgebung, Daten) und dass man diese sauber und idealerweise an einer Stelle versioniert verwaltet.
Auch mit Continuous Delivery muss man sich aber weiterhin aktiv dafür
einsetzen, dass der Release-Prozess auch vom lokalen System problemlos
durchführbar ist (z.B. für ein Setup auf localhost
oder auf einen
neuen Rechner für Kundenpräsentation).
Dies ist ein Aspekt, der meines Erachtens in nahezu allen
bisherigen Continuous-Delivery-Ansätzen verloren geht, weil diese die Pipeline
nur auf einem Server ausführen.