Dies ist der letzte Artikel dieser 3-teiligen Serie. Im ersten Artikel haben Sie die Herausforderungen kennengelernt, wenn Entwickler_innen, die an Kubernetes-basierten Anwendungen arbeiten, versuchen, alle Dienste lokal auszuführen. Im zweiten Artikel ging es darum,
- wie schnell die Komplexität steigt, wenn Dienste nicht gemeinsam genutzt werden können,
- warum Sie wahrscheinlich ungewollt bereits ein komplexes Setup haben und was wirklich die Kosten sind, wenn alle Dienste lokal ausgeführt werden.
Jetzt werfen wir einen Blick auf die Lösungen.
Was funktioniert
Ich habe mit vielen Entwickler_innen gesprochen. Es waren nur die zufrieden, die:
- die in der Lage waren, lokal zu builden und ihre Änderungen zu validieren, bevor sie diese in den Staging-Bereich übertrugen,
- vollen Zugriff auf die Dienste hatten, an denen sie arbeiteten, indem sie diese entweder lokal ausführen (Mehrheit) oder vollen Zugriff auf ein Remote-Deployment hatten (Minderheit),
- Zugang zu einem hochautomatisierten Deployment hatten, welches allen zur Verfügung stand und die Notwendigkeit beseitigte, sich mit der faktoriellen Komplexität ihres Deployments zu befassen (oder reduzierte).
Wie man dorthin kommt
Diesen Punkt zu erreichen bedeutet:
- Eine Automatisierung zu haben, die mit faktorieller Komplexität umgehen kann,
- Remote-Computing-Ressourcen zu haben, um das Deployment für jede_n Entwickler_in zu ermöglichen,
- Dienste zu teilen, sodass Deployments erschwinglich sind.
Glücklicherweise sind 1 und 2 Dinge, die man kaufen kann. Leider ist der dritte Punkt etwas, um den Sie sich intern kümmern müssen: Ihre Software muss in der Lage sein, die gemeinsame Nutzung von Diensten zu unterstützen. Andernfalls können die Kosten unerschwinglich hoch sein. Wie bereits erwähnt, kann dies aber iterativ, d. h. für jeden Dienst einzeln angegangen werden. Das wird ein Umdenken erfordern: Lassen Sie die Abwärtskompatibilität außer Acht und konzentrieren Sie sich darauf, Software zu entwickeln, die auch in Zukunft funktioniert.
1: Dynamisches Konfigurationsmanagement + modulare Automatisierung
Automatisierung, die in der Lage ist, mit faktorieller Komplexität umzugehen, bedeutet zweierlei:
- Die Fähigkeit, vielschichtige Konstellationen von Abhängigkeiten und Einschränkungen in einer wartbaren Weise auszudrücken, was die Grundlage für die dynamische Erstellung von Konfigurationen für bestimmte Deployments ist
- Eine Automatisierung, die modular ist und welche die Wiederverwendung von Automatisierungsschritten erlaubt, um viele verschiedene Ergebnisse (d.h. Deployments) zu erzielen
Dynamisches Konfigurationsmanagement ist ein Ansatz zur Verwaltung von Konfigurationen. Sie ermöglicht es, mit faktorieller Komplexität umzugehen, ohne dass Hunderte verschiedene Konfigurationsdateien im Umlauf sind, die sich alle leicht unterscheiden und schwer zu pflegen sind. Stattdessen verfügen Sie über ein System – eine Konfigurationsdatenbank oder ein ähnliches System – das es Ihnen ermöglicht, ein Datenmodell zu definieren, das Ihre Abhängigkeiten und Einschränkungen beschreibt.
Eine modulare Automatisierung, die mit dem dynamischen Konfigurationsmanagementsystem verbunden ist, ermöglicht es Ihnen, automatisch eine große Anzahl verschiedener Konfigurationen zu deployen und so die faktorielle Komplexität einfach zu managen. Sie stellt sicher, dass alle Abhängigkeiten und Einschränkungen berücksichtigt werden und bietet ein vollautomatisches Deployment für alle Entwickler_innen.
Cloudomation Engine ist eine Automatisierungsplattform mit einem integrierten dynamischen Konfigurationsmanagementsystem
Cloudomation Engine ist eine Automatisierungsplattform, die genau für diesen Zweck entwickelt wurde: Sie verfügt über eine integrierte Konfigurationsdatenbank, mit der Sie benutzerdefinierte Konfigurationsdatenmodelle definieren können, die Ihre Deployments beschreiben. Sie können mit Templates beginnen, um gängige Deployment-Szenarien zu beschreiben. Diese können Sie nach Ihren eigenen Bedürfnissen erweitern. Sie können uns aber auch bitten, ein übliches Deployment-Datenmodell für Sie zu erstellen, das auf Ihren vorhandenen Konfigurationsdateien und Skripten basiert.
In diesem Datenmodell können Sie zum Beispiel festlegen, welche Dienste in welchen Versionen miteinander kompatibel sind, welche Dienste gemeinsam genutzt werden können und welche nicht. Sie können auch zusätzliche Deployment-Optionen definieren, z. B. dass Ihre Software auf einem EKS- oder einem GKE-Cluster bereitgestellt werden kann – oder alles andere, was für das Deployment Ihrer Software relevant ist.
Der nächste Schritt besteht darin, dieses Datenmodell mit einer modularen Automatisierung zu verbinden, um die Software mit jeder der möglichen Konfigurationsoptionen bereitzustellen.
In einem ersten Schritt kann Ihr Datenmodell klein sein und nur die gebräuchlichsten Konfigurationen beschreiben, für die Sie wahrscheinlich bereits Skripte haben. Diese Skripte können referenziert werden, so dass Sie schnell loslegen und vorhandene Skripte und Konfigurationen wiederverwenden können.
Im Laufe der Zeit können Sie Ihre Skripte in modularere Automatisierungsschritte aufteilen, mit denen Sie dynamisch immer mehr verschiedene Deployment-Optionen erstellen können. Das Datenmodell kann im Gleichschritt erweitert werden, so dass Sie Ihre automatischen Deployments iterativ ausbauen können.
Darüber hinaus ist Cloudomation DevStack eine Plattform für Remote Development Environments, mit der Sie das automatische Deployment Ihrer Software mit dem automatischen Deployment von Entwicklungstools kombinieren können.
Dies wird Devs über ein Self-Service-Portal zur Verfügung gestellt. So ist es für jede_m Entwickler_in möglich, Entwicklungsumgebungen bereitzustellen, welche die Software enthalten, an der gearbeitet wird. Wenn Entwickler_innen einige Dienste lokal deployen und mit einem Remote-Cluster verbinden möchten, unterstützt DevStack sie, indem relevante Konfigurationsdateien und Skripte bereitgestellt werden, die auf das Deployment zugeschnitten sind. Bei Bedarf stellt DevStack automatisch den Remote-Cluster oder die Remote-Dienste auf einem bestehenden Cluster bereit oder stellt nur die relevanten Konfigurationen bereit, wenn alle erforderlichen Remote-Ressourcen bereits vorhanden sind.
Cloudomation Engine und Cloudomation DevStack ermöglichen es Developer_innen, an komplexer Kubernetes-basierter Software zu arbeiten, ohne sich mit der Komplexität auseinandersetzen zu müssen,
- alle Dienste lokal auszuführen
- oder einige Dienste lokal auszuführen und sie mit einem Remote-Cluster zu verbinden.
2: Ausreichende Rechenressourcen
Die Rechenressourcen zur Ausführung der Anwendung, können lokal – durch den Kauf leistungsstarker Laptops – oder remote bereitgestellt werden. Wenn die Anwendung zu anspruchsvoll für Laptops wird, ist remote die einzige Option.
Glücklicherweise ist der Kauf von Remote-Ressourcen einfach. Das Problem ist hier aber nicht der Kauf von Rechenleistung von einem Cloud-Anbieter, sondern dass dies schnell sehr teuer werden kann. Wenn Entwickler_innen nicht mehr in der Lage sind, die Anwendung lokal auszuführen, sind die für die Anwendung erforderlichen Rechenressourcen groß genug, um in einer Cloud-Umgebung erhebliche Kosten zu verursachen.
In solchen Fällen gibt es Möglichkeiten, die Kosten zu kontrollieren:
- Downscaling, Hibernation und automatisches und schnelles Entfernen ungenutzter Ressourcen,
- So viel wie möglich lokal belassen, z. B. indem nicht gemeinsam nutzbare Dienste lokal deployed werden und nur eine kleinere Teilmenge der gemeinsam nutzbaren Dienste remote bereitgestellt wird,
- Kauf von Hardware statt Anmietung von Cloud-Rechnern: Der Betrieb eines lokalen Entwicklungsclusters in Ihrem Büro kann sehr viel billiger sein als die Anmietung der gleichen Rechenleistung in der Cloud.
All dies setzt voraus, dass die Automatisierung des Deployments bereits vorhanden ist. Damit können Entwickler_innen die Remote-Rechenressourcen effizient nutzen und verwalten.
3: Gemeinsame Nutzung von Diensten, damit Remote-Computing erschwinglich wird
Wie oben beschrieben, ist der beste Weg zu langfristiger Kosteneffizienz die gemeinsame Nutzung von Diensten. Geichzeitig ist es Entwickler_innen möglich, ihre Arbeit zu validieren.
Zusammenfassung
Für Entwickler_innen, die an komplexen Microservices-Architekturen arbeiten, gibt es zwei Einschränkungen:
- Faktorielle Komplexität der Ausführung der Software
- Begrenzte lokale Rechenressourcen
Beide Probleme nehmen mit dem Alter und der Komplexität der zu entwickelnden Software zu. Bei kleinen und mittleren Softwareunternehmen mit Software mittlerer Größe und Komplexität führt dies dazu, dass ein Großteil der Entwickler_innen-Zeit (10-25 %) für die Verwaltung lokaler Deployments aufgewendet wird.
Für größere Softwareunternehmen mit komplexen und umfangreichen Softwareprodukten bedeutet dies oft, dass die Entwickler_innen nicht in der Lage sind, ihre Änderungen am Code zu validieren, bevor sie diese in ein gemeinsames Repository einspeisen. Das Ergebnis sind kostspielige Qualitätsprobleme in der Software.
Um dieses Problem zu lösen, müssen beide Einschränkungen berücksichtigt werden.
Die faktorielle Komplexität kann durch dynamisches Konfigurationsmanagement und modulare Automatisierung gelöst werden.
Das dynamische Konfigurationsmanagement ermöglicht die automatische Erstellung und Pflege von Konfigurationen für komplexe, vielschichtige Konstellationen von Abhängigkeiten und Bedingungen. Abhängigkeiten und Bedingungen/Einschränkungen können einmal formuliert werden und die Konfigurationen können dann automatisch auf der Grundlage der definierten Regeln deployed werden.
Modulare Automatisierung bedeutet, jeden einzelnen Schritt zu automatisieren, um automatisch einen Teil des Deployments zu erstellen. Diese können somit dynamisch kombiniert werden.
Unsere Produkte, Cloudomation Engine & DevStack, sind genau dafür ausgelegt: Sie nehmen bestehende Skripte und Konfigurationsdateien und extrahieren daraus ein Modell Ihrer Deployments. Abhängigkeiten und Einschränkungen werden klar aufzeigt und können nach Bedarf erweitert und angepasst werden.
Die automatische Erstellung dieser Deployments kann durch die anfängliche Wiederverwendung eines möglichst großen Teils der bestehenden Automatisierung erfolgen, während iterativ eine modulare Automatisierung entwickelt wird, die langfristig wartbar und nutzbar ist.
Das zweite Problem, die begrenzten lokalen Rechenressourcen, kann auf die bewährte Art und Weise angegangen werden, indem man dem Problem „Ressourcen hinterherwirft“: Beispielsweise, indem man einfach die benötigte Rechenleistung bei Cloud-Anbietern einkauft. Je nach Ressourcenbedarf der Software kann dies aber unerschwinglich teuer sein.
Um dieses Problem zu lösen, müssen Mikrodienste gemeinsam genutzt werden können, d. h. mehrmandantenfähig sein. Um Ihre Software an diesen Punkt zu bringen, müssen Sie sich wahrscheinlich von Abwärtskompatibilität verabschieden und einige Änderungen an Ihrer Microservices-Architektur vornehmen.
Glücklicherweise wird sich dies auch auszahlen, indem die Skalierbarkeit Ihrer Software deutlich erhöht wird, die Kosten in Production gesenkt werden und die Komplexität in der Entwicklung reduziert wird, so dass Ihre Entwickler_innen schneller arbeiten und Sie neue Funktionen schneller auf den Markt bringen können. Mittel- bis langfristig wird sich dadurch höchstwahrscheinlich auch die Qualität Ihrer Software verbessern.
Wie das Beispiel im letzten Beitrag zeigt, bietet die Abwärtskompatibilität ohnehin viel geringere Möglichkeiten zur Kosteneinsparung als die gemeinsame Nutzung von Diensten, so dass die Investition in die gemeinsame Nutzung von Diensten der Abwärtskompatibilität eindeutig vorzuziehen ist.