Ex Machina: Automation Divided
AUTOREN DOMINIK RÖSCHKE & NIKOLAS MAY
In diesem Artikel werfen wir einen Blick auf einige Continuous-Integration-Server (CI) und beleuchten ihre Vor- und Nachteile genauer. Am Schluss werden Empfehlungen für mögliche Einsatzgebiete gegeben.
Ex Machina: aus der Maschine. Seinen Ursprung hat der Begriff im altgriechischen Theater, vor allem in Tragödien. Tragische Konflikte konnten nicht von den beteiligten Menschen gelöst werden, dies musste durch Einmischung der Götter von außen geschehen. Meist durch den Einsatz einer Theatermaschine: Deus ex Machina – Gott aus einer (Theater-)Maschine.
Auch als Softwareentwickler haben wir häufiger Probleme, die wir nicht – oder nur schwer – allein lösen können. Continuous Integration (CI) ist eines dieser Probleme. Würde man die CI-Arbeit per Hand erledigen, so würde dies für jeden Commit bedeuten, dass dieser manuell gebaut, getestet und gegen den Haupt-Branch integriert werden müsste. Abgesehen davon, dass dies ein stark repetitives und fehleranfälliges Vorgehen wäre, würde es den eigentlichen Handlungsstrang, die Softwareentwicklung, nur sehr langsam vorantreiben.
Warum sich also nicht den Trick seiner Vorfahren zunutze machen und die Ex Machina zu Hilfe nehmen: einen CI-Server. Dieser übernimmt dann das Bauen, Testen und Integrieren der Software automatisch. Nachdem sich Continuous Integration inzwischen sehr weit verbreitet hat, gibt es auch eine Vielzahl verschiedener CI-Server, jeder mit seinen eigenen Vor- und Nachteilen.
Unter anderem zu nennen sind GitLab CI, Drone CI und CircleCI, auf die wir uns in diesem Vergleich konzentrieren werden. Natürlich gibt es noch eine große Auswahl an anderen Produkten (Jenkins, Teamcity, Concourse CI, Travis-CI, Bamboo usw.), die man noch beleuchten könnte. Aus Platzgründen werden wir bei den genannten drei CI-Servern bleiben.
GitLab CI
GitLab CI hat 2014 seinen Anfang als Git Repository gemacht und wurde kontinuierlich erweitert, unter anderem auch durch eine integrierte CI-Lösung – GitLab CI/CD. Diese ist inzwischen in der Version 13 verfügbar und bringt einige spannende Features mit:
- Auto DevOps: Bei Aktivierung verspricht GitLab, automatisch zu erkennen, welche Art von Software im Repository liegt, und dafür die CI/CD-Features zu aktivieren. Dies umfasst das Bauen, Testen, Deployen und Monitoren der Software.
- Review-Apps: Im Zusammenspiel mit einem Kubernetes-Cluster kann GitLab automatisch für jeden offenen Branch eine eigene Review-Umgebung aufsetzen. Dies ermöglicht, dass bereits vor einem Merge auf einfachste Art und Weise eine Version von Nicht-Entwickelnden ausprobiert werden kann.
- Integrierte Docker-Registry
Auto DevOps und Review-Apps funktionieren im Optimalfall Plug- and-Play und erzeugen keinen zusätzlichen Konfigurationsaufwand. In der Praxis hat sich allerdings gezeigt, dass gerade bei komplexeren Softwareprojekten das Tooling doch überfordert ist und man die Pipeline per Hand konfigurieren muss.
Möchte man GitLab manuell konfigurieren, beginnt alles mit der .gitlab-ci.yml (siehe Listing 1), die versioniert im Repository mitabgelegt wird. Hier wird in verständlicher Pipeline-Syntax beschrieben, wie das Projekt zu bauen ist. Wie man darin erkennen kann, lassen sich Dependencies auf sehr einfache Art und Weise cachen, um Build-Times zu reduzieren.
Die Vorteile von GitLab CI/CD sind unter anderem die Integration in das eigene Repository und die vollautomatische Konfiguration einer CI-Pipeline. Auch, dass man auf gitlab.com eigene CI-Runner anbinden kann, ist unglaublich praktisch. So kann sogar der eigene Entwicklungsrechner zum CI-Runner werden, falls die 200 Freiminuten aufgebraucht sind. Alles was dafür benötigt wird, sind Docker und das GitLab-Runner-Binary. Review-Apps sind, gerade in Zeiten von vermehrter Remote-Arbeit, auch ein nicht zu unterschätzendes Mittel, um Kommunikation und Zusammenarbeit zu vereinfachen.
Ein Nachteil von GitLab CI ist die vergleichsweise kleine Community: 4.300 Fragen auf Stack Overflow verglichen mit 43.000 Fragen zu Jenkins. Das wird aber durch eine sehr umfangreiche Dokumentation sehr gut ausgeglichen.
GitLab gibt es gehostet in vier verschiedenen Preiskategorien. Alternativ kann es mit ein bisschen Konfigurationsaufwand selbst gehostet werden, auch hier gibt es eine Free und eine Premium Edition, die vor allem zusätzliche Organisationsfeatures bietet.
image: docker:latest
services:
- docker:dind
# Definition des Dependency-Caches
cache:
paths:
- .m2/repository
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
stages:
- build
maven-build:
image: maven:3.6-amazoncorretto-11
stage: build
script: "mvn package -B"
artifacts:
paths:
- target/*.jar
CircleCI
Bei CircleCI handelt es sich um eine 2011 gegründete, cloudbasierte Continuous-Integration- und Delivery-Plattform. Es unterstützt den CI/CD-Prozess durch Build-Pipelines. Als Laufzeitumgebungen für die Pipelines werden Docker, Linux, Windows und MacOS angeboten. Von Haus aus wird eine Anbindung an GitHub und Bitbucket ermöglicht. Zudem gibt es für eine Vielzahl von Programmiersprachen und Build-Tools Beispiel-Build-Pipelines.
CircleCI hält seine Konfiguration als YAML-Datei, die sich unter .circleci/config.yml befindet (siehe Listing 2). Somit wird die Pipeline-Definition im Repository versioniert und kann bei jedem Commit ausgewertet und berücksichtigt werden. Zudem werden für jeden Build ein neuer Docker-Container beziehungsweise eine virtuelle Maschine erzeugt. Das hat zur Folge, dass man immer auf einer frischen Instanz arbeitet, die sich in einem wohldefinierten Zustand befindet.
version: 2 jobs: build: #Definiton des Jobs mit Namen build
docker:
- image: circleci/openjdk:8-jdk # Dockerimage in dem gearbeitet wird
working_directory: ~/repo
steps: #
- checkout # Repository auschecken
- restore_cache:
keys:
- maven-dependencies-{{ checksum "pom.xml" }} # Cache wiederherstellen
- maven-dependencies- #Fallback, falls sich die Checksumme geändert hat
- run: mvn dependency:go-offline #Maven dependencies herunterladen
- save_cache: #Lokales Maven Repository cachen
paths:
- ~/.m2
key: maven-dependencies-{{ checksum "pom.xml" }}
- run: mvn package #Test ausführen
- store_test_results: #läd die Testergebnisse hoch
path: petclinic/target/surefire-reports
- persist_to_workspace: #Persistiert die Dateien in einem zwischen den Jobs geteilten Workspace
root: .
paths:
- petclinic/target/petclinic.war
Um die Build-Zeiten dennoch auf eine akzeptable Geschwindigkeit zu bringen, bietet CircleCI integriertes Caching (siehe auch Listing 2). Mittels save_cache können zum Beispiel Dependencies gecacht werden, die bei einem erneuten Build mittels restore_cache wiederhergestellt werden. Somit müssen nur noch bei Änderungen die Abhängigkeiten aktualisiert werden. In der unter Listing 2 gezeigten Pipeline verringert das Caching die Build-Zeiten des Maven-Projekts von 75 auf 13 Sekunden.
CircleCI bietet die Möglichkeit, Pipeline-Elemente über sogenannte Orbs wiederzuverwenden. Damit können öfter auftretende Build-Schritte vereinheitlicht und über Parametrisierung gesteuert werden. Die so erstellten Orbs werden im sogenannten Orb Registry gespeichert und unterliegen dem Open-Source-Gedanken. Dies hat den Vorteil, dass man sich vieler bereits existierender Orbs bedienen kann [1] und nicht das Rad neu erfinden muss.
Sollte es bei CircleCI trotz der umfangreichen Log- und Analysemöglichkeiten zu einem Fehler kommen, der nicht nachgestellt werden kann, besteht die Möglichkeit, eine SSH-Verbindung zum Build-Job aufzubauen, um diesen zu debuggen.
Alles in allem ist CircleCI ein mächtiger CI-Service, der eine Vielzahl an vordefinierten Funktionen und Orbs mitbringt. Des Weiteren besteht die Möglichkeit, CircleCI On-Premises zu hosten, wenn man beispielsweise aus regulatorischen Gründen darauf angewiesen ist. Als Negativpunkte sind zum einen die Beschränkung der Versionsverwaltungen auf Bitbucket und GitHub anzumerken. Zum anderen dürfte das Preismodell abschrecken. Hierfür werden im Cloud-Modell 15,00 Euro pro User zuzüglich Rechenzeit berechnet. Im „On-Premises“-Betrieb werden 35,00 Euro pro User fällig. Eine gute Nachricht für alle Open-Source-Projekte: CircleCI schenkt euch Rechenzeit im Wert von 240,00 Euro pro Monat!
Drone CI
Drone CI ist ein relativ neuer CI-Server (die Version 1.0 wurde am 7. November 2018 veröffentlicht) und ist auf eine Container-native CI-Pipeline ausgelegt. Drone selbst lässt sich am einfachsten direkt über Docker oder Kubernetes hosten. Für ein schmerzfreies Setup werden vorgefertigte Helm-Charts zur Verfügung gestellt. Drone wurde Anfang August 2020 von Harness gekauft und bietet inzwischen auch eine CI-as-a-Service-Lösung unter drone.io an. Diese ist besonders für Test- und Open-Source-Projekte geeignet, da sie 5.000 Builds im Jahr für jedes Projekt anbietet und für Open- Source-Projekte kostenlos ist.
Drone verpackt jeden Pipeline-Step in einen eigenen Container und führt diesen auf konfigurierten Docker-Runnern aus. Eine beispielhafte Drone-CI-Pipeline, um ein simples Java-Projekt zu bauen, sieht man in Listing 3.
kind: pipeline
name: default
steps:
- name: test
image: maven:3-jdk-10
commands:
- mvn package -B
Dadurch, dass jeder Pipeline-Step in einem eigenen Docker-Container auf Docker-Runnern ausgeführt wird, ist eine Skalierung des CI-Service unglaublich einfach. Falls sich Builds aufstauen, kann ohne viel Aufwand ein neuer Runner hinzugefügt werden. Auch Drone CI bietet die Möglichkeit, Build-Dependencies zu cachen, allerdings nur über ein Plug-in. Drone-Plug-ins sind weitere Docker-Container, die eine Pipeline um zusätzliche Features erweitern. Beispielsweise durch einen Dependency-Cache.
Wie bereits eingangs erwähnt, ist Drone unglaublich einfach zu verwenden, wenn Docker-Infrastruktur zur Verfügung steht. Falls nicht, sollte man Drone allerdings auch nicht verwenden, da es voll und ganz dafür ausgelegt ist, Software in Containern zu bauen und auszuliefern.
Fazit
Leider gibt es keine Ex Machina, die jegliche Probleme löst, ohne fehl am Platz zu wirken. So überlege man sich, wie es wirken würde, wenn die Göttin Athene die Probleme von Friedrich Dürrenmatts „die Physiker“ lösen würde. Ähnlich verhält es sich auch bei unseren CI-Servern. Jeder der genannten Server hat eine Daseinsberechtigung, da er sich in seinen Bereichen besonders auszeichnet (siehe Tabelle 1).
CircleCI bietet eine leichtgewichtige, skalierbare Lösung, die dank des Caching und der performanten Maschinen schnelle Build- und Deployment-Zeiten gewährt. Durch die Integration mit Bitbucket und GitHub bekommt man fast eine Out-of-the-Box-Erfahrung. Der einzige Wermutstropfen mag hier der Preis sein, den man allerdings gegen die Betriebskosten für die entsprechende Infrastruktur rechnen sollte.
Falls bereits Container-Infrastruktur vorhanden ist, ist Drone CI ein schnell aufgesetzter, leichtgewichtiger CI-Service, der sich perfekt für Cloud-native Anwendungen eignet. Hier liegt allerdings auch der große Nachteil von Drone CI: Befindet man sich noch in einer Welt voller Legacy-Anwendungen und hat sich noch nie mit Containern beschäftigt, ist ein anderer CI-Service vermutlich besser geeignet.
GitLab bietet mit GitLab CI eine gut integrierte CI-Erweiterung für das eigene Repository. Vor allem wenn man kein externes Repository in GitLab CI integrieren muss, lohnt es sich das eigene Projekt mit GitLab CI auszuprobieren. Kaum ein anderes CI-System bietet so viele Features ohne Konfigurationsaufwand. Das einzige nennenswerte Manko sind die Kosten, falls zusätzliche Organisationsfeatures benötigt werden, und die enttäuschten Erwartungen, wenn Auto DevOps doch nicht für das eigene Projekt funktioniert.
Ebenfalls erschienen in der Java aktuell, Ausgabe 02/21: https://www.ijug.eu/de/java-aktuell/
Quellen
[1] https:/circleci.com/developer/orbs
Über die Autoren
Dominik Röschke
Software Developer
Seit 2017 unterstützt Dominik Röschke bei MATHEMA Kunden, wertschaffende und kundenorientierte Software zu schreiben und zu betreuen. Als Speaker und auf Barcamps liegt sein Augenmerk auf dem Austausch mit anderen Entwickelnden und dem Kennenlernen neuer Technologien.
Nikolas May
Senior Consultant, Team Lead
Seit 2008 beschäftigt sich Nikolas May bei MATHEMA mit der Entwicklung von Softwarelösungen für Unternehmen. Zusätzlich arbeitet er seit 2013 mit Java EE und unterstützt Kunden bei der Entwicklung und Integration von Enterprise-Anwendungen. Sein aktuelles Steckenpferd ist DevOps.