Stateless CI-Server mit Concourse

2 Kommentare

Continuous Integration ist einer der wichtigsten Bestandteile der agilen Software-Entwicklung und in den meisten Teams so selbstverständlich wie Versionskontrolle geworden. Trotz der alten und bewährten Werkzeuge wie Jenkins, Bamboo oder TeamCity, ist es immer noch eine große Herausforderung, die Komplexität einer Continuous-Integration-Umgebung zu beherrschen. Das Entwicklungsteam aus der Software-Schmiede Pivotal hat sich vorgenommen, ein neues CI-System mit einer modernen Architektur und zukunftssicheren Technologien aufzubauen. Was vor drei Jahren mit einem eher unbekannten Open-Source-Projekt startete, ist nun eine reife Technologie mit stabilen Release-Zyklen geworden. In diesem Blog stelle ich die Kernkonzepte und die Architektur von Concourse CI vor.

Warum brauchen wir ein neues CI-System?

Einer der wichtigsten Gründe warum ein neues Werkzeug vorteilhaft sein kann, ist die Unabhängigkeit von alten Anforderungen, die über viele Jahre gewachsen sind und immer noch erfüllt werden müssen. Solche Systeme sind meistens stabil und zuverlässig, haben jedoch die Schwierigkeit, sich an die schnelle technologische Entwicklung anzupassen. Die Komplexität eines Build-Servers hat über die letzten Jahre enorm zugenommen, so dass immer mehr Plugins und Konfigurationen verwaltet werden müssen. Weiterhin besteht oft eine Lücke zwischen den Builds auf einem Entwicklungsrechner und auf einem CI-System. Denn die Konfiguration des CI-Servers muss mit jedem Entwicklungsrechner übereinstimmen. Durch zunehmende Komplexität der Build- und Auslieferungsprozesse hat sich auch der Aufbau der Jobs verändert:  von simplen Build- und Test-Jobs bis zu komplexen und langläufigen Pipelines, die den gesamten Prozess von Compile bis Deployment über mehrere Stages wie Integrationstest, Quality-Gates und Blue/Green-Deployments abdecken. Diese Komplexität hemmt eine schnelle Adaption neuer Technologien in bestehenden CI-Systemen, da es sehr oft umständlich ist alte Strukturen aufzubrechen und etwas Neues einzuführen.

Kernkonzepte von Concourse

In Concourse CI wurden neue Technologien und moderne Architekturen bereits beim Design berücksichtigt, wodurch das Plugin-Management und Konfiguration des CI-Systems vereinfacht wird. Das System basiert auf vier grundlegenden Konzepten: Resources, Tasks, Jobs und Pipelines.

Unter einer Resource versteht man eine Entität, die drei Merkmale erfüllen muss:

  • Eine Resource ist versionierbar.
  • Eine Resource wird aus einem anderen System ausgelesen.
  • Eine Resource wird in ein anderes System geschrieben.

Durch diese Abstraktion können viele verschiedene Resources für das CI-System definiert werden, beispielsweise Git Repository, S3 Bucket, Docker Image, NPM Paket oder sogar die Zeit selbst. Resources werden als Inputs oder Outputs für Tasks und Jobs verwendet. Entscheidend ist, dass die Konfiguration für das Lesen und das Schreiben der Resources nicht innerhalb des CI-Systems definiert wird. Concourse stellt viele Adapter für Resources zur Verfügung und lässt dabei die Freiheit eigene Resource-Adapter zu schreiben.

Ein Task ist eine Ausführung eines bestimmten Build-Skripts. Es entspricht dem bekannten Konzept von Steps innerhalb eines Jobs in Jenkins – mit dem entscheidenen Unterschied, dass alle Tasks immer in einem Docker-Container ausgeführt werden. Dadurch wird sichergestellt, dass alle benötigten Abhängigkeiten und Konfigurationen gekapselt und vom CI-System entkoppelt sind. Das Konzept wurde in ähnlicher Form von meinem Kollegen Tobias Flohre in einem Blog-Artikel diskutiert.

Ein Job besteht aus mehreren Tasks und definierten Inputs und Outputs, welche wiederum Resources sind. Wenn sich die Input-Resources ändern, wird der Job ausgelöst, und es werden neue Output-Resources erstellt. Ein Job innerhalb des CI-Systems ist dadurch stateless. In Concourse ist es gar nicht möglich Abhängigkeiten zum CI-Server innerhalb der Jobs aufzubauen. Dadurch kann ein Job auf einem beliebigen Concourse-System ausgeführt werden und wird exakt gleiche Ergebnisse produzieren.

Resources, Tasks und Jobs lassen sich zu einer Pipeline orchestrieren. Änderungen der Input-Resources lösen bestimmte Jobs aus, die wiederum neue Output-Resources erzeugen und dabei andere Jobs auslösen. Mit dieser Verkettung lassen sich beliebig komplexe Strukturen für Auslieferungsprozesse abbilden, wobei Jobs zusätzlich parallelisiert oder aggregiert werden können.

Eine weitere Stärke von Concourse ist die Benutzeroberfläche, in der die Pipelines, Jobs und Resources minimalistisch dargestellt werden. Aufgrund der zunehmenden Komplexität der Build-Pipelines ist es in anderen Systemen oft schwierig, die wesentliche Information möglichst schnell zu finden. Als Beispiel kann die Pipeline von Concourse selbst betrachtet werden.

Abbildung 1: CI-Pipeline von Concourse CI

Die Übersicht bietet eine gesamte Darstellung der Pipeline auf einen Blick und ermöglicht eine schnelle Navigation zu den Details eines Jobs oder Resources. Die grünen Blöcke zeigen die jeweiligen Jobs und ihre Input- und Output-Resourcen und werden je nach Dauer der Jobs skaliert dargestellt. Mit diesem Konzept wird der Aufbau und der Betrieb der Pipeline offen und präzise kommuniziert. Das Wissen über den Auslieferungsprozess sowohl innerhalb eines Entwicklungsteams als auch zwischen verschiedenen Teams kann besser verteilt werden, ohne sich in den Details zu verlieren.

Architektur

In Concourse haben viele Begriffe einen Bezug zur Luftfahrt, wodurch die Architektur und die Zusammensetzung der einzelnen Komponenten besser veranschaulicht wird. Das System ist in mehrere Module unterteilt: ATC, TSA, Workers und Fly. Die Architektur sieht wie folgt aus:


Abbildung 2: Concourse-CI-Architektur

Air Traffic Control (ATC) ist die zentrale Instanz des Systems und bietet eine Benutzeroberfläche zur Darstellung und eine REST API für das Management der Pipelines und deren Ausführung. Transport Security Administration (TSA) ist eine Schnittstelle zwischen ATC und den Workern, wobei die Worker lediglich registriert und an ATC weiter geleitet werden. Die Worker sind unabhängige und einheitliche Server, die als Container für die Ausführung der Jobs verwendet werden. Sie sind stateless, benötigen keine zusätzliche Konfiguration und registrieren sich am TSA als mögliche Laufzeitumgebung für die Pipeline. Wann, wo und wie ein Worker zum Einsatz kommt, wird von ATC gesteuert. Für das optimale Scheduling der Worker melden diese ihren Zustand periodisch an ATC.

Um die Pipelines innerhalb des ATC zu verwalten wird das CLI-Tool Fly verwendet. In Concourse gibt es keine Möglichkeit, eine Pipeline über die Benutzeroberfläche zu erstellen; alle Konfigurationen müssen mit Fly durchgeführt werden. Was im ersten Moment nach einem fehlendem Feature klingt, ist eine bewusste Designentscheidung. Denn oft sind die Entwickler dazu geneigt den Zustand des CI-Systems manuell in der Benutzeroberfläche zu verändern. Diese Änderungen werden über längeren Zeitraum oft vergessen oder nicht dokumentiert. Mit Fly lassen sich Pipelines ebenfalls lokal ausführen, was die Entwicklung der Pipeline stark vereinfacht und die gleiche Funktionalität auf einem CI-Server garantiert. In weiteren Blog-Artikeln werde ich auf die Architektur und das Pipeline-Management in Concourse detailliert eingehen und mögliche Einsatzszenarien diskutieren.

Fazit

Mit Concourse werden viele alte Probleme eines CI-Servers angegangen und elegant gelöst. Die Jobs werden in Docker-Containern ausgeführt, was den Konfigurationsaufwand des CI-Servers reduziert und die Reproduzierbarkeit der Builds garantiert. Die Abstraktion durch Resources, Tasks und Jobs bietet Spielraum für viele Technologien und Orchestrierung komplexer Build-Prozesse. Entwicklungsteams haben die Autonomie über ihre Pipeline und können das Wissen über den Auslieferungsprozess mit einer aufgeräumten Oberfläche besser verteilen.

Komplexe Konfiguration eines CI-Servers ist oft ein Hindernis für kleine Teams, um mit Continuous Integration zu starten. Ein CI-Server wird oft von mehreren Teams mit verschiedenen Plugins und Umgebungsvariablen überladen – das reduziert die Flexibilität der Entwicklung und erhöht den Wartungsaufwand. Concourse CI versucht dieses Problem durch Abstraktion und Container-Einstatz zu vermeiden. Letztlich muss jedes Team für sich selbst entscheiden, ob der Wechsel zu einem neuen CI-System Mehrwert bringt. In der Regel ist die Technologie allein nicht der entscheidende Faktor für den Erfolg eines Projekts.

Alexander Melnyk

Als Cloud Native Developer realisiert Alexander skalierbare und robuste Systeme.
Seine Schwerpunkte sind Cloud-Technologien, Serverless Architekturen, DevOps und Data Science.
Er ist ein Deep Worker, Machine Learner und Python Whisperer.

Artikel von Alexander Melnyk

API management with Kong

Architektur

API-Management mit Kong

Kommentare

  • Benjamin Jung

    11. Dezember 2017 von Benjamin Jung

    Sehr schöner Artikel!
    Mich persönlich hat Concourse CI vom ersten Augenblick an begeistert und ich denke, wenn man den Einstieg (die Lernkurve ist hier für manche etwa sehr steil, da eben alles per CLI [fly] konfiguriert werden muss) geschafft hat, ist die weitere Nutzung sehr effizient.

    Ich habe mittlerweile eigene Concourse Resources in Go, Node.js und mit Bash-Scripten geschrieben, da es so einfach ist. Die Dokumentation der Plugin-Schnittstellen von anderen CI/CD-Systemen hingegen schreckt mich immer wieder ab…. 🙄

    Dass Concourse es erlaubt, einzelne Tasks zu Testzwecken komplett losgelöst von bereits deployten Pipelines auszuführen („fly execute“) halte ich noch für erwähnenswert, da das meiner Meinung nach ein weiteres einzigartiges Feature darstellt.

    • Alexander Melnyk

      13. Dezember 2017 von Alexander Melnyk

      Hi Bejamin,

      danke für dein Beitrag. Ich bin von Concourse auch sehr überzeugt und werde die von dir erwähnten und weitere Features (fly hijack) im kommenden Blog Post genauer beschreiben.

      Gruß
      Alexander

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.