Continuous Integration ist seit ein paar Jahren in aller Munde.
In der Praxis merke ich bei diesem Thema oft, dass es immer noch eine Unsicherheit gibt, was der Unterschied zwischen Continuous Integration, Continuous Delivery und Continuous Deployment ist. Daher möchte ich das Thema hier gerne noch einmal erläutern. Dazu wird es mehrere Artikel geben, als erstes werde ich hier nun auf Continuous Integration eingehen.
Continuous Integration
Continuous Integration ist ein Verfahren aus der Softwareentwicklung, bei der eine kontinuierliche Integration der täglichen Arbeit durchgeführt wird, um die Software Qualität zu verbessern. Bei der Integration werden automatisiert alle Tests ausgeführt und ein Build angestoßen. So können Integrationsfehler schnell gefunden und behoben werden. Das Konzept beruht auf der permanenten Integration von Software Komponenten von Kent Beck, der es im Rahmen von Extreme Programming populär gemacht hat und später von Martin Fowler weiter definiert wurde.
Martin Fowler hat 10 Grundsätze definiert, die erfüllt werden müssen:
- Gemeinsame Codebasis Verwendung eines Versionsverwaltungssystems
- Automatische Übersetzung Quelltexte sollten nicht manuell übersetzt werden
- Kontinuierliche automatisierte Test-Entwicklung Zu jedem fertigen Feature sollte es eine entsprechende Testabdeckung geben.
- Häufige Integration Jeder Entwickler sollte seine Änderungen so oft wie möglich in das Versionsverwaltungssystem integrieren.
- Integration in den Hauptbranch Es sollte regelmäßig in den Hauptbranch integriert werden, wo dann der Build- und Testzyklus gestartet werden und die kontinuierliche Integration beginnt.
- Kurze Testzyklen Es muss abgewogen werden welche Tests zur ständigen Integration ausgeführt werden und welche nur kurz vor der Inbetriebnahme. (Unit-Tests, Integrations-Tests, Security-Tests …)
- Gespiegelte Produktionsumgebung Die Änderungen sollten in einem Abbild der echten Produktionsumgebung getestet werden. Das heißt die Testumgebung sollte der Produktionsumgebung entsprechen.
- Einfacher Zugriff Auch Nicht-Entwickler brauchen einen Zugriff auf die Ergebnisse der Integration und der Tests.
- Automatisierte Reports Die Reports sollten automatisiert erstellt werden, wann die letzte erfolgreiche Integration ausgeführt wurde, welche Änderungen seit der letzten Auslieferung integriert wurden, welche Qualität die Version hat.
- Automatisierte Verteilung Jede Version sollte leicht in eine Produktionsumgebung gebracht werden können.
Vorteile
- schnelles Erkennen und Beheben von Integrationsproblemen
- kontinuierliches Ausführen aller Unit-Tests, dadurch schnelles Erkennen von Fehlern (Nebeneffekten von Änderungen)
- Ständige Verfügbarkeit eines aktuellen lauffähigen Standes für Demo-, Testzwecke und Kundenpräsentation (agiles Herangehen)
- „Erziehung“ von Entwicklern im positiven Sinne, sich etwas mehr um gute Codequalität zu kümmern und verantwortungsbewusster mit Checkins umzugehen.
- Häufiges Integrieren verringert den merge-Aufwand und die Fehler beim Mergen.
In der Praxis
In der Praxis sieht Continuous Integration wie folgt aus.
Gemeinsame Codebasis
Um eine gemeinsame Codebasis zu realisieren benutzt man ein Versionsverwaltungstool, mit Hilfe dieser Software werden die Quelltexte verwaltet, versioniert und integriert, üblicherweise werden dazu Tools wie git, svn usw. verwendet.
Im Repository sollten alle projektrelevanten Dateien enthalten sein, also nicht nur der Quellcode. Ein neu ins Team gekommender Entwickler sollte das Projekt frisch aus dem git auschecken können und mit Hilfe einer kurzen Dokumentation schnell alles einrichten können (z.B. Datenbankskripte, Konfigurationsdateien, Testskripte, IDE Einstellungen).
Ich habe die Erfahrung gemacht, dass es auch sehr sinnvoll sein kann alle anderen projektspezifischen Dokumente ins Repository einzuchecken. Dadurch werden auch Dokumente wie Spezifikationen, Angebote immer zu der entsprechenden Projektversion versioniert und getaggt, so lassen sich Anforderungsänderungen später gut nachvollziehen.
Zusätzlich sollte darauf geachtet werden, dass alles was für einen automatisieren Build (inkl. Tests) auf einem CI Server notwendig ist entsprechend im Repository abgelegt wird.
Allerdings sollten die Ergebnisse des Builds nicht im Repository hinterlegt werden, diese können entweder für eine kurze Zeit auf dem CI-Server verbeiben oder wenn es produktive Ergebnisse sind können diese in einem Software Komponenten Management System wie z.B. Nexus hinterlegt werden. Im Allgemeinen sollte man aber immer in der Lage sein aus jedem Versionsstand (min. aber aus der getaggten Version) im Repository ein Build Ergebnis wiederherzustellen.
Automatische Übersetzung/Bauen
Um ein automatisches Bauen der Software zu realisieren benutzt man Build-Management-Tools, mit Hilfe dieser Software kann der komplette Build-Prozess automatisiert werden, üblicherweise werden Tools wie gradle, ant, MSBuild usw. verwendet.
Kontinuierliche automatisierte Test-Entwicklung
Um eine kontinuierliche automatisierte Test-Entwicklung zu realisieren gibt es mittlerweile viele Frameworks und Tools. Hier ein paar Beispiele: im Java Umfeld: JUnit, Spock …, im .Net: NUnit …, im JavaScript: Jasmine … zusätzlich gibt es auch noch Cucumber, Geb …
Allerdings helfen diese Framworks und Tools nichts wenn man diese nicht auch kontinuierlich benutzt. Der Einsatz der Frameworks ist nicht schwer, das Einbauen in den Entwicklungsalltag erfordert ein Umdenken und Umstrukturieren der eigenen Arbeit und ein gutes Stück Disziplin. Sobald man es aber geschafft hat, will man das gute Gefühl eine hohe Testabdeckung zu haben und diese grünen Bubbles und Häkchen zu sehen nicht mehr vermissen.
Wichtig für CI ist das so beim Integrieren Fehler schnell gefunden werden bevor die Software zum Kunden geht, wenn alle Features eine hohe Testabdeckung haben können mögliche Integrationsfehler automatisiert gefunden werden.
Häufige Integration
Die häufige Integration ist wichtig, um schnell Integrationsfehler zu finden. Je länger man wartet, um so mehr hat man an der bestehenden Version geändert und kann entstehende Konflikte oder Fehler nicht mehr so gut finden und nachvollziehen. Außerdem entwicklert man nicht unnötigt ein Feature weiter obwohl es evtl. bei der Integration so gar nicht funktioniert.
Integration in den Hauptbranch
Das häufige Integrieren in den Hauptbranch stellt sicher, dass Fehler beim Integrieren aller Komponenten schnell gefunden werden.
Kurze Testzyklen
Es ist wichtig kurze Testzyklen zu haben, um das häufige Integrieren zu fördern. Muss ein Entwickler beim Einchecken immer lange warten bis alle Tests durchlaufen sind, wird er nicht mehr häufig integrieren. Auch im CI Server müssen beim Einchecken nur alle “schnellen” Tests durchlaufen werden, damit man schnelles Feedback bekommt. Die langsamen Tests können in einem Nightly Build durchlaufen werden, so dass der Entwickler am nächsten Tag die Ergebnisse seiner Arbeit vom Vortag prüfen kann.
Gespiegelte Produktionsumgebung
Das ist ein ganz wichtiges Thema, was aber leider immer wieder nicht so umgesetzt wird. Mindestens eine der Testumgebungen (mittlerweile haben Unternehmen ja 2-5 oder so gar noch mehr) muss genau der Produktionsumgebung entsprechen, sonst macht das vorherige Testen gar keinen Sinn. Ist die Produktionsumgebung anders als die Testumgebungen, kann man nicht mit Sicherheit sagen, dass es auf der Produktionsumgebung wirklich läuft und nicht doch Fehler auftreten, die man vorher schon hätte finden können. Man kann es nicht wirklich 100%ig verhindern, aber das was man tun kann finde ich sollte man tun, um Fehler vorher abzuschätzen und herrauszufinden. Der Kunde bezahlt ja schließlich viel Geld für unsere Dienstleistung.
Einfacher Zugriff
Der einfache Zugriff ist auch ein sehr wichtiger Punkt. Alle Teammitglieder müssen schnell und einfach auf Ergebnisse der Tests und der Integration zugreifen können, damit diese die dadurch entstandenen Fehler auch schnell beheben können. Der Projektleiter sollte auch einen einfachen Zugriff auf Ergebnisse haben, um den Stand des Projektes gut einschätzen zu können. Eventuell können sogar Kunden auf Auswertungen Zugriff erhalten, um den Fortschritt Ihres Projektes verfolgen zu können. Diese Auswertungen sollten auf jeden Fall auf die Bedürfnisse der Anwendergruppen angepasst sein. Ein Kunde interessiert sich sicher nicht für die Logfiles und der Entwickler nicht nur für Statistiken.
Automatisierte Reports
Die Reports für die Anwendergruppen sollten automatisiert erstellt werden, um einen schnellen und einfachen Zugriff zu gewährleisten. Die Reports zeigen auch welche Version ohne Fehler durchgelaufen ist.
Automatisierte Verteilung
Für bestimmte Tests muss die Software in verschiedenen Umgebungen laufen. Dazu muss die Software automatisiert in verschiedenen Umgebungen deployed werden können, zusätzlich verhintert das automatisierte Deployment Fehler und es geht wesentlich schneller. Am Besten kann man bestimmte Konfigurationen von außen für die Anwendung ändern, wie z.B. die Umgebung, E-Mail Server, Datenbankverbindungen …. so dass die Anwendung beim Deployen in anderen Umgebungen nicht neu gebaut werden muss. So stellt man sicher, dass das getestete Artefakt in jeder Umgebung eingesetzt werden kann, ohne das Risiko dass es beim Bauen wieder zu Fehlern kommt.
Sandra Gerberding
Senior DevOps Engineer