Continuous Integration

Aus THM-Wiki
Wechseln zu: Navigation, Suche

Continuous Integration (permanente bzw. ständige Integration, zu deutsch Kontinuierliche Integration, im Folgenden auch mit "CI" abgekürzt) ist ein Begriff aus der Software-Entwicklung, der beschreibt, wie Software regelmäßig nach jeder Änderung neu gebuildet und getestet wird. In den meisten Fällen wird dieses Konzept mit Extreme Programming (XP) in Verbindung gebracht, obwohl es bereits schon älter ist. Das Ziel soll eine kürzere Entwicklungszeit durch das schnellere Bemerkungen von Fehlern im Code, sowie eine vereinfachteres und weniger fehleranfälliges Zusammenarbeiten von mehreren Entwicklern an einem Projekt sein.

Grundsätze

Jeder Entwickler, der an einem Projekt arbeitet, lädt den Code aus einem gemeinsamen Repository (zu deutsch "Lager") herunter ("auschecken") und erhält so eine lokale Kopie. Er arbeitet anschließend an der lokalen Kopie und lädt seinen geänderten Code zu der gemeinsamen Code-Basis hoch ("committen"). Auf diese Art und Weise können mehrere Entwickler an einer Codebasis arbeiten. Nach jedem Commit wird der Code aus der Codebasis neu gebuildet und vorher definierte Testfälle ausgeführt. Mit diesem Vorgehen kann sehr schnell getestet werden, ob der Code fehlerhaft ist. Dies wäre der Fall, wenn der Code aufgrund von fehlerhafter Syntax gar nicht erst gebuildet werden kann oder eines der Testfälle fehlschlägt.

Geschichte

CI erlangte erst durch das Extreme Programming-Vorgehensmodell Bekanntheit, welches von Kent Back während des Chrysler Comprehensive Compensation System (C3) payroll project erfunden wurde. Er schrieb 1999 das Buch Extreme Programming Explained, indem CI erstmals einem breiteren Publikum vorgestellt wird. Martin Fowler, ebenfalls einer Verfechter von XP und zur der damaligen Zeit bei ThoughtWorks angestellt, stellte um die Jahrtausendwende noch einmal dezidiert die Vorteile von CI heraus. Seitdem erfreut sich CI wachsender Beliebtheit und wird in zunehmend mehr Software-Projekten eingesetzt, da so frühzeitig Fehlerquellen erkannt werden und die Entwickler besser aufeinander abgestimmt arbeiten können.

Bestandteile eine CI-Systems und empfohlene Praktiken

Im Folgenden werden die Bestandteile eines CI-Systems, sowie die empfohlenen Praktiken aufgezeigt und erklärt:

Verwendung eines verteilten Versionierungssystems

Es sollte ein verteiltes Versionierungssystem mit einem Repository (bzw. einer Code-Basis) verwendet werden. Jeder Entwickler hat die Möglichkeit, aus dieser Code-Basis auszuchecken und somit eine lokale Kopie dieses Codes zu kreieren. Diesen Code kann er dann bearbeiten, testen und anschließend wieder committen. Wichtig ist, dass aller Entwickler am Repository arbeiten und jede für das Projekt relevante Datei (auch Bilder, Dokus, SQL-Migration-Files, usw.) sich in dem Repository befindet. Es muss möglich sein, allein aus den im Repository befindlichen Dateien das Projekt zu builden.

Automatisiertes Builden

Der Build-Status sollte vollautomatisiert mit einem geschrieben Skript erfolgen. Für UNIX bietet sich Make an, für Java ANT sowie für .NET-Entwickler NAT bzw. MSBuild. Bei einem Build-Vorgang sollte darauf geachtet werden alle Dateien zu inkludieren. Auch solche, die sich mit an Sicherheit grenzender Wahrscheinlichkeit nicht mehr ändern. Das Build-System sollte so intelligent sein und erkennen, welche Dateien sich seit den letzten Build geändert haben und dieses beim Build-Vorgang berücksichtigen. Gerade bei großen Projekten kann so wertvolle Zeit gespart werden. Darüber hinaus soll es die Möglichkeit anbieten, den Code in unterschiedlichen Variationen (Mit/ohne Testcode, mit/ohne GUI, Anfänger/Experten-Modus, usw.) zu builden.

Testfälle erstellen

Ebenfalls wichtig ist die Anwesenheit von Testfällen, welche automatisiert ausgeführt werden können. Syntaktische Fehler (beispielsweise ein vergessenes ";") werden leicht erkannt und haben meistens keine schwerwiegenden Folgen im Gegensatz zu semantischen Fehlern, die im schlimmsten Fall - gerade bei verteilten Systemen - nur selten auftreten und nicht reproduzierbar sind. Aus diesem Grund ist es wichtig, testgetrieben zu entwickeln (Test Driven Development (TDD)), um bereits durch Testfälle die Anforderungen an die Software zu spezifizieren.

Bekannte Tools

Nachfolgend werden zwei bekannte Tools zum automatisierten Durchführen von Testfällen vorgestellt

XUnit

XUnit wurde ursprünglich von Kent Beck unter dem Namen SUnit für die Programmiersprache Smalltalk entworfen. Mittlerweile gibt es Ausprägungen für viele Programmiersprachen, zum Beispiel phpUnit, JUnit, DUnit, usw.


Beispiel:

public function testShouldEncryptAndDecryptMessageWithRSA() {
		$this->crypt = Crypt::instance("RSA", $this->keymanager);
		
		$message = $this->getRandomMessage();
		
		$enc    = $this->crypt->encrypt($message);
		$actual = $this->crypt->decrypt($enc);
		
		$expected = $message;	

		$this->assertEquals( $expected, $actual );	
	}

In diesem Beispiel aus der eModeration im eStudy.Projekt wird getestet, ob der Ver- und Entschlüsselungsalgorithmus funktioniert indem einfach eine Nachricht ent- und anschließend verschlüsselt wird und mit der ursprünglichen Nachricht verglichen wird.

Selenium

Selenium ist ein Framework zum Testen von Web-Anwendungen. Benutzereingaben (Tastatureingaben, Mausklicks) können automatisiert aufgezeichnet werden. Die Selenium-IDE gibt es beispielsweise als Firefox-Addon. Mit Hilfe von "verify" und "assert" können Zusicherungen definiert werden, die für einen erfolgreichen Testverlauf eingehalten werden müssen.

Häufiges Durchführen eines Commits

Jeder Entwickler sollte mindestens einmal täglich seinen geänderten Code committen. Dadurch werden Konflikte, die durch das gemeinsame Bearbeiten an einer Datei enstehen, frühzeitig erkannt. Je früher solch ein Konflikt erkannt wird, desto ressourcen- und nervenschonender kann er beseitigt werden. Insofern der Entwickler seinen Code lokal getestet hat machen auch häufigere Commits Sinn um entstehende Konflikte noch früher zu bemerken.

Verwendung eines CI-Servers

Durch den Einsatz eines CI-Servers, beispielsweise der Hudson CI oder der etwas älterere CruiseControl, kann nach jedem Commit der Code neu gebuildet und getestet werden. Der CI-Server untersucht den Code statisch und dynamisch nach den vorgegebenen Testfällen. Im Falle eines Fehlschlagens des Builds oder einer Nichteinhaltung kann er automatisiert ein Revert des letzten Commits veranlassen und den entsprechenden Benutzer benachrichten (alternativ auch nur den Entwickler benachrichtigen, welcher das Revert dann selber ausführt).


CI-Server im Einsatz im Dotplot-Projekt

Skalierbarkeit des Builds

Der Build sollte schnell (ca. 10 Minuten) erfolgen, um eine schnelle Rückmeldung zu dem letzten Commit zu erhalten. Leider ist dies gerade bei größeren Projekten, wo auch größere Daten(bank)mengen in den Build mit einfließen können, nicht möglich. Aus diesem Grund und zur Erschaffung einer Skalierbarkeit ist der Einsatz einer "Build Pipeline" sinnvoll. Es existiert ein Basis-Build, in der auch große Mengen aus einer evtl. vorhandenen Datenbank vorhanden sein können. Diese Basis-Build kann längere Zeit dauern und wird aus diesem Grund seltener, zum Beispiel jede Nacht um 3 Uhr ausgeführt. Über den Basis-Build existiert ein zweiter Build, ein (nach Fowler) "Commit Build", der nach jedem Commit neu gebuildet wird. Dadurch wird eine schnelle Rückmeldung erreicht, allerdings um den Preis, dass Bugs, die den Basis-Build betreffen, vorerst nicht erkannt werden. Man muss somit eine Gratwanderung zwischen steigender Wahrscheinlichkeit von Bug-Erkennung im Commit Build und höherer Ausführungzeit des Commit Builds treffen.

Tests in einen Produktions-Klons

Sofern größere Projekte bereits produktiv eingesetzt werden, macht es Sinn, einen Klon der Produktion zu erstellen und darauf zu testen. Im eStudy-Projekt werden beispielsweise Testumgebungen mit aus der Produktion anonymisieren Datenbankdaten erstellt, um darauf produktionsnah testen zu können. Gerade Perfomance-Probleme können oft nur durch den Einsatz produktionsnaher Testumgebungen gefunden werden.

Transparenz der Vorgänge

Es ist wichtig, dass alle Entwickler auf eine einfache Art und Weise den Build-, Test- und Projektverlauf transparent angezeigt bekommen. Auf diese Art bleiben sie immer up-to-date und wissen über aktuelle Entwicklungen Bescheid. Außerdem wissen sie, wer aktuell an welchem Teil der Software arbeitet und ob diese Arbeiten seine Arbeiten in irgendeiner Weise tangieren. Im eStudy-Projekt kommt hier ein Ticket-System zum Einsatz, dass - neben dem CI-Server - alle Builds aufzeigt. Darüber hinaus zeigt es an, wen wann was committet hat (natürlich tragen die Commits sinnvolle Kommentare).

Automatisiertes Deployment

Es nützt natürlich wenig, wenn man das gebuildete und getestete Projekt nun auf seinen Server liegen hat, aber nicht weiß wie man es in die Produktion bekommt. Um zu vermeiden, dass nach den ganzen Sicherheitsmechanismen Fehler auf der "letzten Meile" passieren, sollte das Deployment automatisiert geschehen. Das in der Produktion clusterbasierte eStudy bietet für die Administratoren auf den einzelnen Nodes Skripte an, die automatisch den letzten Build auschecken. Sinnvoll ist dies nicht nur, um neuen Code in der Produktion zu deployen, sondern auch um Neuentwicklern den Einstieg in das Projekt zu erleichtern. Diese können dann in ihren lokalen TUs den letzten Build mithilfe des Skripts in ihrer TU deployen.

Weitergehende Werkzeuge

Projektmanagement- und Bugtracker Trac

Mit Trac und seiner integrierten Subversion-Schnittstelle kann ein Projekt transparent gemacht werden. Die letzten Build-Vorgänge können angezeigt werden und anhand der Commit-Kommentare kann man sich einen schnellen Überblick über den derzeitigen Stand des Projekts machen. Es können Meilensteine definiert werden, um die Entwicklung strukturierter gestalten zu können. Eine weitere wichtige Funktion ist das Bugtracking. Es können Tickets erstellt werden, in denen ein Bug oder ein Feature-Wunsch eingetragen wird. Diesem kann eine gewisse Priorität, Keywords zur Kategorisierung und weitere Informationen zugewiesen werden. Die Entwickler erhalten so eine zentrale Stelle, wo alle Bugs/Features enthalten sind. Gefixte Bugs/erfüllte Featurewünsche können eingetragen werden, womit das Ticket als erledigt gilt.

Nachfolgend die Übersicht über die vergangenen Builds:

Übersicht über vergangene Builds



Nachfolgend die Übersicht von Tickets im Trac:

Tickets im Trac

Security Scanner

Mithilfe von Security Scannern können leicht Schwachstellen an der eigen Netz-Infrastruktur erkannt werden. Das Tool der Wahl ist hierbei (traditionell) NMap ("Network Mapper"), mit dem so viele Informationen wie möglich über einen entfernten PC gefunden werden kann. Dazu gehören offene Ports, verwendetes Betriebssystem, verwendete Software (zB. "OpenSSH" oder "Apache Webserver") mit exakter Versionsnummer, Uptime, verwendete Firewall-Techniken, u.U. Hersteller der Netzwerkkarte, usw. Über dieses Tool kann somit die Sicherheit der Produktion überprüft werden und Schwachstellen gefunden werden. Aufgrund des sogenannten Hackerparagraphen kann der Einsatz von NMap rechtliche Konsequenzen tragen, da mit diesem Tool auch ein Angriff auf einen anderen PC vorbereitet werden könnte. Die Sinnhaftigkeit dieses Gesetzes ist, gerade wegen der so entstandenen Situation rechtlicher Ungewissheit für Administratoren und versierten Internet-Nutzern, durchaus diskussionswürdig.


NMap im Einsatz

ModSecurity

ModSecurity ist eine Web Application Firewall (WAF) und eine ideale Ergänzung zur Hardware-Firewall. 70 % aller Angriffe auf Web-Anwendungen laufen über die Applikationsschicht[1]. Um dies einzudämmen, werden alle Requests (Body und Header) und Responses (Body und Header) überprüft und mithilfe von wahlweise Black- oder Whitelists gefiltert.

Weiterführende Verweise

Zusätzlich zu denen im Artikel angegeben Verweise sind noch Folgende interessant:

Martin Fowler in seinem fundamentalen Artikel über CI

Die englische Wikipedia über CI

Der Hudson-CI-Server vom MNI-Fachbereich

Das Trac-System von eStudy

Eine sehr gute Übersicht (von ThoughtWorks) über die zur Zeit erhältlichen CI-Tools

Das Vorgehensmodell XP ausführlich erklärt

Einführung von Jared Richardson in CI