O-Neko: Deployments von Entwicklungsständen mit Docker und Kubernetes

O-Neko ist eine Software, die in Kubernetes läuft und die es ermöglicht, Entwicklungszustände von Software basierend auf Docker-Images im Cluster bereitzustellen und zu überprüfen.

Oneko User Interface
Oneko User Interface (Bild: subshell/CC BY)

Wir stellen euch eine neue Lösung vor, die wir, das Team Weasel, in einer Vielzahl von Lab Days innerhalb der letzten 1,5 Jahre entwickelt haben. Wer es eilig hat und trotzdem alles wissen möchte: Im tl;dr am Ende haben wir eine Zusammenfassung.

Der Kampf um die Produktivität

Montagvormittag, am 18. Dezember 2017 um halb 11: Wie in jeder zweiten Woche kurz vor Sprintbeginn trafen wir uns im Team zu unserer Retro, um den vergangenen Sprint Revue passieren zu lassen.  

Zu dieser Zeit hatten wir sehr intensiv an vielen neuen Features und Verbesserungen für den Sophora MobileClient gearbeitet. Um unsere Arbeit gut zu parallelisieren, hatten wir für jede Aufgabe einen eigenen Entwicklungs-Branch im Code genutzt.

Obwohl dieses Vorgehen mittlerweile ein Standard in der Industrie ist, war es bei uns bis dahin nicht üblich gewesen und in seiner konsequenten Umsetzung neu für alle Projektbeteiligten.

Während Branches uns Entwicklern die Arbeit erleichterten, sorgten sie zu der Zeit teilweise für Frust bei unseren Testern und Projektmanagern. Der Grund dafür waren die Unzulänglichkeiten unserer statischen Testinfrastruktur, in der wir für jede weiterentwickelte Major-Version sowie für die kommende Version einen Testserver haben.

Jeder dieser Server besteht aus Sophora-Servern, einer Demoausspielung und den meisten unserer Tools, also auch dem MobileClient. Erst wenige Wochen vorher hatten wir Sophora 2.5 released und verfügten somit über Testserver für die Versionen 2.5, 2.4, 2.3 und die nächste Version 3.0.

Die Entwicklungsstände unserer Software, die automatisch auf den Testservern installiert werden, ermitteln sich aus der Haupt-Codebasis jedes Projekts. Entwicklungsstände für Features, die auf eigenen Branches entwickelt wurden, wurden somit auf keinem Testserver installiert.

Für uns Entwickler, die eine eingerichtete Entwicklungsumgebung auf dem eigenen Computer hatten, war das kein Problem, da jeder Entwicklungsstand im Handumdrehen kompiliert und gestartet werden konnte.

Wollte aber ein Projektmanager kontrollieren, wie weit ein Feature bereits funktionierte oder wie das UI einer Entwicklung aussah, musste er entweder auf das Wohlwollen und Präsentationstalent eines Entwicklers hoffen oder versuchen, eine eigene Entwicklungsumgebung einzurichten.

Das Ganze entwickelte sich zu einer Diskussion zwischen Entwicklern und Projektmanagern, bei der wir als Entwickler natürlich nicht darauf verzichten wollten, Branches zu nutzen, weil sie uns produktiver machten. Auf der anderen Seite wollten die Projektmanager Features unabhängig von uns möglichst schnell auf den Testservern sehen, um selber produktiv zu sein.

Es ging also darum, die jeweiligen Bedürfnisse nach Produktivität gegeneinander abzuwägen, denn sowohl Entwickler als auch Projektmanager hatten plausible Argumente für ihre jeweilige Haltung.

So begannen wir, während unserer Retrospektiven nach einem neuen Weg zu suchen. Die Messlatte war hoch, denn es musste eine Lösung gefunden werden, mit der alle glücklich werden konnten.

Der Kampf um die beste Lösung

Die Inspiration für die problemlösende Idee kam durch Zufall von einem unserer Kunden. Um Weiterentwicklungen an seiner Website testen zu können (die ebenfalls auf Branches entwickelt wurde), ist dort ein kleines selbstgebasteltes Programm im Einsatz, welches mithilfe von Docker-Containern auf einem Server die Ausspielungen zu allen Branches bereitstellt.

Damit das funktioniert, ist ein Konglomerat an unterschiedlichsten Tools notwendig. Weil die Container ausschließlich auf einer einzigen, dedizierten Maschine ausgeführt werden, ist diese so intensiv mit Arbeitsspeicher und Rechenleistung ausgestattet, wie Muskeln in einem Fitnessstudio zu finden sind: Übertrieben viel.

An dieser Stelle sollen nicht alle Details der Vorgänge erläutert werden. Aber diese Lösung umfasst eine selbstgebastelte, puristische Weboberfläche, die es ermöglicht, rudimentäre Einstellungen an der Ausspielung vorzunehmen (Produktiv- oder Testsystem usw.) und die Container ggf. zu stoppen. Zu diesem Programm ist jedoch anzumerken, dass es sehr spezifisch für die zugehörige Website ist und daher nicht ohne weiteres für andere Projekte, wie den Sophora MobileClient, benutzt werden kann.

Wollte man eine analoge Lösung, müsste man also selbst ein entsprechendes Projekt aufsetzen. Für uns klang das nach einem gangbaren Weg. Mit erfinderischem Leichtsinn und überzeugter Naivität traten wir also während der besagten Retrospektive an das Projektmanagement-Team heran und erläuterten unsere Idee, eine Anwendung nach diesem Vorbild zu schaffen, die die Stände der Software aus Entwicklungsbranches ausführt, sodass alle jederzeit Zugriff darauf haben.

Die Idee kam gut an, bis auf ein kleines Detail: Die Entwicklung war mit Kosten verbunden. Außerdem hatten wir bereits viele Projekte mit Terminfristen eingeplant.

Bei uns machte sich leichtes Unverständnis breit: Wie sollten wir ein Problem lösen, ohne dass es Zeit und Geld kosten würde? Glücklicherweise waren unser Erfindergeist und Pragmatismus noch nicht ganz am Ende, so dass wir inmitten dieser schwarz-weiß-Diskussion eine kleine Grauzone entdeckten und uns zunutze machten.

Bei subshell dürfen wir pro Person und Sprint einen Tag als sogenannten Lab Day nutzen und an diesem Tag (oder eben 8 Stunden verteilt über den gesamten Sprint) an Projekten arbeiten, die nicht regulär auf dem Projektplan stehen, sondern beispielsweise dazu dienen, uns weiterzubilden, Wunsch-Funktionen in unsere Software einzubauen oder neue Technologien in Verbindung mit Sophora zu evaluieren.

Beim Team Weasel herrschte schnell Konsens, dass wir unsere eigene Branch-Ausspielung als Lab Day-Projekt umsetzen wollten.

Rückblickend hatte die Deklaration unserer Idee als Lab Day Projekt sogar viele Vorteile: Keiner konnte uns sagen, was und wie wir es machen sollen, was es können oder wie es aussehen und heißen musste.

Diese plötzliche Anarchie spiegelte sich dann auch direkt in unserer ersten Entscheidung wider: Wir legten fest, wie unser neues Haustier heißen würde.

Die Taufe des Kindes

Es war der 2. Januar 2018 und wie saßen in der ersten Retro im neuen Jahr. Nachdem wir nun eine grobe Idee hatten und das Projekt nach unseren eigenen Vorgaben umsetzen konnten, war uns schnell klar, was das konkrete Ziel sein sollte: Etwas wie unser Vorbildsystem, nur größer, besser, flexibler, professioneller, moderner und „Cloud Native“. Außerdem eigenständig und nicht abhängig von der Laune eines Build-Systems, das wir nicht unter Kontrolle hatten.

Passend zum damals (und jetzt 1,5 Jahre später immer noch) aufblühenden Trendprojekt Kubernetes setzten wir uns zum Ziel, eine flexible Anwendung zu schaffen, mit der in einem Kubernetes-Cluster ein beliebiger Entwicklungsstand einer Software gestartet werden konnte.

Wir begannen damit, ein grobes Lastenheft zu spezifizieren, in dem wir unsere Ziele aufzeichneten:

  • Von jedem Branch des MobileClient sollte ein Docker-Image gebaut werden.
  • Wir mussten ein Kubernetes-Cluster bei uns installieren.
  • Es sollte eine neue Anwendung geben.
  • Diese Anwendung sollte als Docker-Container im Kubernetes-Cluster laufen.
  • Sie sollte eine Übersicht aller MobileClients zeigen können, sowie URLs zu jedem der MobileClients.
  • Beim Löschen eines Branches sollten automatisch die zugehörigen Container gestoppt werden.

Das Ganze sah überschaubar aus, wenn auch mit einer sehr steilen Lernkurve dekoriert. Kubernetes kannten zu dem Zeitpunkt nicht alle aus unserem Team, manche nur aus Benutzersicht. Mit der Administration und Einrichtung eines Clusters kannte sich keiner von uns aus – aber die Phantasie, dort das nötige Know-how für ein paradiesisches Entwicklerleben zu finden, trieb uns an.

Der letzte Teil unserer Neujahrs-Retro bestand darin, das Projekt zu taufen. Somit saßen wir wie kommende Eltern grübelnd da und versuchten, einen Namen zu finden, der sowohl modern und interessant klang als auch die nötige Ernsthaftigkeit und am besten noch eine passende metaphorische Bedeutung mit sich trug.

Nach vielen Ideen, in Anlehnung an „Kubernetes“ (was aus dem griechischen Wort „Steuermann“ abgeleitet ist) suchten wir nach griechischen Worten, die etwas mit Branches, Zweigen, Abzweigungen, Versionen und ähnlichen Begriffen zu tun hatten. Keines davon traf den Nagel so richtig auf den Kopf, und gerade als uns die Kreativität so langsam auszugehen drohte, entstand die Idee, das Projekt „Tzatziki“ zu taufen. (Inwieweit die nahende Mittagspause etwas mit dem Geistesblitz zu tun hatte, bleibt an der Stelle offen...)

Wir akzeptierten die Taufe (Spoiler: Bei dem Namen ist es langfristig nicht geblieben), machten uns erst an die Mittagspause und dann an die Arbeit und legten Tickets an, mit welchen wir in den folgenden Monaten Freude und Hass gleichermaßen neu entdecken würden.

K(r)ampf mit dem Kubernetescluster

Bevor es an das Entwickeln der eigentlichen Anwendung ging, mussten wir zunächst ein Kubernetescluster aufsetzen.

Uns war früh klar, dass der Betrieb dieses Clusters aufwändig werden würde und langfristig ein gehostetes Cluster eine gute Alternative sein würde. Entsprechend ließ sich auch die Einstellung, mit der wir die Sache angingen, beschreiben: Es muss funktionieren, aber nicht perfekt sein. Sicherheit ist ein Feature und keine Voraussetzung.

Mit dieser Mentalität konnten wir eine ganze Reihe an Problemen wie Zugriffskontrolle, Berechtigungen oder Verteilung der Knoten auf mehrere Hardware-Server entspannt vernachlässigen und uns zunächst um das Wesentliche kümmern.

Die Installation eines Basis-Kubernetes-Clusters ist erstaunlicherweise sehr simpel.

Man kann aus einer Reihe an Möglichkeiten wählen, und wir entschieden uns dafür, das Tool kubeadm zu nutzen. Nachdem man einige Grundeinstellungen auf den Servern vorgenommen hat (z.B. Swap deaktivieren, kubeadm und kubelet installieren) führt man auf dem angehenden Primary-Knoten kubeadm init aus und wartet den Output ab.

Praktischerweise wird nach einer erfolgreichen Installation direkt der Befehl ausgegeben, den man auf den zukünftigen Replica-Knoten ausführen muss, damit diese alles installieren und dem Cluster beitreten. In unserem ersten Setup haben wir uns in Bescheidenheit geübt und lediglich ein Cluster aus zwei Knoten eingerichtet (später sind es dann sechs geworden).

Im Anschluss mussten wir dann noch einige Konfigurationen (ein „Pod-Netzwerk“ bsp.) installieren, damit die Entitäten miteinander kommunizieren konnten. Dann waren wir so weit, dass wir Docker-Container deployen konnten.

Freude! …oder? Nicht ganz: Damit wir zumindest halbwegs konkret mit dem Cluster arbeiten konnten, galt es noch zwei Probleme zu lösen bzw. Features in Kubernetes zu installieren. Wir waren an einem Punkt angelangt, wo wir Software deployen konnten, allerdings gab es keine Möglichkeit, von außen an sie heranzukommen oder Daten langfristig zu persistieren. Das waren aber zwei Voraussetzungen für unsere neue Anwendung.

Für beide Probleme gibt es in Kubernetes bereits Lösungen, die man „ganz einfach“ nachinstallieren kann („Persistent Volumes“ und „Ingresses“ mit jeweiligen „Controllern“). Wir hatten ziemlich zu kämpfen und haben die konkreten Stolpersteine zum Glück inzwischen verdrängt. Die Zahl von knapp 5 Arbeitstagen geloggter Zeit auf den entsprechenden Tickets zeigt aber, dass es dauerte, bis wir alles verstanden und eingerichtet hatten.

An dieser Stelle ein Tipp für alle, die es mal selber probieren wollen: Schreibt Schritt für Schritt mit, was ihr gemacht habt. Wir haben das Cluster in den ersten Monaten gute 5-mal komplett löschen und neu aufsetzen müssen. Hätten wir uns nicht notiert, was es zu tun gab, und die entsprechende Konfiguration in einem Git-Repository gespeichert, wäre das Ganze noch aufwändiger geworden.

Tzatziki im Rampenlicht

Als es an das Programmieren der neuen Anwendung ging, stellten wir uns im Rückblick auf unsere Retro Fragen wie: Welche Ziele haben wir? Welches Problem soll die neue Software für uns lösen? Für wen schreiben wir diese Software überhaupt und wie soll ihre Bedienung aussehen?

Neben den Entrepreneur-Ambitionen, die uns packten und die uns motivierten, ein möglichst „hippes“ Stück Software zu produzieren, mussten wir zunächst auf den Boden der Tatsachen zurückkommen und realistische Ziele definieren.

Unser wichtigstes Ziel war, dass mit unserer Software das Starten einer Anwendung auch für nichttechnische Projektbeteiligte möglichst ein One-Klick-Kinderspiel sein sollte. Die Voraussetzung dafür war allerdings, dass ein Entwickler im Vorfeld Einstellungen vorgenommen hatte, die für das Ausführen des Projekts relevant waren.

Gleichzeitig wollten wir eine möglichst einfache Anwendung schaffen. Damit wollten wir verhindern, uns an bestimmte APIs und Versionen von Kubernetes zu binden oder Tzatziki spezielle Dateiformate beibringen zu müssen. Es sollte eher als Mittler agieren, mit dessen Hilfe alle Features von Kubernetes genutzt werden konnten.

Um diese relativ einfache Anwendung erreichen zu können, entschieden wir uns dafür, für die Konfiguration eines Projekts in Tzatziki die nativen Kubernetes .yaml-Dateien zu verwenden. Sprich: Ein Entwickler definiert in einem .yaml-Template, wie die Software in Kubernetes ausgeführt werden kann und ein beliebiger anderer Mensch muss dann nur noch einen Knopf betätigen, um eine zu einem Branch gehörende Version dieser Software zum Laufen zu bringen.

Tzatziki führt somit nur noch die vorprogrammierten Befehle des Menschen aus, ohne selbst Details über die Software, die es da startet, kennen zu müssen.

Jetzt habe ich in den vergangenen Absätzen bereits häufig das Wort „Projekt“ verwendet. Unser neues Programm sollte „Projekte“ verwalten. In unserem ursprünglichen Anwendungsfall war das der Sophora MobileClient.

Unsere Kreativität war mit der Namensfindung noch nicht erschöpft, und so war aus den knappen Anforderungen sogar eine erste Skizze entstanden:

O-Neko: Scribble
O-Neko: Scribble (Bild: subshell/CC BY)

Hier sieht man eine Menüleiste, oben mit den Buttons „Registries“ und „Projects“ sowie eine Ansicht mit zwei Kästen, die jeweils ein Projekt repräsentieren sollen. In dem einen Projekt-Kasten sind wiederum weitere Kästen und Buttons angedeutet. Die weiteren Kästen sollen Versionen des jeweiligen Projekts visualisieren, die jeweils gestartet oder gestoppt werden können.

Jetzt sind schon mehrfach die Begriffe „Branch“ und „Version“ gefallen. Tzatziki musste also git beherrschen, damit es wusste, welche Branches überhaupt vorhanden waren und zu jedem Branch dann das richtige Docker-Image finden. Das widersprach allerdings unserem Ziel, Tzatziki zu einer simplen Anwendung zu machen.

Deshalb überlegten wir uns eine pragmatische Lösung: Die Docker-Images werden bei uns ohnehin automatisiert erstellt und mit dem Namen des Branches getaggt. Somit gibt es zu jedem Branch ein Tag.

Tzatziki listet nun einfach die Tags auf, die es zu einem Docker Image gibt und diese können dann gestartet werden. Das führt auf Dauer zwar zu einer gigantischen Liste an Tags, die es zu einem Image gibt, ist aber erstens nicht schlimm und lässt sich zweitens durch Aufräumen in der Docker-Registry wieder beheben.

Grundsätzlich werden unter einem Projekt verschiedene .yaml-Dateien zusammengefasst, die dann gemeinsam ins Cluster befördert werden.

Diese Dateien sind Templates, die Tzatziki zur Laufzeit mit den richtigen Variablen füllt. Besonders wichtig ist das Tag des Docker-Images, da dieser ja von Version zu Version unterschiedlich ist. Das Templating ermöglicht nebenher mit etwas Programmieraufwand noch andere schöne Features, wie das Überschreiben und Anpassen von Konfigurationen für einzelne Versionen.

So kann auch ein Branch, in dem ein neues Feature, welches auch eine neue Konfiguration erfordert, ganz einfach mit ein paar Anpassungen an der entsprechenden Projektversion innerhalb von Tzatziki ausgeführt werden.

Technologiestack

Als wir mit Tzatziki angefangen haben, war Spring Boot 2 gerade relativ frisch und der Reactive-Ansatz war der ganz hippe Stoff, von dem wir natürlich auch mal kosten wollten. Somit baute Tzatziki in seiner ersten Version auf folgenden Technologiebausteinen auf:

Rückblickend war das Arbeiten mit Spring Boot 2 ein riesengroßer Aufwand, da wir den Reactive Web Ansatz komplett durchgezogen hatten und dadurch nicht nur vieles umdenken mussten, sondern auch viele Dinge nicht so funktionierten, wie man sie bisher gemacht hatte.

Das bereitete uns zu Beginn einige Kopfschmerzen. Angular hatten wir gewählt, da wir damit bereits andere Projekte umsetzten (bsp. den MobileClient).

Fast forward zu heute: Inzwischen sind wir natürlich technologisch etwas vorangeschritten und nutzen jetzt zumindest Java 11 und Angular 8.

6 Monate nach Projektbeginn

Nach über sechsmonatiger Arbeit an dem Projekt hatten wir eine erste funktionierende Version der Software.

Der Funktionsumfang war durchaus ausbaufähig und die Stabilität war allerhöchstens bei zwei Neunen, allerdings konnten wir unser ursprüngliches Problem damit bereits relativ solide lösen.

Tzatziki benötigte jedoch noch eine Menge manuelle Unterstützung und war auch noch nicht am Ende seiner Entwicklung.

Derweil fanden wir teamintern Gefallen an der Idee, das Projekt mittelfristig open source zu stellen, da wir sicher waren, dass das Projekt auch Probleme anderer Personen würde lösen können – das sahen auch unsere Chefs so.

Wir wurden deshalb vor ein neues, altes Problem gestellt: Der Name. Es gab Bedenken, ob die Bezeichnung für einen griechischen Quark mit Knoblauch und Gurken ein passender Name für Software war.

Nach erneutem Hin und Her kamen wir auf einen neuen Namen: O-Neko.

Im Fachjargon der Hafenindustrie werden die Containerkräne Katzen genannt. Neko ist das japanische Wort für „Katze“ und „O-“ wird im Japanischen vor Worte herangestellt, wenn man etwas besonders unterstreichen will.

Metaphorisch passte der Name somit sogar perfekt zu Software in Containern und Kubernetes als Steuermann, der diese orchestriert. Und da Katzen darüber hinaus ein tolles Internetphänomen sind (und der Name zumindest nicht schlechter war als Tzatziki), sind wir damit sogar bei unseren Chefs durchgekommen.

Der Umbenennung folgte auch eine Reihe von Re-Designs unserer Benutzeroberfläche. In der folgenden Bildergalerie sind einige Screenshots von Tzatziki/O-Neko, auf denen man gut erkennen kann, wie sich das Projekt im Laufe der Zeit verändert hat.

O-Neko im zeitlichen Wandel

Wir beginnen mit unserer allerersten vorzeigbaren Version. Im ersten Bild ist unser erster Login-Screen zu sehen. Ganz analog zum Kubernetes-Logo und zum Gurkensalat haben wir uns für den Querschnitt einer Gurke als Logo entschieden. (Nein, das soll kein Gehirn darstellen – Tzatziki sollte simpel und nicht allwissend sein!).

Tzatziki: Home - Erste Version
Tzatziki: Home - Erste Version (Bild: subshell/CC BY)

Nach dem Einloggen wurde man vom folgenden Screen begrüßt. Die Projektverwaltung fand in diesem Zustand nur über die „Home“ Seite statt. Docker-Registries, von denen die Projekte bezogen werden, sowie Benutzer hatten einen eigenen Unterpunkt im Menü. Rechts zeigte ein Activity Log, was alles in Tzatziki vor sich ging.

An dieser Stelle ist zu erwähnen, dass das Bild nicht ganz vollständig ist. Man konnte innerhalb der „Sophora MobileClient“-Kachel noch einige aufklappbare Untermenüs ganz nach dem Vorbild der Skizze von vorhin sehen. Beim Erstellen der Screenshots für diesen Artikel ist jedoch aufgefallen, dass die Anbindung an die Docker Registries in der alten Version nicht länger funktioniert und dementsprechend wäre ein vollständiger Screenshot nicht ohne erheblichen Aufwand zu haben gewesen.

Tzatziki: Home - Erste Version
Tzatziki: Home - Erste Version (Bild: subshell/CC BY)

Einige Monate später kamen wir dann doch dazu, eine verbesserte Version des Dashboards zu entwickeln.

Man sieht auf der Home-Seite nunmehr noch die aktuell laufenden Instanzen einer Software (hier in dem Beispiel läuft eine Instanz des MobileClient). Sofern die Software eine aufrufbare Weboberfläche bereitstellt, kann diese über das Link-Symbol in der Zeile direkt geöffnet werden.

Re-Deploy und Stop-Buttons ermöglichen es, die Instanz neu zu starten oder dauerhaft zu stoppen. Das Activity Log verbirgt sich mittlerweile einklappbar hinter dem Button auf der rechten Seite.

Tzatziki: Home
Tzatziki: Home (Bild: subshell/CC BY)

Die Projektverwaltung ist in eine eigene Ansicht verschoben worden. Hier konnten Projekte angelegt und bearbeitet werden. Außerdem gelangte man so zu einer vollständigen Liste der Versionen einer Software (hier satte 519!), welche man von dort aus mit einem Klick starten konnte (was man auch dann schafft, wenn man keine Ahnung von Technik hat).

Tzatziki: Projekte
Tzatziki: Projekte (Bild: subshell/CC BY)

An dieser Stelle sind wir nun am Ende von Tzatziki angekommen und es folgt das erste O-Neko-Design. Das Layout ist ähnlich, die Farben und das Logo sind völlig anders.

O-Neko: Log-in  - Erste Version
O-Neko: Log-in - Erste Version (Bild: subshell/CC BY)

Dieses erste O-Neko-Design überzeugte uns noch nicht. Das Logo sah nicht aus wie eine Katze und die Farben wirkten auch nur in den ersten zwei Minuten ansprechend.

Die folgenden Screenshots vom Redesign zeigen die aktuelle Version von O-Neko. Der Login Screen wirkt nun etwas fröhlicher (farblich zumindest).

O-Neko: Log-in
O-Neko: Log-in (Bild: subshell/CC BY)

Die Liste der aktiven Deployments ist auch etwas übersichtlicher, da sie farblich stärker abgetrennt ist.

O-Neko: Home
O-Neko: Home (Bild: subshell/CC BY)

Die Projektansicht hat seit Tzatziki-Tagen nur die Farbveränderungen mitmachen müssen.

O-Neko: Projekte
O-Neko: Projekte (Bild: subshell/CC BY)

Das folgende Bild zeigt die Auflistung aller Versionen des Sophora MobileClient in unserer Docker-Registry.

O-Neko: Projektversionen
O-Neko: Projektversionen (Bild: subshell/CC BY)

O-Neko heute

Kommen wir zum Hier und Jetzt: Heute hilft uns O-Neko täglich bei der Arbeit. Es erleichtert unseren Projektmanagern die Vorschau von unserem Work-In-Progress und ermöglicht es unseren Testern, bestimmte Versionen einer Software auszuprobieren.

Dank O-Neko kann das Projekt- und Produktmanagement-Team viel früher Entwicklungsstände anschauen und Feedback dazu geben. Das tut der Produktentwicklung richtig gut. O-Neko ist für uns unverzichtbar geworden.
—Nils Hergert, subshell, Member of the Executive Board

Der Funktionsumfang von O-Neko umfasst das Anlegen von Projekten, welche durch ein Docker-Image sowie eine Reihe von Kubernetes-.yaml Dateien definiert werden. Die .yaml werden mit Template-Variablen versehen, welche von O-Neko ausgefüllt werden, wenn eine Version in das Kubernetescluster deployed werden soll.

Die Versionen eines Projekts ermitteln sich ausschließlich aus den Tags, die zu dem jeweiligen Docker-Image verfügbar sind. Eine Verbindung zu Git ist also nicht notwendig.

An einem Projekt können neben Templates noch weitere Einstellungen vorgenommen werden. So kann bestimmt werden, ob neue Tags zu dem Image automatisch gestartet werden sollen, ob neue Versionen eines Tags automatisch aktualisiert werden sollen und wie lange eine Version des Projekts aktiv sein soll, bevor sie automatisch heruntergefahren wird (oder ob sie für alle Ewigkeit aktiv sein soll).

Die Einstellungen einer Version lassen sich vollständig überschreiben und erweitern, sodass auch komplizierte Neuentwicklungen in einem Branch nicht auf O-Neko verzichten müssen.

Projekte lassen sich aus beliebigen Docker-Registries beziehen. Sollten also mehrere Registries relevant sein, ist das kein Problem, denn die verfügbaren Registries lassen sich dynamisch im Frontend bestimmen.

Aktive Deployments werden auf der Startseite komfortabel angezeigt und sind mit Links zu den Weboberflächen der jeweiligen Programme versehen.

Durch eine Benutzerverwaltung mit einem einfachen Rollensystem kann bestimmt werden, wer Projekte modifizieren oder nur sehen und starten darf.

Wir finden, der Aufwand hat sich gelohnt und nutzen O-Neko weiterhin für den Sophora Mobile Client und unsere Team-Weasel-Projekte.

Die subshell-Kollegen haben sich bisher noch nicht richtig an unser neues Tool herangetraut – aber wir werden sie noch rumbekommen!

O-Neko morgen

Spannend bleibt es bei O-Neko auch in Zukunft: Schon jetzt haben wir weitere praktische Features in der Pipeline – und Visionen für eine Weiterentwicklung unseres Projektes.

Project Meshes

Dieses Feature soll es ermöglichen, aus diversen Projekten innerhalb von O-Neko mehrere auszuwählen und diese gemeinsam in spezifischen Versionen zu deployen.

Damit wäre es z.B. möglich, eine vollständige Testumgebung einzurichten: In unserem Fall etwa für alle Sophora-Majorversionen und darin alle notwendigen Komponenten wie Server (Primary, Replica, Staging), Tools (Importer, Exporter, MobileClient, Dashboard, UGC, Teletext usw.) und Ausspielung auszuführen.

Das könnte langfristig unser statisches Test-Setup (eine VM pro Majorversion, mit Ansible verwaltet) ablösen. Aber auch kleinere Setups sind damit möglich, wenn man z.B. das Zusammenspiel zweier sich in Entwicklung befindender Programme in bestimmten Versionen testen will.

Variablen zum Auswählen

Momentan können zwar Variablen definiert, im Template ersetzt und in den verschiedenen Versionen überschrieben werden, allerdings ist das relativ unkomfortabel, falls es beispielsweise eine Variable gibt, die bestimmte feste Werte annehmen kann und die man häufig wechselt.

Beispielsweise kann eine Auswahlvariable mit URLs zu den Sophora-Servern verschiedener Umgebungen (bsp. Test und Produktiv) genutzt werden, um schnell das gewünschte System auswählen zu können.

Diese Variable wird nicht nur in der Konfigurationsansicht zu sehen sein, sondern auch auf dem Dashboard, so dass Anpassungen schnell und komfortabel möglich sind.

O-Neko übermorgen (und ein kleiner technischer Blick hinter die Kulissen)

In fernerer Zukunft planen wir, uns die Baustellen, die in O-Neko aus Entwicklersicht die größten Schwierigkeiten bereiten und die technisch suboptimal gelöst sind, genauer anzusehen.

Einerseits ist O-Neko aktuell eine monolitische Anwendung. Das ist nicht schlimm, da die Anwendung nicht groß ist, allerdings wäre es aus unserer Sicht vorteilhaft, wenn wir O-Neko in mehrere Microservices aufteilen würden.

Um das besser zu erklären, müssen wir eine kleine Exkursion in den Maschinenraum von O-Neko machen.

Vom Monolithen zu Microservices

O-Neko besteht innerhalb des Monolithen aus mehreren unabhängigen Teilen.

Ein Teil kümmert sich um die Kommunikation mit dem Frontend und reicht die Daten heraus.

Ein Teil befasst sich mit dem Abfragen der Docker-Registries. Dieser Teil stellt fest, ob sich zu den Projekten neue Versionen gefunden haben oder ob bestehende Versionen aktualisiert wurden. Wenn Versionen gelöscht werden, wird das ebenfalls erkannt und in O-Neko auch eine Löschung der jeweiligen Version vorgenommen. Es geht hierbei letztendlich um ein Synchronisieren der Informationen zwischen O-Neko und der Docker-Registry.

Ein Teil übernimmt die Kommunikation mit dem Kubernetescluster. Klickt ein Nutzer auf „Deploy“, werden die Templates zu dem Projekt mit den richtigen Variablen gefüllt. Dann wird ein Namespace in Kubernetes angelegt, dessen Name eine Mischung aus Projektname und Versionsname ist, und in diesem Namespace werden wiederum sämtliche Templates, die im Projekt definiert waren, ausgerollt. Dieser Namespace wird beim Herunterfahren einer Version wieder gelöscht. Außerdem übernimmt dieser Teil von O-Neko die kontinuierliche Überwachung der laufenden Anwendungen, sodass der Status komfortabel in der Weboberfläche eingesehen werden kann.

Aus jedem dieser drei Stichpunkte könnte man einen eigenständigen Microservice machen.

Das hätte den Vorteil, dass man beispielsweise nicht mehr alles in Java programmieren müsste. Wir finden Java alle toll, aber für die Kommunikation mit Docker oder mit Kubernetes gibt es bessere Alternativen, beispielsweise in Go. Das würde gleichzeitig dafür sorgen, dass O-Neko bei Problemen mit Kubernetes und Docker garantiert keine Probleme in der Benutzeroberfläche hätte (und auch anders herum).

Diese fundamentale Umorientierung ist allerdings mit großem Aufwand verbunden und wird daher noch etwas auf sich warten lassen.

Zumindest ein Teil aus der vorherigen Liste wird aber ggf. noch vorher gemacht werden müssen, nämlich das Deployment in eigenen Namespaces. Wir hatten konkrete Gründe dafür, warum wir Namespaces für jede Version anlegen, uns ist allerdings klar, dass das nicht die optimale Arbeitsweise für das Tool ist.

O-Neko als Open-Source-Projekt

Außerdem steht natürlich noch im Raum, O-Neko als Open-Source-Projekt zu veröffentlichen.

Grundsätzlich haben wir dafür konkrete Pläne, allerdings ist es nicht damit getan, ein Projekt auf GitHub hochzuladen.

Wir werden vor der Herausforderung stehen, READMEs und Dokumentationen neu zu schreiben, sodass sie auch von Externen verstanden werden. Wir können das Projekt dann auch nicht länger auf unserem internen Jenkins bauen, sondern müssten zu einem externen CI Anbieter wechseln. Damit wären auch einige verwaltungstechnische Aufgaben verbunden. So müssten wir uns überlegen, wie wir Releases machen wollen oder ob wir die Aufmerksamkeit auf O-Neko lenken wollen.

Ausprobieren!

Nachdem ihr jetzt so viel Text zu O-Neko gelesen habt, fragt Ihr Euch vielleicht, ob Ihr unser neues Tool einmal ausprobieren könnt.

O-Neko ist ausschließlich als Dockerimage zu bekommen, welches wir unter https://hub.docker.com/r/subshellgmbh/o-neko zur Verfügung gestellt haben.

Mit den unter https://gist.github.com/philmtd/b78b343c65f706c1d2bf0b6386707a01 zu findenden (und anzupassenden) Kubernetes-Konfigurationen kann O-Neko in ein Cluster deployed werden und läuft dann eigentlich schon vollständig.

Damit O-Neko korrekt arbeitet, ist es wichtig, dass der Service-Account für O-Neko die Rechte zum Anlegen und Entfernen von Namespaces hat.

Die Doku unter https://gist.github.com/philmtd/ce15370ca46a82243bbd0e629ef85f04 erklärt dann die ersten Schritte, um ein Projekt in O-Neko einzurichten.

tl;dr– eine Zusammenfassung

Wir haben in dem vorangehenden Artikel O-Neko vorgestellt. O-Neko ist eine Software, die innerhalb von Kubernetes läuft und die es möglich macht, Entwicklungsstände von Software auf Basis von Docker-Images im Cluster zu deployen.

Dabei soll es in erster Linie einfach zugehen: O-Neko soll eine Brücke zwischen den Entwicklern und den nichttechnischen Projektbeteiligten schlagen. So können auch Projektmanager ohne technisches Hintergrundwissen Entwicklungsversionen starten und testen.

Die Features von O-Neko

  • Entwicklungsstände (bsp. Branches) von Projekten können mit nur einem Klick deployed werden.
  • Projekte können über natives Kubernetes-.yaml konfiguriert werden – mit der Möglichkeit zur Überschreibung von Konfigurationen in speziellen Versionen des Projekts.
  • Die Weboberfläche ist übersichtlich.
  • Laufende Deployments aktualisieren sich automatisch, sobald sich etwas an der Version geändert hat (pro Projekt und Version konfigurierbar).
  • Alte Deployments stoppen automatisch (pro Projekt und Version konfigurierbar).

Kommende Features:

  • Project Meshes ermöglichen das Zusammenfassen spezieller Versionen mehrerer Projekte und erlauben es, dynamische große Testumgebungen mit voneinander abhängigen Programmen mit einem Klick hochzufahren.
  • Auswahlvariablen sollen auch nicht technisch versierten Nutzern ein schnelles Wechseln einer häufigen Konfigurationseinstellung (bsp. einer Server-URL) ermöglichen.

Voraussetzungen, damit ein Projekt in O-Neko ausgeführt werden kann:

  • Es müssen Docker-Container für alle Versionen des Projekts gebaut werden, die in O-Neko ausgeführt werden sollen.
  • Es müssen Kubernetes-.yaml Dateien zum Deployen in einem Kubernetescluster vorhanden sein.
  • Grundsätzlich ist bis auf wenige Ausnahmen jedes Projekt, das sich in Kubernetes ausführen lässt, mit O-Neko kompatibel.
  • Logischerweise muss ein Kubernetescluster vorhanden sein, damit alles funktioniert. Wir empfehlen, eine gehostete Lösung einzukaufen, wenn es ernsthaft betrieben werden soll, da der Wartungsaufwand eines eigenen Clusters relativ hoch sein kann. Zu Experimentierzwecken reicht aber auch ein kleines Cluster aus, z.B. ein lokales auf dem eigenen PC.

Technische Hintergründe zu O-Neko:

  • O-Neko läuft innerhalb eines Kubernetes-Clusters.
  • An einem Projekt wird bestimmt, welches Docker Image aus welcher Registry dazu gehört. Anschließend pollt O-Neko regelmäßig alle Tags zu diesem Image und listet diese als ausführbare Versionen des Projekts auf.
  • Die Konfiguration eines Projekts wird mit typischen Kubernetes-.yaml Dateien gemacht. Diese werden als Template mit Variablen versehen, sodass darin bestimmte Platzhalter von O-Neko ausgefüllt werden können, bsp. das Tag der Docker-Images oder der Name der Version bzw. des Projekts. Es können aber auch eigene Variablen definiert werden, von denen einige in einem zukünftigen Update auch auf der Übersichtsseite einfach verstellt werden können (siehe oben „Kommende Features“).
  • Wird eine Version eines Projekts deployed, erstellt O-Neko dafür einen eigenen Namespace im Cluster. Das schottet die darin laufenden Container gegen alles andere ab und stellt sicher, dass es nicht zu Konflikten kommt. In diesem Namespace werden dann alle vom Anwender definierten Ressourcen deployed. Wird eine Version abgeschaltet, wird der gesamte Namespace wieder gelöscht, sodass keine alten Daten liegen bleiben.
  • Der Status aller laufenden Versionen wird dauerhaft von O-Neko überwacht, sodass Probleme in  der Weboberfläche einsehbar sind.

Zum Schluss …

Wir hoffen, Euch hier einen interessanten Einblick in unser neues Projekt O-Neko gegeben zu haben.

Weil es dem Prinzip der Einfachheit gehorcht, können auch weniger technikaffine Projektbeteiligte im Handumdrehen auf Entwicklungsstände von Software zugreifen – ohne den Umweg über die Entwickler  oder die Installation von Entwicklungstools.

Wenn ihr Fragen oder Anmerkungen zu O-Neko habt, lesen wir gern von Euch unter team-weasel@subshell.com.

Team Weasel
Team Weasel
26.09.19
Icon