Skip to main content

Kubernetes-native Anwendungen zuverlässig bereitstellen

Artikel von
Headshot of Amir Moualem

Amir Moualem

kubernetes tumb

14. November 2019

0 Min. Lesezeit

Vor einigen Monaten begann unser Team mit der Entwicklung eines neuen Produkts.

Dieses neue Produkt hat einige Eigenschaften, die es von anderen Softwareprojekten unterscheiden, die wir bisher entwickelt haben:

  1. Es ist Kubernetes-nativ. Das bedeutet, dass es eng an die Kubernetes-API gekoppelt ist und diese spezifische API zum Ausführen benötigt (und sogar zum Testen … mehr dazu später).

  2. Damit es ordnungsgemäß funktioniert, müssen bestimmte Konfigurationen angewendet werden. Deshalb müssen wir das Image zusammen mit den Kubernetes-.yaml-Konfigurationsdateien bereitstellen, damit unsere Nutzer es problemlos installieren können. Zusätzlich möchten wir das Onboarding für dieses Produkt so einfach wie möglich gestalten. Deshalb unterstützen wir die Installation sowohl über Helm-Charts als auch über „unveränderte“ .yaml-Dateien. Beides muss getestet und veröffentlicht werden.

  3. Es handelt sich um eine Client-Anwendung. Sobald sie veröffentlicht ist, haben wir keine Kontrolle mehr darüber – zurücknehmen können wir sie nicht. Hotfixes für unsere Microservices lassen sich in wenigen Minuten veröffentlichen. Unsere Nutzer jedoch wiederholt zu Upgrades ihrer Installation aufzufordern, würde das Onboarding bestenfalls erschweren.

Deshalb investierten wir etwas mehr Zeit in die Planung einer CI-&-CD-Pipeline, mit der wir ein solches Produkt zuverlässig bereitstellen können, ohne uns dabei (zu sehr) auszubremsen.

Die bestehende CI/CD-Pipeline von Snyk für Microservices dreht sich um die Interaktion unserer Entwickler mit GitHub:

  1. Beim Öffnen von Pull Requests wird der Service in einer Testumgebung installiert, ausgeführt und getestet.

  2. Beim Mergen von Pull Requests beginnt der Prozess genauso. Anschließend folgen ein semantisches Release für den Service, das Erstellen eines Docker-Images, dessen Ausführung und die Bereitstellung im Kubernetes-Cluster unserer Produktionsumgebung.

Wir suchten nach ähnlichen Interaktionen, damit sich der Prozess so natürlich wie möglich anfühlt.

Wie sind wir also an dieses Projekt herangegangen?

Kubernetes-Integrationstests mit Kind

Die erste große Frage für uns als unerfahrenes Team war, wie wir Software testen sollten, die so stark auf die Kubernetes-API angewiesen ist. Alle Kubernetes-API-bezogenen Abläufe zu simulieren, wäre unglaublich aufwendig zu schreiben und zu warten und würde wahrscheinlich einige der kritischen Abläufe, die getestet werden sollten, nicht abdecken. Ein naheliegender Ansatz wäre, einen echten Kubernetes-Cluster zu verwenden – entweder indem wir für jede Testphase einen neuen einrichten oder eine dauerhaft laufende Umgebung bereitstellen. Das würde zwar funktionieren, aber zu diesem frühen Zeitpunkt unserer Softwareentwicklung mehr Komplikationen mit sich bringen, als wir bewältigen wollten:

  • Jedes Mal neue Kubernetes-Umgebungen hochzufahren, würde die Entwicklung verlangsamen und die Einrichtungszeit für Tests deutlich verlängern.

  • Eine dauerhaft laufende Umgebung würde bedeuten, dass wir uns um die Bereinigung kümmern müssten, und könnte zu vielen unzuverlässigen Tests führen.

Zum Glück wurden wir auf dasKind-Projekt aufmerksam gemacht. Kind ist ein Docker-basiertes Tool zum Ausführen lokaler Kubernetes-Cluster, die der Kubernetes-API entsprechen. Es passte perfekt zu unseren Anforderungen, da es die Vorteile einer sauberen Umgebung für jeden Test mit einem sehr schnellen Setup verbindet.

Unsere Testphase erstellt jetzt also – in unserer CI-Umgebung ebenso wie lokal – einfach einen Kind-Cluster, lädt unser neu erstelltes Image hinein und prüft, ob der Container die vorgesehene Aufgabe erfüllt.

Testen Sie, was Sie bereitstellen; stellen Sie bereit, was Sie testen

Die nächste Herausforderung bestand darin, sicherzustellen, dass wir nicht nur unseren Code, sondern auch das vollständige Produkt testen, das wir bereitstellen.

Der Code (und der Service) wird in einem Docker-Image installiert. Das Docker-Image kann mithilfe von Helm-Charts oder „unveränderten“ .yaml-Dateien in Kubernetes-Clustern bereitgestellt werden. Diese Cluster können unterschiedliche Versionen der Kubernetes-API unterstützen.

Wir möchten dieses breite Spektrum an Szenarien testen und unterstützen.

Sobald wir ein Image erstellen, versehen wir es mit einem Tag, damit wir es während der verschiedenen Testphasen identifizieren können. Bevor es als „genehmigt“ markiert wird, installieren wir dieses Image sowohl mit Helm-Charts als auch mit „unveränderten“ .yaml-Dateien auf Kubernetes-Clustern mit unterschiedlichen API-Versionen.

In dieser Phase nutzen wir außerdem semantic-release, um unseren Images Versionsnummern zuzuweisen. Statt unsere Produkte anhand ihrer Git-SHAs oder manuell vergebenen, willkürlichen Versionsnummern nachzuverfolgen, haben wir uns für semantische Versionierung entschieden. So stellen wir einfache, verständliche Versionsnummern bereit. Die zugehörigen Release-Notes werden automatisch erstellt und auf GitHub veröffentlicht. Das Ergebnis ist ein Image mit einem versionierten Tag, zum Beispiel 1.2.3.

In dieser Phase müssen wir nur noch in unseren Deployment-.yaml-Dateien auf die neue Version verweisen …

Einfach veröffentlichen mit Helm-Charts und GitHub Pages

Helm ist der Paketmanager für Kubernetes. Eine kurze Recherche ergab zwei gängige Ansätze für die Veröffentlichung von Helm-Charts:

  1. Das Helm-Chart im öffentlichenkuratieren Helm-Charts-Repository hosten.

  2. Sie mitGitHub Pages in unserem Repository selbst hosten.

Der zweite Ansatz erschien uns zunächst sinnvoller, da er in sich geschlossen ist und keine Abhängigkeit von Dritten schafft. Wenn unser Produkt und unsere CI-&-CD-Pipeline ausgereifter sind, könnten wir unser Chart irgendwann auch in das öffentliche Repository einbringen.

Alles mit GitHub zusammenführen

Es sieht so aus, als hätten wir die meisten unserer Probleme gelöst: Wir führen Integrationstests mit Kind durch, um reale Kubernetes-Umgebungen in unterschiedlichen Szenarien zu simulieren. Außerdem versehen wir unsere Produkte mit semantischen Versionsnummern und veröffentlichen sie der Einfachheit halber auf GitHub Pages.

Wie fügt sich das alles in unseren Entwicklungsprozess ein?

Wir haben uns auf zwei primäre Branches in unserem Git-Repository geeinigt:

  1. staging ist unserStandard-Branch und dient als Ausgangs- und Zusammenführungspunkt für alle Änderungen am Produkt (neue Funktionen, Fehlerbehebungen, Wartungsaufgaben usw.).

  2. master ist der Entscheidungspunkt für die Veröffentlichung unseres getesteten Produkts.

Die CI-&-CD-Pipeline umfasst insgesamt vier Phasen, die sich über diese beiden Branches und die beiden wichtigsten Interaktionen unserer Entwickler mit GitHub-Branches erstrecken (einen Pull Request öffnen und einen Pull Request mergen). Jede Phase soll unser Vertrauen in das entwickelte Produkt stärken und uns der Bereitstellung für unsere Nutzer näherbringen.

Die Orchestrierung selbst übernimmt Travis CI (die Migration zu CircleCI läuft gerade). Im Grunde hätte dafür aber auch ein kurzes, intern entwickeltes Python-Skript genügt.

Die folgende Tabelle fasst diese vier Phasen und die jeweiligen Ziele zusammen.

Auslöser

Ursache

Ziel

Einen Pull Request von einem Feature-Branch zu staging öffnen.

Linting, Kompilierung und Unit-Tests.
Ein (verwerfbares) Docker-Image erstellen und für die Kind-Integrationstests verwenden.

In einer sauberen Umgebung testen.

Einen Pull Request von einem Feature-Branch zu staging mergen.

Ein Docker-Image erstellen und als candidate taggen. Es unsere Integrationstests durchlaufen lassen. Semantic Release aufrufen, um das getestete Image als 1.2.3-approved. zu taggen.

Sicherstellen, dass die gemergten Branches keine Konflikte verursachen. In einer sauberen Umgebung testen und erstellen. Das Produkt, das wir veröffentlichen möchten, einmal erstellen.

Einen Pull Request von staging zu master öffnen.

Nichts.

Dies ist ein Haltepunkt für manuelle Schritte, die wir vor der Veröffentlichung des Produkts durchführen möchten, etwa Dogfooding.

Die letzte Gelegenheit für Feedback.

Bei Bedarf eine Erinnerung, bestimmte Testumgebungen manuell zu überprüfen.

Einen Pull Request von staging zu master mergen.

Das Image mit seinem öffentlichen Tag versehen: 1.2.3. Den Tag-Verweis in unseren .yaml-Dateien und Helm-Charts auf die neue Version ändern. Einen neuengh-pages-Branch mit allen Änderungen veröffentlichen, damit das Produkt genutzt werden kann.

Das bereits erstellte und getestete Produkt veröffentlichen – wir wissen, was wir bereitstellen.

Hier gibt es keine weitere langweilige Zusammenfassung wie jeden Tag

Nur Spaß, sie ist total langweilig. Ich fasse mich aber kurz.

Wir haben eine hervorragende CI-&-CD-Pipeline aufgebaut und dabei viel gelernt. Wir verbinden Automatisierung mit genau dem richtigen Maß an manuellen Klicks, sodass es sich für uns natürlich anfühlt. Jeder Schritt stärkt unser Vertrauen in das, was wir bereitstellen. Mit dem letzten Schritt wird unsere Bereitstellung tatsächlich öffentlich.

Wie bei jedem anderen Softwareprojekt gibt es auch hier noch Verbesserungspotenzial. Wir könnten mit der Veröffentlichung in einem öffentlichen Helm-Chart-Repository beginnen. Außerdem könnten wir unsere Testinfrastruktur um länger laufende Umgebungen erweitern, um die Aktualisierbarkeit zu testen. Wenn wir unseren Tests genug vertrauen, können wir vor der Veröffentlichung vielleicht sogar einige manuelle Schritte überspringen – wer weiß?

Container-Sicherheit mit Fokus auf Entwickler

Snyk findet und behebt automatisch Schwachstellen in Container-Images und Kubernetes-Workloads.

Gepostet in:

Weiterlesen

Blog

Stadium-Sommer: Die Snyk Connect Fan Zone Tour

Die Fan Zone Tour von Snyk brachte KI-Security-Workshops, Networking und freundschaftlichen Wettbewerb in 8 Städte und 3 virtuelle Sessions. Die Teilnehmenden bauten ihre Kompetenzen aus, tauschten Ideen aus und entwickelten sich gemeinsam weiter.

Blog

Snyk VulnBench JS 1.0: Finden LLMs zweimal dieselben Fehler?

Snyk VulnBench JS 1.0: 300 wiederholte Scans zeigen, dass LLM-Sicherheitsergebnisse von Durchlauf zu Durchlauf variieren, während SAST und Modelle unterschiedliche Sicherheitslücken aufdecken.

Article

Bösartige node-ipc-Versionen nach mutmaßlicher Kompromittierung eines Maintainer-Kontos auf npm veröffentlicht

Am 14. Mai 2026 wurden mehrere bösartige Versionen des beliebten npm-Pakets node-ipc in der npm-Registry veröffentlicht. Aktuelle öffentliche Berichte nennen node...