Git, Docker, Kubernetes & Co.: Wer macht eigentlich was?

Durchblick im DevOps-Dschungel: Von Git über Docker und Kubernetes bis hin zu Grafana

Git verwaltet den Code, Docker verpackt Anwendungen und Grafana zeigt Diagramme – so weit die Kurzfassung. Aber was bedeutet das eigentlich im Detail? Und wie wird aus dem Programmcode auf einem Entwicklerrechner eine Anwendung, die zuverlässig läuft und bei Problemen rechtzeitig auf sich aufmerksam macht?

Wer sich mit Softwareentwicklung oder dem Betrieb von IT-Systemen beschäftigt, stößt schnell auf Namen wie GitHub, Jenkins, Kubernetes oder Prometheus. Vielleicht hast du einige davon schon gehört oder sogar selbst genutzt. Trotzdem ist nicht immer klar, welches Werkzeug wofür zuständig ist. Manche übernehmen ähnliche Aufgaben, andere ergänzen sich. Gerade am Anfang fehlt häufig der Überblick, wie das alles zusammenpasst.

Genau diesen Überblick möchte ich mit diesem Beitrag schaffen. Denn um die Grundidee hinter diesen Werkzeugen zu verstehen, musst du weder ein „Nerd“ sein noch jahrelange Erfahrung in der IT mitbringen. Oft hilft schon ein verständliches Beispiel: Woher weiß ich, wer etwas am Programm geändert hat? Wie kann ich automatisch prüfen, ob danach noch alles funktioniert? Und woran erkenne ich, dass die Anwendung im laufenden Betrieb plötzlich langsamer wird? Für solche Fragen gibt es passende Werkzeuge?

Als Ausbilder für Fachinformatikerinnen und Fachinformatiker der Fachrichtungen Anwendungsentwicklung und Systemintegration vermittle ich diese Themen und ihre Zusammenhänge. Auch als IHK-Prüfer greife ich sie im Fachgespräch auf, wenn sie zum jeweiligen Projekt passen. Mir ist dabei wichtig, dass jemand erklären kann, welches Problem ein Werkzeug löst und warum es für die konkrete Aufgabe sinnvoll ist. Wer das verstanden hat, kann seinen Einsatz begründen und auch einschätzen, wann eine einfachere Lösung ausreichen würde.

Der Beitrag soll gleichzeitig als kompaktes Glossar und kleines Nachschlagewerk dienen. Was unterscheidet Git von GitHub? Wofür braucht man Kubernetes? Und warum werden Prometheus und Grafana so häufig gemeinsam eingesetzt?

Was bedeutet DevOps überhaupt?

Der Begriff DevOps setzt sich aus Development (Softwareentwicklung) und Operations (IT-Betrieb) zusammen. Dahinter steckt eine Idee, die sich gut nachvollziehen lässt: Die Menschen, die eine Anwendung entwickeln, und diejenigen, die sie später betreiben, arbeiten von Anfang an eng zusammen. Schließlich soll die Software nicht nur auf dem Rechner des Entwicklers funktionieren, sondern auch im Alltag zuverlässig ihren Zweck erfüllen. Stell dir vor, ein Team entwickelt eine neue Funktion für einen Onlineshop. Aus Sicht der Entwicklung funktioniert zunächst alles wie geplant. Im Betrieb stellen sich aber weitere Fragen: Was passiert, wenn viele Kunden gleichzeitig bestellen? Wie wird die neue Version eingespielt? Woran erkennt das Team einen Fehler? Und wie lässt sich bei Problemen wieder ein funktionierender Stand herstellen? Bei DevOps werden solche Fragen frühzeitig gemeinsam betrachtet. Auch die Software Qualitätssicherung gehört dazu, damit passende Prüfungen direkt mitgedacht werden.

Ein wichtiger Baustein sind dabei nachvollziehbare und wiederholbare Abläufe. Eine neue Version sollte möglichst nach einem festgelegten Verfahren erstellt, getestet und bereitgestellt werden. Wiederkehrende Schritte lassen sich automatisieren. Dadurch hängt weniger davon ab, ob jemand gerade an jede Kleinigkeit denkt oder eine Anleitung fehlerfrei von Hand abarbeitet.

Genauso wichtig ist die Rückmeldung aus dem laufenden Betrieb. Wird die Anwendung nach einer Änderung langsamer oder treten bestimmte Fehler häufiger auf, braucht das Entwicklungsteam diese Informationen. So lassen sich Ursachen untersuchen und Verbesserungen gezielt angehen. Die Arbeit endet also nicht mit der Veröffentlichung einer neuen Version: Was im Betrieb passiert, hilft dabei, die Software weiterzuentwickeln.

DevOps ist deshalb vor allem eine Frage der Zusammenarbeit und der gemeinsamen Verantwortung. Die passenden Werkzeuge können viel erleichtern: Sie führen Tests aus, unterstützen die Bereitstellung oder machen Probleme sichtbar. Ihren Nutzen entfalten sie aber erst, wenn das Team weiß, was es damit erreichen möchte und wer auf die Ergebnisse reagiert. Ziel ist, Probleme früher zu erkennen, vermeidbare Fehler zu reduzieren und Änderungen mit mehr Sicherheit zu veröffentlichen.

Git: Änderungen nachvollziehbar festhalten

Wer schon einmal Dateien mit Namen wie „Projekt_final“, „Projekt_final_neu“ oder „Projekt_final_wirklich_fertig“ angelegt hat, kennt das Problem: Man möchte verschiedene Arbeitsstände behalten, verliert dabei aber schnell den Überblick. Welcher Stand ist aktuell? Was wurde geändert? Und wie komme ich zurück zu der Version, in der noch alles funktioniert hat? Genau hier hilft Git. Git ist ein sogenanntes Versionsverwaltungssystem. Es hält nachvollziehbar fest, wie sich die Dateien eines Projekts verändern. Dadurch lässt sich später nachsehen, welche Änderungen vorgenommen wurden, von wem sie stammen und welche Erklärung dazu hinterlegt wurde. Bei Bedarf können frühere Stände wiederhergestellt oder einzelne Änderungen rückgängig gemacht werden.

Dafür werden zusammengehörige Änderungen als Commit festgehalten. Einen Commit kannst du dir wie einen bewusst gesetzten Zwischenstand vorstellen, den du mit einer kurzen Beschreibung versiehst – beispielsweise „Fehlermeldung bei falschem Passwort ergänzt“. Das normale Speichern einer Datei im Editor reicht dafür noch nicht aus: Zunächst speicherst du deine Arbeit, anschließend entscheidest du, welche Änderungen du in den nächsten Commit aufnehmen möchtest. So entsteht nach und nach eine verständliche Geschichte deines Projekts.

Besonders praktisch sind die sogenannten Branches, also Entwicklungszweige. Stell dir vor, die Anmeldung deiner Anwendung funktioniert und du möchtest eine neue Funktion ausprobieren. Dafür kannst du einen eigenen Branch anlegen. Dort bearbeitest du deine Änderungen, während der Hauptentwicklungszweig zunächst unverändert bleibt. Auch mehrere Personen können auf diese Weise parallel an unterschiedlichen Aufgaben arbeiten.

Ist die neue Funktion fertig und geprüft, können ihre Änderungen wieder in den Hauptentwicklungszweig übernommen werden. Dieses Zusammenführen nennt man Merge. Git kann dabei vieles automatisch erledigen. Haben aber beispielsweise zwei Personen dieselbe Textstelle unterschiedlich bearbeitet, kann ein Konflikt entstehen. Dann muss jemand entscheiden, wie die gemeinsame Fassung aussehen soll. Git zeigt die betroffenen Stellen an – welche Lösung fachlich richtig ist, beurteilt das Team.

Das Wort „verteilt“ bedeutet bei Git, dass die Versionsgeschichte nicht ausschließlich auf einem zentralen Server liegt. Bei einer vollständigen Kopie eines Projekts hast du auch dessen bisherige Historie auf deinem Rechner. Deshalb kannst du viele Aufgaben ohne Internet erledigen: Änderungen bearbeiten, Commits erstellen, Entwicklungszweige anlegen oder frühere Stände vergleichen. Erst wenn du Änderungen mit anderen austauschen möchtest, brauchst du eine Verbindung zum jeweiligen Gegenüber oder zu einer gemeinsamen Ablage.

Eine solche Projektablage heißt Repository. Für die Zusammenarbeit wird häufig zusätzlich ein entferntes Repository verwendet, auf das das Team zugreifen kann. Dafür braucht es nicht zwingend GitHub oder GitLab: Auch ein eigener Git-Server ist möglich. Was diese Plattformen darüber hinaus bieten, schauen wir uns im nächsten Abschnitt an.

Praxisbeispiel: Du möchtest die Anmeldung deiner Anwendung um verständlichere Fehlermeldungen ergänzen. Dafür legst du einen eigenen Branch an. In einem ersten Commit hältst du die neuen Meldungen fest, in einem weiteren die dazugehörigen Tests. Nach der gemeinsamen Prüfung werden die Änderungen übernommen. Fällt später ein Problem auf, lässt sich gezielt nachvollziehen, was an der Anmeldung verändert wurde. Genau diese Nachvollziehbarkeit macht Git für die Zusammenarbeit und die Fehlersuche so wertvoll.

GitHub und GitLab: Zusammenarbeit rund um den Code

GitHub und GitLab kannst du dir als gemeinsame Arbeitsplattformen für Softwareprojekte vorstellen. Die Grundlage bildet dabei Git, das Änderungen am Code nachvollziehbar festhält. GitHub und GitLab geben dem Projekt zusätzlich einen zentralen Platz, an dem das Team zusammenarbeiten kann. Dort liegt das sogenannte Repository: vereinfacht gesagt der Projektordner mit dem Quellcode und seiner Änderungshistorie.

Der Vorteil ist, dass sich dort auch die Arbeit rund um den Code organisieren lässt. Hat jemand einen Fehler entdeckt oder eine Idee für eine neue Funktion, kann dafür eine Aufgabe angelegt werden – ein sogenanntes Issue. Darin lässt sich beschreiben, was zu tun ist, wer sich darum kümmert und welche Fragen noch offen sind. So bleiben Informationen direkt beim Projekt und verteilen sich nicht auf E-Mails, Chatnachrichten und persönliche Notizzettel.

Besonders hilfreich finde ich die Möglichkeit, Änderungen gemeinsam anzuschauen, bevor sie in den Hauptentwicklungsstand übernommen werden. Stell dir vor, du hast die Anmeldung einer Anwendung erweitert. Deine Änderungen hast du zunächst in einem eigenen Entwicklungszweig, einem Branch, umgesetzt. Jetzt möchtest du sie dem Team vorstellen. Dazu eröffnest du auf GitHub einen Pull Request oder auf GitLab einen Merge Request. Die Bezeichnungen unterscheiden sich, die Grundidee ist dieselbe: „Meine Änderungen sind bereit. Schaut bitte einmal drüber, bevor wir sie übernehmen.“

Die anderen können dann genau sehen, welche Stellen du verändert hast. Sie können Fragen stellen, Hinweise geben oder Verbesserungen vorschlagen. Diese gemeinsame Durchsicht nennt man Code-Review. Gerade in der Ausbildung ist das eine schöne Gelegenheit, voneinander zu lernen: Warum wurde etwas auf diese Weise gelöst? Gibt es eine verständlichere Variante? Wurde ein möglicher Fehlerfall berücksichtigt? Ob vor der Übernahme zwingend jemand zustimmen muss, lässt sich über die Regeln des Projekts festlegen.

Beide Plattformen können außerdem wiederkehrende Arbeitsschritte automatisch ausführen. Bei GitHub übernimmt das GitHub Actions, bei GitLab GitLab CI/CD. Beispielsweise kann nach einer Änderung automatisch geprüft werden, ob sich die Anwendung noch erstellen lässt und die vorhandenen automatisierten Tests erfolgreich durchlaufen. Das Team bekommt dadurch früh eine Rückmeldung, ob die Änderung möglicherweise etwas beschädigt hat.

Welche Plattform besser passt, hängt deshalb vor allem davon ab, wie das Team arbeitet: Welche Werkzeuge sind schon vorhanden? Was soll automatisiert werden? Und möchte man die Plattform als Dienst nutzen oder selbst betreiben? Für den Einstieg ist mir vor allem wichtig, dass die Rollen klar sind: Git hält die Änderungen fest. GitHub und GitLab helfen dabei, gemeinsam an diesen Änderungen zu arbeiten und die Abläufe drumherum zu organisieren.

CI/CD: Änderungen automatisch bauen, prüfen und bereitstellen

Zwischen einer Änderung am Programmcode und einer neuen Version, die von den Nutzern verwendet werden kann, liegen mehrere Arbeitsschritte. Die Anwendung muss erstellt, geprüft und schließlich in der vorgesehenen Umgebung bereitgestellt werden. Damit diese Aufgaben nachvollziehbar und möglichst automatisch ablaufen, werden sie häufig in einer Pipeline organisiert. Du kannst dir eine Pipeline wie einen festgelegten Arbeitsablauf mit mehreren Stationen vorstellen. An einer Station wird aus dem Quellcode eine ausführbare Anwendung oder ein Softwarepaket erstellt. Diesen Vorgang nennt man Build. An weiteren Stationen laufen beispielsweise automatisierte Tests oder eine Codeanalyse. Sind die vorgesehenen Prüfungen erfolgreich, kann die Anwendung auf einer Testumgebung bereitgestellt werden. Schlägt eine wichtige Prüfung fehl, wird der weitere Ablauf entsprechend den eingerichteten Regeln gestoppt.

In diesem Zusammenhang begegnet dir häufig die Abkürzung CI/CD. Dahinter stehen eng miteinander verbundene Vorgehensweisen. Gerade bei „CD“ lohnt es sich, genauer hinzuschauen, denn das kann sowohl Continuous Delivery als auch Continuous Deployment bedeuten.

Continuous Integration (CI): Änderungen regelmäßig zusammenführen und prüfen

Wenn mehrere Personen an einer Anwendung arbeiten, müssen ihre Änderungen irgendwann zusammenpassen. Bei Continuous Integration werden Änderungen deshalb häufig und möglichst in kleinen Schritten in einen gemeinsamen Entwicklungsstand integriert. Automatisierte Builds und Tests liefern dazu früh eine Rückmeldung.

Stell dir vor, du veränderst die Anmeldung, während ein Kollege an der Benutzerverwaltung arbeitet. Für sich genommen können beide Änderungen funktionieren. Ob sie auch gemeinsam funktionieren, zeigt sich erst beim Zusammenführen und Prüfen. Je früher das passiert, desto leichter lässt sich ein auftretendes Problem meist eingrenzen. Für mich liegt darin ein wesentlicher Vorteil von CI: Das Team bekommt zeitnah Hinweise, solange die Änderungen noch überschaubar und gedanklich präsent sind.

Continuous Delivery: Eine veröffentlichungsfähige Version bereithalten

Continuous Delivery führt diesen Gedanken weiter. Die Software durchläuft einen wiederholbaren Ablauf, der sie erstellt, prüft und für eine Veröffentlichung vorbereitet. Ziel ist, eine Version bereitzuhalten, die bei Bedarf zuverlässig in den Produktivbetrieb übernommen werden kann – also in die Umgebung, mit der die tatsächlichen Nutzer arbeiten.

Die Veröffentlichung kann dabei weiterhin eine bewusste Entscheidung sein. Beispielsweise prüft das Team die Version zusätzlich auf einer Testumgebung und gibt sie anschließend frei. Die technischen Schritte sind weitgehend automatisiert, aber der Zeitpunkt der Veröffentlichung wird gezielt festgelegt.

Continuous Deployment: Erfolgreich geprüfte Änderungen automatisch veröffentlichen

Bei Continuous Deployment entfällt diese zusätzliche manuelle Freigabe vor jeder Veröffentlichung. Eine Änderung, die alle vorgesehenen automatisierten Prüfungen erfolgreich durchläuft, wird automatisch in den Produktivbetrieb übernommen.

Das setzt großes Vertrauen in die Prüfungen und den Bereitstellungsprozess voraus. Auch die Überwachung nach der Veröffentlichung und ein geeigneter Umgang mit Problemen müssen mitgedacht werden. Automatisierte Tests können schließlich nur die Fälle überprüfen, für die sie eingerichtet wurden.

Praxisbeispiel: Du ergänzt eine verständlichere Fehlermeldung bei der Anmeldung. Im Rahmen von CI wird die Änderung mit dem gemeinsamen Entwicklungsstand zusammengeführt und automatisch geprüft. Bei Continuous Delivery wird daraus eine veröffentlichungsfähige Version vorbereitet, deren Produktivsetzung noch freigegeben werden kann. Bei Continuous Deployment erfolgt auch dieser letzte Schritt automatisch, sobald alle vorgesehenen Prüfungen bestanden sind.

GitHub Actions

Wenn du an einer Anwendung arbeitest, wiederholen sich viele Aufgaben: Nach einer Änderung möchtest du beispielsweise prüfen, ob die automatisierten Tests noch erfolgreich durchlaufen und sich die Anwendung weiterhin fehlerfrei erstellen lässt. Diese Schritte jedes Mal von Hand anzustoßen, kostet Zeit – und im Alltag wird auch schnell einmal etwas vergessen. GitHub Actions hilft dir dabei, solche Abläufe direkt in deinem GitHub-Projekt zu automatisieren. Dazu legst du einen sogenannten Workflow an. Den kannst du dir wie einen Arbeitsplan vorstellen: Er beschreibt, wann etwas passieren soll und welche Schritte anschließend ausgeführt werden. Ein Auslöser kann beispielsweise sein, dass du neue Änderungen zu GitHub überträgst – das nennt man Push. Auch ein Pull Request, also eine Anfrage zur Übernahme von Änderungen, kann einen Workflow starten. Ebenso sind zeitgesteuerte Abläufe möglich, etwa ein Testlauf in jeder Nacht.

Dieser Arbeitsplan wird in einer Textdatei im Format YAML festgehalten. Die Dateien liegen im Projektverzeichnis unter .github/workflows/. Auch wenn das zunächst technisch klingt: Im Grunde stehen darin strukturierte Anweisungen wie „Hole den aktuellen Code, bereite die benötigte Umgebung vor und führe anschließend die Tests aus“. Weil die Dateien zum Projekt gehören, werden Änderungen an diesen Abläufen ebenfalls mit Git dokumentiert.

Die eigentliche Arbeit übernehmen sogenannte Runner. Das sind die Ausführungsumgebungen, auf denen die festgelegten Schritte abgearbeitet werden. Du kannst sie dir wie einen zusätzlichen Rechner vorstellen, der deinen Arbeitsplan ausführt. Ein Workflow beschreibt also die Aufgaben – der Runner erledigt sie. Damit lässt sich weit mehr als ein Testlauf automatisieren. Ein Workflow kann beispielsweise die Anwendung erstellen, sie als Container-Image verpacken oder auf einer Testumgebung bereitstellen. Welche Schritte sinnvoll sind und aufeinander folgen, legst du passend zu deinem Projekt fest.

Praxisbeispiel: Du hast die Anmeldung deiner Anwendung erweitert und eröffnest einen Pull Request. Daraufhin startet GitHub Actions automatisch die dafür eingerichteten Tests. Schlägt einer davon fehl, wird das Ergebnis beim Pull Request sichtbar. Du kannst dir die Ausgabe ansehen und die Ursache untersuchen, bevor die Änderung übernommen wird. Gerade im Team finde ich diese frühe Rückmeldung hilfreich: Alle Beteiligten können sehen, welche Prüfungen bereits erfolgreich waren und wo noch etwas geklärt werden muss.

GitLab CI/CD

Was GitHub mit GitHub Actions ermöglicht, bietet GitLab mit GitLab CI/CD: Du kannst wiederkehrende Aufgaben rund um deine Anwendung automatisieren und direkt mit deinem Projekt verbinden. Beispielsweise lässt sich nach einer Codeänderung automatisch prüfen, ob die Anwendung noch erstellt werden kann und die vorgesehenen Tests erfolgreich durchlaufen. Der gesamte Ablauf wird als Pipeline bezeichnet. Stell sie dir wie eine Reihe von Arbeitsstationen vor: Zuerst wird die Anwendung erstellt, anschließend geprüft und danach beispielsweise auf einer Testumgebung bereitgestellt. Jede Station hat eine bestimmte Aufgabe. Schlägt eine wichtige Prüfung fehl, können die nachfolgenden Schritte gestoppt werden. So lässt sich verhindern, dass eine als fehlerhaft erkannte Version einfach weitergereicht wird.

Den Arbeitsplan dafür hinterlegst du normalerweise in der Datei .gitlab-ci.yml im Hauptverzeichnis deines Projekts. Darin beschreibst du einzelne Aufgaben, sogenannte Jobs. Ein Job könnte beispielsweise die automatisierten Tests starten, ein anderer die Anwendung für die Veröffentlichung vorbereiten. Zusammengehörige Jobs lassen sich in Ablaufstufen, sogenannten Stages, organisieren – etwa „Erstellen“, „Testen“ und „Bereitstellen“. Jobs derselben Stufe können parallel laufen, sofern genügend Ausführungskapazität vorhanden ist.

Außerdem kannst du Regeln festlegen: Welche Aufgaben sollen bei jeder Änderung ausgeführt werden? Welche nur dann, wenn eine neue Version veröffentlicht werden soll? Und an welcher Stelle möchtest du eine manuelle Freigabe einbauen? Bei größeren Projekten lässt sich die Konfiguration auf mehrere Dateien verteilen oder um wiederverwendbare Vorlagen ergänzen. So muss nicht jedes Team denselben Ablauf von Grund auf neu beschreiben.

Die eigentliche Arbeit übernehmen GitLab Runner. Sie nehmen die Jobs entgegen und führen die darin beschriebenen Anweisungen in einer passenden Umgebung aus. Dafür müssen die benötigten Voraussetzungen vorhanden sein: Ein Testlauf braucht beispielsweise die passende Programmiersprache und die verwendeten Testwerkzeuge. Soll die Anwendung auf einen Server übertragen werden, werden zusätzlich eine erreichbare Zielumgebung und die erforderlichen Zugriffsrechte benötigt. Die Pipeline-Datei ist also der Arbeitsplan; Runner und Ausführungsumgebung sorgen dafür, dass er umgesetzt werden kann.

Praxisbeispiel: Du hast die Anmeldung deiner Anwendung überarbeitet und überträgst deine Änderungen zu GitLab. Die Pipeline erstellt zunächst die Anwendung und führt anschließend die automatisierten Tests aus. Sind diese erfolgreich, kann die neue Version auf einer Testumgebung bereitgestellt werden. Dort lässt sie sich zusätzlich ausprobieren und prüfen. Die Veröffentlichung im Produktivbetrieb kann weiterhin eine bewusste Freigabe erfordern. Gerade diese Verbindung aus automatischen Prüfungen und gezielten Entscheidungen finde ich hilfreich: Wiederkehrende Arbeit wird abgenommen, während das Team die Kontrolle über die Veröffentlichung behält.

Jenkins

Jenkins hilft dabei, wiederkehrende Aufgaben in der Softwareentwicklung automatisch auszuführen. Dazu gehören beispielsweise das Erstellen einer Anwendung, das Ausführen von Tests oder die Bereitstellung einer neuen Version. Du kannst dir Jenkins wie eine zentrale Arbeitsorganisation vorstellen: Es stößt die vorgesehenen Aufgaben an, koordiniert ihre Ausführung und zeigt anschließend, welche Schritte erfolgreich waren und wo Probleme aufgetreten sind. Im Unterschied zu den direkt in GitHub oder GitLab eingebauten Automatisierungsfunktionen ist Jenkins ein eigenständiger Automatisierungsserver. Damit ist zunächst eine Software gemeint, die auf einem geeigneten Rechner oder in einer entsprechenden Serverumgebung betrieben wird. Jenkins lässt sich mit verschiedenen Werkzeugen und Projektablagen verbinden. Das ist beispielsweise dann interessant, wenn in einem Unternehmen bereits unterschiedliche Systeme genutzt werden, die in einem gemeinsamen Ablauf zusammenarbeiten sollen.

Ein solcher Ablauf lässt sich als Pipeline in einer Datei namens Jenkinsfile beschreiben. Darin steht beispielsweise: „Hole den aktuellen Programmcode, erstelle die Anwendung, führe die Tests aus und stelle die geprüfte Version auf dem Testserver bereit.“ Das Jenkinsfile kann gemeinsam mit dem Anwendungscode in Git gespeichert werden. Dadurch bleibt nachvollziehbar, wer den Ablauf wann verändert hat. Auch Änderungen an der Automatisierung können so vom Team gemeinsam geprüft werden.

Viel von seiner Flexibilität erhält Jenkins durch Erweiterungen, sogenannte Plugins. Sie ermöglichen die Anbindung weiterer Werkzeuge und Dienste. So kann Jenkins beispielsweise mit einer Codeverwaltung zusammenarbeiten, Testergebnisse aufbereiten oder Benachrichtigungen versenden. Die einzelnen Aufgaben können außerdem auf zusätzlichen Ausführungsrechnern erledigt werden, den sogenannten Agents. Das ist praktisch, wenn unterschiedliche Aufgaben verschiedene Umgebungen benötigen oder mehrere Arbeiten parallel laufen sollen. Dieser Gestaltungsspielraum bringt allerdings auch Verantwortung mit sich. Wer Jenkins selbst betreibt, muss sich um Updates, die verwendeten Plugins, Zugriffsrechte und die Ausführungsumgebungen kümmern. Auch die Automatisierung braucht also Pflege, damit sie dauerhaft zuverlässig funktioniert. Bei der Auswahl würde ich deshalb immer mit betrachten, wer diese Aufgaben übernimmt und wie viel Aufwand das Team dafür einplanen kann.

Praxisbeispiel: Ein Team möchte seine Webanwendung nach jeder Änderung automatisch prüfen. Jenkins holt sich dafür den aktuellen Code, erstellt die Anwendung und startet die vorgesehenen Tests. Schlägt ein Test fehl, kann das Team die Ergebnisse und Protokolle einsehen und die Ursache untersuchen. Sind alle Prüfungen erfolgreich, kann die Pipeline die Anwendung anschließend auf einer Testumgebung bereitstellen. Der Vorteil liegt für mich vor allem darin, dass dieser festgelegte Ablauf wiederholbar ausgeführt wird und seine Ergebnisse für das Team nachvollziehbar bleiben.

Docker: Anwendungen reproduzierbar verpacken

„Bei mir funktioniert es!“ – dieser Satz beschreibt ein bekanntes Problem in der Softwareentwicklung. Auf dem Rechner des Entwicklers läuft eine Anwendung problemlos. Auf dem Testserver fehlt plötzlich eine benötigte Bibliothek oder es ist eine andere Version der Programmiersprache installiert. Der Code ist derselbe, aber die Umgebung unterscheidet sich. Genau hier hilft Docker: Es ermöglicht, eine Anwendung zusammen mit vielen ihrer benötigten Bestandteile in einem definierten Paket bereitzustellen.

Dieses Paket heißt Image. Darin stecken beispielsweise der Anwendungscode, benötigte Bibliotheken und die passende Laufzeitumgebung – also die Software, die zum Ausführen der Anwendung erforderlich ist. Du kannst dir das Image wie eine vorbereitete Vorlage vorstellen, aus der sich immer wieder dieselbe Ausgangsumgebung erzeugen lässt. Wie diese Vorlage entsteht, beschreibt eine Datei namens Dockerfile. Sie ist gewissermaßen das Rezept: Welche Grundlage verwenden wir? Welche zusätzlichen Pakete werden installiert? Wohin wird der Anwendungscode kopiert? Und mit welchem Befehl soll die Anwendung starten? Docker arbeitet diese Anweisungen beim Erstellen des Images ab.

Ein Container ist eine aus diesem Image erzeugte Instanz, in der die Anwendung ausgeführt werden kann. Der Unterschied lässt sich gut merken: Das Image ist die Vorlage, der Container die daraus erzeugte Umgebung. Aus demselben Image können mehrere Container entstehen, beispielsweise für verschiedene Testläufe. Jeder davon besitzt seine eigene beschreibbare Schicht. Daten, die dauerhaft erhalten bleiben sollen, etwa Datenbankinhalte, müssen deshalb gezielt außerhalb dieser austauschbaren Schicht gespeichert werden. Der große Vorteil ist, dass sich ein einmal erstelltes Image auf dem Entwicklungsrechner, in der Testumgebung und später im Produktivbetrieb verwenden lässt – vorausgesetzt, die jeweilige Umgebung ist dafür geeignet. Viele Unterschiede durch manuelle Installationen entfallen dadurch. Das macht die Bereitstellung einer Anwendung besser nachvollziehbar und wiederholbar.

Ganz verschwinden die Unterschiede zwischen den Umgebungen allerdings nicht. Eine Anwendung braucht möglicherweise weiterhin eine erreichbare Datenbank, passende Zugangsdaten oder bestimmte Netzwerkeinstellungen. Auch die Prozessorarchitektur kann eine Rolle spielen. Docker schafft also eine einheitlichere Grundlage, ersetzt aber weder die richtige Konfiguration noch die Prüfung unter den tatsächlichen Einsatzbedingungen.

Häufig kommt an dieser Stelle die Frage auf: Ist ein Container nicht einfach eine kleine virtuelle Maschine? Der Vergleich ist verständlich, technisch gibt es aber einen wichtigen Unterschied. Eine virtuelle Maschine betreibt ein eigenes Betriebssystem mit einem eigenen Kernel, also dem zentralen Betriebssystemkern. Container teilen sich dagegen den Kernel der Umgebung, in der sie laufen. Dadurch benötigen sie häufig weniger Ressourcen und können schnell gestartet werden. Auf manchen Systemen stellt eine virtuelle Maschine im Hintergrund erst die passende Umgebung für diese Container bereit. Container und virtuelle Maschinen lassen sich deshalb gut miteinander kombinieren. Beispielsweise kann auf einem Server eine virtuelle Maschine laufen, in der wiederum mehrere Container betrieben werden. Welche Aufteilung sinnvoll ist, hängt unter anderem von den Anforderungen an Betrieb, Sicherheit und Wartung ab.

Praxisbeispiel: Das Team hat die Anmeldung seiner Webanwendung erweitert. Die Pipeline erstellt daraus ein neues Image und stellt es zunächst in der Testumgebung bereit. Dort wird die Anwendung geprüft. Sind die vorgesehenen Prüfungen erfolgreich, wird genau dieselbe Image-Version für die Veröffentlichung verwendet. Für mich liegt darin ein wichtiger Vorteil: Die Anwendung wird zwischen Test und Veröffentlichung nicht noch einmal neu zusammengestellt. Die geprüfte Grundlage bleibt erhalten – auch wenn beispielsweise die Verbindung zur Datenbank für den Produktivbetrieb anders konfiguriert wird.

Kubernetes: Container über mehrere Systeme betreiben

Mit Docker lässt sich eine Anwendung in Containern bereitstellen. Doch was passiert, wenn viele solcher Container betrieben werden sollen – vielleicht sogar verteilt auf mehrere Server? Wer entscheidet, wo sie laufen? Was passiert bei einem Ausfall? Und wie wird eine neue Version eingespielt, während die Anwendung weiterhin genutzt wird? Bei diesen Aufgaben unterstützt Kubernetes.

Kubernetes verwaltet containerisierte Anwendungen in einem sogenannten Cluster. Damit ist eine Gruppe von Rechnern gemeint, die zusammenarbeiten. Das können physische Server oder virtuelle Maschinen sein. Du kannst dir Kubernetes dabei wie eine Betriebskoordination vorstellen: Es verteilt die vorgesehenen Anwendungen auf geeignete Rechner und überprüft regelmäßig, ob der tatsächliche Zustand noch zu den Vorgaben passt.

Die kleinste Einheit, die Kubernetes dabei bereitstellt und verwaltet, heißt Pod. In einem Pod läuft ein Container oder eine kleine Gruppe eng zusammengehöriger Container. Das könnte beispielsweise deine Webanwendung zusammen mit einem zusätzlichen Hilfsdienst sein. Die Container innerhalb eines Pods teilen sich bestimmte Ressourcen, etwa die Netzwerkumgebung, und werden gemeinsam auf einem Rechner untergebracht. Für den Einstieg reicht zunächst die Vorstellung: Ein Pod ist die gemeinsame Hülle, in der Kubernetes diese Container betreibt.

Besonders wichtig ist das Prinzip des gewünschten Zustands. Du beschreibst beispielsweise: „Von meiner Webanwendung sollen dauerhaft drei Exemplare laufen.“ Kubernetes versucht dann, diese Vorgabe einzuhalten. Fällt ein entsprechend verwalteter Pod aus, kann ein neuer als Ersatz angelegt werden. Fällt ein ganzer Rechner aus, können betroffene Anwendungen auf anderen geeigneten Rechnern neu gestartet werden – sofern dort genügend Ressourcen zur Verfügung stehen und die übrigen Voraussetzungen erfüllt sind. Auch bei Aktualisierungen hilft dieses Prinzip. Statt alle laufenden Exemplare einer Anwendung gleichzeitig zu beenden, kann Kubernetes sie schrittweise durch die neue Version ersetzen. Das nennt man ein Rolling Update. Während ein Teil bereits aktualisiert wird, bedienen andere Exemplare weiterhin Anfragen. Damit das zuverlässig funktioniert, muss unter anderem geprüft werden können, ob die neuen Exemplare tatsächlich bereit sind, Anfragen zu übernehmen. Eine unterbrechungsfreie Aktualisierung ergibt sich also nicht allein daraus, dass Kubernetes eingesetzt wird.

Ein weiterer Begriff, dem du dabei begegnen wirst, ist Skalierung. Gemeint ist, die verfügbare Kapazität an den Bedarf anzupassen. Wird deine Anwendung stärker genutzt, können beispielsweise zusätzliche Exemplare gestartet werden. Sinkt die Auslastung wieder, lässt sich deren Anzahl reduzieren. Das kann mit passenden Messwerten und Regeln automatisch erfolgen. Die dafür benötigte Rechenleistung muss allerdings vorhanden sein oder zusätzlich bereitgestellt werden können. Auch muss die Anwendung dafür geeignet sein, mit mehreren parallel laufenden Exemplaren zu arbeiten.

Praxisbeispiel: Ein Onlineshop erwartet während einer Rabattaktion deutlich mehr Besucher. Mit einer entsprechend eingerichteten automatischen Skalierung können zusätzliche Exemplare der Webanwendung gestartet werden, um mehr Anfragen zu bearbeiten. Fällt eines aus, sorgt Kubernetes für Ersatz. Eine langsame Datenbank oder einen Fehler im Bestellprozess behebt das allerdings nicht automatisch. Deshalb bleiben eine durchdachte Architektur, aussagekräftige Tests und die Überwachung der gesamten Anwendung wichtig.

Für mich ist Kubernetes deshalb vor allem dann interessant, wenn die Anforderungen an den Betrieb diesen zusätzlichen Aufwand rechtfertigen. Der Cluster selbst muss schließlich ebenfalls eingerichtet, abgesichert und gepflegt werden. Wer Docker nutzt, braucht also nicht automatisch auch Kubernetes. Für eine kleine Anwendung kann eine einfachere Betriebsumgebung völlig ausreichen. Entscheidend ist, welche Probleme tatsächlich gelöst werden sollen und ob das Team die gewählte Lösung zuverlässig betreiben kann.

Ansible: Infrastruktur nachvollziehbar verwalten

Wer schon einmal mehrere Server eingerichtet hat, kennt die wiederkehrenden Aufgaben: Software installieren, Benutzer anlegen, Konfigurationsdateien anpassen und Dienste starten. Auf einem einzelnen System lässt sich das noch gut von Hand erledigen. Bei mehreren Rechnern wird es schnell aufwendig. Außerdem kann es passieren, dass eine Einstellung vergessen oder auf einem Server etwas anders umgesetzt wird. Ansible hilft dabei, solche administrativen Aufgaben nachvollziehbar zu automatisieren. Dafür beschreibst du die erforderlichen Schritte in einem sogenannten Playbook. Du kannst dir das wie eine Arbeitsanweisung vorstellen, die Ansible für dich abarbeitet. Darin steht beispielsweise: „Installiere den Webserver, übertrage diese Konfigurationsdatei und stelle sicher, dass der Dienst läuft.“ Die Playbooks werden im Textformat YAML geschrieben. Dadurch lassen sie sich auch mit Git verwalten, gemeinsam prüfen und bei Bedarf anpassen.

Welche Rechner bearbeitet werden sollen, wird in einer Übersicht festgelegt, dem sogenannten Inventory. Dort können Systeme auch zu Gruppen zusammengefasst werden, beispielsweise Entwicklungsserver und Testserver. So lässt sich dieselbe Grundkonfiguration auf mehrere Rechner anwenden, während einzelne Einstellungen je nach Umgebung unterschiedlich bleiben dürfen.

Für die einzelnen Aufgaben verwendet Ansible Module. Das sind spezialisierte Bausteine, etwa zum Installieren von Softwarepaketen, zum Kopieren von Dateien oder zum Verwalten von Diensten. Du musst damit nicht jeden Arbeitsschritt selbst als Folge einzelner Systembefehle formulieren, sondern kannst häufig direkt beschreiben, welches Ergebnis du erreichen möchtest.

Besonders hilfreich finde ich dabei ein Prinzip mit einem zunächst etwas sperrigen Namen: Idempotenz. Gemeint ist, dass eine erneute Ausführung den bereits erreichten Zustand nicht unnötig verändert. Soll ein bestimmtes Softwarepaket installiert sein und ist es bereits vorhanden, muss nichts weiter passieren. Entspricht eine Konfigurationsdatei schon der vorgesehenen Fassung, braucht sie nicht erneut geändert zu werden. Stell es dir wie eine Checkliste vor: Ansible prüft bei entsprechend aufgebauten Aufgaben, welche Punkte bereits erfüllt sind, und erledigt die noch offenen Arbeiten. Dadurch kann ein Playbook wiederholt ausgeführt werden, beispielsweise nachdem ein neuer Server hinzugekommen ist oder die gewünschte Konfiguration angepasst wurde.

Dieses Verhalten ist allerdings nicht automatisch bei jeder Aufgabe gegeben. Wenn du in einem Playbook einen eigenen Shell-Befehl hinterlegst, der eine Zeile an eine Datei anhängt, könnte diese Zeile bei jedem Durchlauf erneut ergänzt werden. Deshalb kommt es darauf an, geeignete Module zu verwenden und die Aufgaben sorgfältig zu formulieren. Auch ein automatisierter Ablauf muss geprüft werden, bevor er auf wichtigen Systemen eingesetzt wird.

Praxisbeispiel: Für eine Webanwendung werden ein Entwicklungs- und ein Testserver benötigt. Beide sollen denselben Webserver und eine vergleichbare Grundkonfiguration erhalten. Statt die Einrichtung zweimal von Hand durchzuführen, beschreibt das Team sie in einem Ansible-Playbook. Unterschiede, etwa die jeweiligen Servernamen, werden gezielt als Werte für die passende Umgebung hinterlegt. Kommt später ein weiterer Testserver hinzu, lässt sich dieselbe Arbeitsanweisung wiederverwenden. Das spart Arbeit und macht zugleich nachvollziehbar, wie die Systeme eingerichtet wurden.

SonarQube: Codequalität frühzeitig prüfen

Eine Anwendung kann auf den ersten Blick funktionieren und trotzdem problematische Stellen im Programmcode enthalten. Vielleicht wurde ein möglicher Fehlerfall übersehen, eine Funktion ist unnötig kompliziert geworden oder dieselbe Logik wurde an mehreren Stellen kopiert. Solche Dinge fallen beim Ausprobieren nicht unbedingt auf, können spätere Änderungen aber erschweren. SonarQube hilft dabei, den Quellcode automatisiert auf solche Auffälligkeiten zu untersuchen. Dabei kommt unter anderem die sogenannte statische Codeanalyse zum Einsatz. „Statisch“ bedeutet hier, dass der Code untersucht wird, ohne die Anwendung dafür wie ein Nutzer auszuführen. SonarQube prüft ihn anhand von Regeln und sucht nach auffälligen Mustern. Du kannst dir das ein wenig wie eine Rechtschreib- und Stilprüfung für Programmcode vorstellen: Sie liefert Hinweise auf mögliche Probleme und erklärt, warum eine Stelle genauer betrachtet werden sollte.

Die Ergebnisse können unterschiedliche Bereiche betreffen. Manche Hinweise deuten auf mögliche Programmfehler hin, andere auf Sicherheitsprobleme oder auf Code, der schwer verständlich und aufwendig zu pflegen ist. Gerade diese Wartbarkeit wird leicht unterschätzt. Schließlich soll sich auch jemand im Code zurechtfinden, der die betreffende Funktion nicht selbst geschrieben hat – oder der nach einem Jahr wieder daran arbeiten muss. Welche Prüfungen konkret möglich sind, hängt unter anderem von der Programmiersprache sowie vom verwendeten SonarQube-Produkt und dessen Edition ab. Außerdem müssen die Ergebnisse sinnvoll eingeordnet werden: Ein Hinweis ist ein Anlass, genauer hinzusehen. Er bedeutet nicht automatisch, dass die Anwendung an dieser Stelle tatsächlich einen Fehler verursacht.

Damit das Team aus den Ergebnissen klare Entscheidungen ableiten kann, gibt es sogenannte Quality Gates. Dahinter stehen festgelegte Qualitätsbedingungen, die erfüllt sein sollen, bevor eine Änderung weitergegeben wird. Stell dir das wie einen vereinbarten Prüfpunkt vor: Sind die Anforderungen erfüllt, kann der nächste Schritt folgen. Sind sie nicht erfüllt, muss das Team zunächst nacharbeiten oder den Befund klären.

SonarQube lässt sich dafür in eine automatisierte Pipeline einbinden. Bei entsprechender Einrichtung kann ein nicht bestandenes Quality Gate verhindern, dass die Pipeline mit der Veröffentlichung fortfährt oder eine Änderung in den Hauptentwicklungszweig übernommen wird. Diese Wirkung muss allerdings bewusst eingerichtet werden – allein durch die Installation von SonarQube wird noch keine Veröffentlichung blockiert.

Praxisbeispiel: Ein Team erweitert die Anmeldung seiner Webanwendung. Die automatisierten Funktionstests laufen erfolgreich durch. SonarQube weist jedoch darauf hin, dass eine neue Funktion sehr komplex geworden ist und sich dadurch nur schwer nachvollziehen lässt. Das Team schaut sich die Stelle an und teilt sie in kleinere, verständlichere Funktionen auf. Die Anmeldung verhält sich anschließend weiterhin wie vorgesehen, ihr Code lässt sich aber leichter prüfen und später ändern.

Aus Sicht der Qualitätssicherung finde ich diese zusätzliche Rückmeldung wertvoll. Ein unauffälliges Analyseergebnis ist allerdings kein Beweis dafür, dass die Anwendung insgesamt fehlerfrei oder sicher ist. Ob sie die fachlichen Anforderungen erfüllt, verständlich bedienbar ist oder bei vielen gleichzeitigen Zugriffen zuverlässig arbeitet, muss mit weiteren geeigneten Prüfungen untersucht werden. SonarQube ergänzt deshalb die Tests und das gemeinsame Code-Review um einen zusätzlichen Blick auf den Quellcode.

Prometheus und Grafana: Messwerte sammeln und sichtbar machen

Eine Anwendung ist veröffentlicht und der Server läuft – aber läuft auch alles so, wie es soll? Werden Anfragen zügig beantwortet? Reicht der verfügbare Arbeitsspeicher aus? Und nimmt die Auslastung vielleicht schon seit Tagen zu, ohne dass es bisher jemand bemerkt hat? Prometheus und Grafana helfen dabei, solche Entwicklungen sichtbar zu machen. Die beiden Werkzeuge werden häufig gemeinsam eingesetzt, übernehmen aber unterschiedliche Aufgaben. Prometheus sammelt und speichert Messwerte. Dazu gehören beispielsweise die CPU-Auslastung, der belegte Arbeitsspeicher oder die Anzahl der Anfragen an eine Anwendung. Jeder Messwert wird zusammen mit einem Zeitpunkt gespeichert. So entstehen sogenannte Zeitreihen. Du siehst dadurch nicht nur, wie stark ein Server gerade beschäftigt ist, sondern kannst auch nachvollziehen, wie sich seine Auslastung über Minuten, Stunden oder Tage verändert hat.

Damit Prometheus an diese Informationen kommt, fragt es in regelmäßigen Abständen dafür vorgesehene Schnittstellen ab. Manche Anwendungen stellen ihre Messwerte selbst bereit. In anderen Fällen übernehmen kleine Hilfsprogramme diese Aufgabe, die sogenannten Exporter. Du kannst sie dir wie Messfühler vorstellen: Sie lesen Informationen über ein System aus und stellen sie in einer Form bereit, die Prometheus verarbeiten kann.

Mit der Abfragesprache PromQL lassen sich die gesammelten Daten anschließend gezielt auswerten. Damit kann man beispielsweise untersuchen, wie hoch die durchschnittliche Auslastung in einem bestimmten Zeitraum war oder wie sich die Fehlerrate einer Anwendung entwickelt hat. Für den Einstieg musst du diese Sprache noch nicht beherrschen. Wichtig ist zunächst die Idee: Aus vielen einzelnen Messwerten werden Informationen, mit denen sich der Zustand eines Systems besser beurteilen lässt.

Grafana macht diese Informationen anschaulich. Es greift auf angebundene Datenquellen wie Prometheus zu und stellt deren Daten in sogenannten Dashboards dar. Das sind Übersichtsseiten mit Diagrammen, Kennzahlen und Statusanzeigen. Statt lange Zahlenlisten durchzugehen, kannst du beispielsweise anhand einer Kurve erkennen, wann die CPU-Auslastung gestiegen ist oder ob der freie Speicherplatz langsam knapp wird. 

Die Aufgabenverteilung lässt sich deshalb gut merken: Prometheus sammelt die Messwerte und bewahrt ihren zeitlichen Verlauf auf. Grafana bereitet sie so auf, dass du Veränderungen und Auffälligkeiten leichter erkennst. Grafana kann dabei auch andere Datenquellen nutzen; Prometheus ist eine häufig eingesetzte Kombination. Natürlich möchte niemand den ganzen Tag auf ein Dashboard schauen. Deshalb lassen sich Alarmregeln einrichten. Sie legen fest, unter welchen Bedingungen eine Benachrichtigung ausgelöst werden soll – beispielsweise, wenn der freie Speicherplatz einen bestimmten Wert unterschreitet oder eine Anwendung über längere Zeit ungewöhnlich langsam antwortet. Dafür können unter anderem Prometheus-Regeln zusammen mit dem Alertmanager oder die Alarmierungsfunktionen von Grafana verwendet werden. Dabei lohnt es sich, die Regeln bewusst zu gestalten. Eine kurze Auslastungsspitze ist nicht unbedingt ein Problem. Bleibt die Belastung dagegen längere Zeit hoch, kann eine Meldung sinnvoll sein. Ebenso sollte klar sein, welches Werkzeug für welche Benachrichtigung zuständig ist, damit dieselbe Störung nicht mehrfach gemeldet wird. Für mich ist ein Alarm dann hilfreich, wenn er auf etwas hinweist, bei dem ich tatsächlich nachsehen oder handeln sollte.

Ich nutze Prometheus zusammen mit Grafana selbst zur Überwachung meines Homeservers und meiner externen Server. Für jedes überwachte System habe ich ein eigenes Dashboard eingerichtet. Dort kann ich mir mit einem Blick einen ersten Eindruck verschaffen: Wie stark ist der Server ausgelastet? Ist noch genügend Speicherplatz vorhanden? Gibt es auffällige Veränderungen? Das hilft mir, den Zustand der Systeme einzuschätzen, ohne mich dafür auf jedem Server einzeln anmelden zu müssen. Die eingerichteten Alarmregeln informieren mich per E-Mail, wenn etwas nicht ganz rund läuft. So muss ich die Dashboards nicht ständig geöffnet haben. Kommt eine Meldung, kann ich mir den zeitlichen Verlauf genauer ansehen und gezielt nach der Ursache suchen.

Praxisbeispiel: Nach der Veröffentlichung einer neuen Version dauert die Anmeldung an einer Webanwendung plötzlich deutlich länger. Wenn die entsprechenden Antwortzeiten erfasst werden, wird die Veränderung im Grafana-Dashboard sichtbar. Ein passend eingerichteter Alarm informiert das Team, sobald die festgelegte Grenze ausreichend lange überschritten wird. Anschließend lässt sich untersuchen, ob beispielsweise die Serverauslastung ebenfalls gestiegen ist oder eine andere Ursache dahintersteckt. Das Monitoring liefert damit einen wichtigen Hinweis – die eigentliche Ursachenanalyse folgt danach.

ELK-Stack: Protokolle zentral durchsuchen

Ein Dashboard zeigt dir, dass die Fehlerrate einer Anwendung gestiegen ist. Aber was ist genau passiert? Welche Anfrage ist fehlgeschlagen, und welche Fehlermeldung wurde dabei ausgegeben? Für solche Fragen lohnt sich ein Blick in die Protokolle, auch Logs genannt. Darin halten Anwendungen und Systeme Ereignisse fest – beispielsweise einen fehlgeschlagenen Verbindungsaufbau oder einen Fehler beim Verarbeiten einer Bestellung. Auf einem einzelnen Server lassen sich solche Protokolle noch recht gut direkt durchsuchen. Wenn eine Anwendung aber aus mehreren Diensten besteht, die auf unterschiedlichen Rechnern laufen, wird das schnell unübersichtlich. Der ELK-Stack hilft dabei, diese Informationen zentral zusammenzuführen und durchsuchbar zu machen. „Stack“ bezeichnet hier eine Kombination von Werkzeugen, die zusammenarbeiten.

Die Abkürzung ELK steht für drei Bestandteile mit unterschiedlichen Aufgaben:

  • Elasticsearch speichert die Daten und bereitet sie so auf, dass sie gezielt durchsucht werden können. Diese Aufbereitung nennt man Indexierung. Ähnlich wie ein Stichwortverzeichnis in einem Buch erleichtert sie es, passende Einträge zu finden, ohne jedes Protokoll einzeln durchzugehen.
  • Logstash nimmt Daten aus verschiedenen Quellen entgegen und verarbeitet sie für die weitere Nutzung. Es kann beispielsweise aus einer Protokollzeile den Zeitpunkt, die Art der Meldung und den Namen des betroffenen Dienstes herauslösen. Dadurch lassen sich diese Angaben später gezielt filtern und vergleichen.
  • Kibana ist die Oberfläche, über die du die Daten erkunden kannst. Dort suchst du nach Fehlermeldungen, grenzt einen Zeitraum ein oder erstellst Diagramme und Übersichten.

Du kannst dir die Zusammenarbeit also so vorstellen: Logstash bereitet eingehende Protokolldaten auf, Elasticsearch macht sie durchsuchbar, und Kibana gibt dir die passende Oberfläche, um damit zu arbeiten. Heute begegnet dir dafür häufig die Bezeichnung Elastic Stack, weil inzwischen weitere Komponenten dazugehören. Ein Beispiel ist Elastic Agent, das Daten von Systemen erfassen und weiterleiten kann. Welche Bausteine benötigt werden, hängt vom jeweiligen Aufbau ab. Logstash muss deshalb nicht zwangsläufig in jeder Installation eingesetzt werden.

Besonders hilfreich finde ich das Zusammenspiel mit dem Monitoring: Messwerte zeigen häufig, dass etwas auffällig ist; Protokolle liefern weitere Hinweise darauf, was passiert ist. Eine Kurve kann beispielsweise zeigen, dass seit 14 Uhr mehr Anfragen fehlschlagen. In den Logs lässt sich anschließend nachsehen, welche Fehlermeldungen in diesem Zeitraum aufgetreten sind und welche Dienste betroffen waren. Die Ursache steht damit nicht automatisch fest, aber die Suche lässt sich deutlich gezielter angehen.

Praxisbeispiel: Nach einer Aktualisierung melden mehrere Nutzer, dass sie sich nicht mehr anmelden können. Das Monitoring bestätigt eine erhöhte Fehlerrate. In Kibana grenzt das Team die Suche auf den Zeitraum seit der Veröffentlichung und den Anmeldedienst ein. Dabei fallen wiederholte Meldungen auf, dass keine Verbindung zur Datenbank hergestellt werden konnte. Das ist ein konkreter Ansatzpunkt: Nun lässt sich prüfen, ob sich etwa die Verbindungseinstellungen geändert haben oder die Datenbank nicht erreichbar ist.

Bei der zentralen Protokollierung lohnt sich allerdings etwas Planung. Viele Dienste können in kurzer Zeit große Datenmengen erzeugen. Deshalb sollte das Team festlegen, welche Informationen wirklich gebraucht werden und wie lange sie aufbewahrt werden sollen. Auch sensible Inhalte wie Passwörter oder Zugriffsschlüssel gehören nicht in die Protokolle. Ziel ist eine hilfreiche Grundlage für die Fehlersuche, die übersichtlich bleibt und deren Speicherbedarf zum tatsächlichen Nutzen passt.

Die genannten DevOps-Tools im Überblick

Welches Werkzeug übernimmt welche Aufgabe?
Werkzeug Schwerpunkt Typischer Einsatz
Git Versionsverwaltung Änderungen dokumentieren und zusammenführen
GitHub / GitLab Codehosting und Zusammenarbeit Repositories, Reviews und Aufgaben verwalten
GitHub Actions / GitLab CI/CD Integrierte Automatisierung Builds, Tests und Deployments ausführen
Jenkins CI/CD Automatisierte Pipelines organisieren
Docker Containerisierung Images bauen und Container ausführen
Kubernetes Containerverwaltung Anwendungen im Cluster betreiben
Ansible Konfigurationsautomatisierung Systeme wiederholbar konfigurieren
SonarQube Statische Codeanalyse Codequalität prüfen und Quality Gates auswerten
Prometheus Metriken und Alarmregeln Messwerte erfassen und auswerten
Grafana Visualisierung und Alerting Dashboards und Benachrichtigungen erstellen
Elastic Stack / ELK Datensuche und Logauswertung Protokolle zentral analysieren

Praxisbeispiel: Von der Codeänderung bis zum Betrieb

Angenommen, ein Team erweitert die Anmeldung einer Webanwendung. Ein möglicher Ablauf sieht so aus:

  1. Änderung entwickeln: Der neue Code entsteht in einem Git-Branch und wird mit nachvollziehbaren Commits dokumentiert.
  2. Prüfung anstoßen: Ein Pull Request auf GitHub oder Merge Request auf GitLab startet die eingerichtete Pipeline.
  3. Qualität untersuchen: Automatisierte Tests prüfen das Verhalten. SonarQube ergänzt die Prüfung durch statische Analyse. Teammitglieder führen ein Code-Review durch.
  4. Anwendung verpacken: Die Pipeline erstellt ein versioniertes Container-Image und legt es in einer Registry ab.
  5. Testumgebung nutzen: Das Image wird bereitgestellt. Dort folgen beispielsweise Integrations- und Systemtests. Ansible kann die benötigte Umgebung vorbereiten.
  6. Veröffentlichung durchführen: Nach den vorgesehenen Prüfungen und Freigaben wird dieselbe geprüfte Image-Version produktiv eingesetzt, beispielsweise in Kubernetes.
  7. Betrieb beobachten: Prometheus erfasst Messwerte, Grafana macht Veränderungen sichtbar, und zentrale Logs unterstützen die Ursachenanalyse bei Problemen.

Das ist lediglich eine mögliche Kombination von vielen verschiedenen Varianten. Die verwendeten Werkzeuge können natürlich auch anders aussehen – entscheidend sind nachvollziehbare Übergänge und verlässliche Rückmeldungen.

Ich hoffe, ich konnte hier den Nicht-Nerds einen kleinen Überblick geben, was 2026 im Softwareentwicklungsprozess bzw. als DevOps eingesetzt wird, sofern einem diese Tools/Frameworks zur Verfügung stehen. Natürlich muss man dann immer auch noch abwägen, ob man dabei nicht mit Kanonen auf Spatzen schießt. 🙂

Quellen & weiterführende Links

Du möchtest mehr erfahren? Hier findest du die offiziellen Dokumentationen zum Weiterlesen und Ausprobieren.

Meine Inspiration: Das Video Every Major DevOps Tool Explained in 11 Minutes! hat mich zu diesem Beitrag angeregt.

Versionsverwaltung & Zusammenarbeit
Git-Handbuch  ·  GitHub

Automatisierung & CI/CD
GitHub Actions  ·  GitLab CI/CD  ·  Jenkins

Container & Systemkonfiguration
Docker  ·  Kubernetes  ·  Ansible

Codequalität
SonarQube: Quality Gates

Monitoring & Protokollauswertung
Prometheus  ·  Grafana  ·  Elastic Stack

Schreibe einen Kommentar

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