Im vorangegangen Beitrag habe ich auf hoher Ebene drei unterschiedliche Szenarien für den Betrieb einer Webapplikation besprochen. Gegen Ende des Beitrags habe ich das Problem geschildert, dass insbesondere bei der evolutionären Modernisierung von Legacy-Systemen ggf. alle Szenarien erfüllen zu müssen, was scheinbar einander widersprechenden Anforderungen entspricht. Bevor ich weiter ins Details gehe, skizziere ich daher Ansätze für den Umgang mit dieser Herausforderung.
Ich werde mich entlang der zuvor gebildeten Szenarien vorarbeiten:
Traditionelle Anwendungen in einem komplexen Betriebsumfeld
Traditionelle Anwendungen in einem einfachen Betriebsumfeld
Microservice-Umfeld
Die Lösungsansätze werden aufeinander aufbauen und somit geeignet sein, auch mehrere oder alle Szenarien abzudecken. Natürlich muss man diese Flexibilität durch Aufwand bezahlen, daher wird jeweils diskutiert, wann das sinnvoll erscheint.
Szenario 1
Die Anforderung für dieses Szenario ist, die eigene Anwendung in eine Betriebs-Landschaft einfügen zu können. Gerade für ein Produkt mit einem langen Lebenszyklus ist unmöglich abzuschätzen, welche Systeme und Protokolle dafür in Zukunft bedienen werden müssen. Es muss fleixible und einfache Möglichkeiten geben, eine Integrationsschicht zu entwickeln.
Der Schlüssel zu dieser Flexibilität ist die Trennung des Applikationskerns der den Business-Code enthält von Integrations-Code. Eine bildliche Metapher hatten wir in den Illustrationen des letzten Beitrags gesehen, wo die Integrationsschicht wie eine Hülle den Applikationskern umschließt und an die Außenwelt anbindet – gleichzeitig aber klar getrennt ist. Dies ist kein neues Ziel und es gibt seit Jahrzehnten verschiedene Konzepte, dies umzusetzen. Natürlich können in einer Applikation diese Konzepte miteinander kombiniert werden:
Verschiedene Frameworks verfolgen dieses Ziel seit 20 Jahren (z.B. trat vor 20 Jahren J2EE mit diesem Ziel an) und haben seither erhebliche Fortschritte dabei gemacht.
Eine Möglichkeit ist die Entkopplung der beiden Code-Teile durch Interfaces. Dies ist Gegenstand von Architektur-Mustern, z.B. der sogenannten Hexagonalen Architektur (auch Zwiebel- bzw. Onion-Architektur).
Ein anderer Ansatz ist die Aspektorientierte Programmierung.
Es ist jeweils zu bewerten, ob sich die zusätzliche Komplexität lohnt. Insbesondere bei sogenannten CRUD-Services, die sich darauf beschränken, persistierte Daten zu exponieren, ist das Verhältnis zwischen Aufwand und Nutzen kritisch zu hinterfragen.
Das Einführen der beschriebenen Architekturmuster bringt abseits von den Betriebsaspekten einen großen Mehrwert für die Anwendung, kann aber bei einer großen Code-Basis mit erheblichem Aufwand verbunden sein. Um den Kern des Business-Codes von CRUD-Services abzugrenzen ist eine Modularisierung der Anwendung notwendig – hier kann Domain-Driven-Design einen Beitrag (DDD) leisten. Auch wenn Synergien bestehen, ist die Modularisierung und Modernisierung von Legacy-Software ist aber ein Thema, das weit über den hier gesetzten Rahmen hinausgeht und vielleicht mal mit einer eigenen Blog-Serie bedacht wird.
Überblicksebene, wechseln aber vom Problem- in den Lösungsraum.
Szenario 2
Die drei Schichten (Gechäftslogik, Integrationsschicht, Umgebung) werden hier in Form von konzentrischen Kreisen darstellt, wie das bei der sog. Zwiebel-Architektur (Onion-Architecture, aka Hexagonal Architecture) ueblich ist. Austausch der Integrationsschicht erlaubt unter Beibehaltung der Business-Logik die Anbindung einer anderes gestalteten Umgebung. Man sieht, dass der Scope der App im Szenario 1 bis zur Integrationsschicht reicht.
Gerade Rechenzentren, die viele Instanzen einer Anwendung für verschiedene Kunden betreiben, sind auf einen hohen Automatisierungsgrad angewiesen. Dies erfordert, dass unsere Anwendung per Kommandozeile administriert werden kann. Der Einsatz einer Administrationsoberfläche für Betrieb, Migration oder Installation – vielleicht noch als PC-Anwendung – entwickelt sich hier vom Komfort-Feature zum Hindernis. Die Automatisierung erfolgt wahrscheinlich durch den Betreiber selbst, aber als Entwickler müssen wir hier frühzeitig die Anforderungen zuliefern (Konfigurationsparameter, Operator-Handbücher etc.), was gerade in agilen Entwicklungsprojekten herausfordernd ist.
In der Serie beleuchte ich aus der Perspektive des Entwicklers die Anforderungen bei der Übergabe einer Web-Anwendung an einen Betreiber. Zum Auftakt bespreche ich den Einfluss des Betreibertyps sowie der Art der Anwendung. Ich leite drei Szenarien ab, entlang derer in Folgebeiträgen einzelne Aspekte besprochen werden. Die Betrachtung schreitet so vom Allgemeinen zum Spezifischen voran.
Betriebsübergabe und DevOps – Ein Widerspruch?
Folgt man der DevOps-Debatte, so entsteht der Eindruck, eine klassische Betriebsübergabe gebe es gar nicht mehr. Entwicklungsteams betreuen danach ihre Anwendungen auch in Produktion und nutzen dazu Public-Cloud-Services. Die Wirklichkeit sieht jedoch oft anders aus: Kunden betreiben auch heute noch ihre Anwendungen „on premise“ oder im Rechenzentrum und die Nutzung der Public-Cloud sowie die Betreuung von Produktionsumgebungen durch Entwickler werden mit Verweis auf Datenschutz oder andere Regularien abgelehnt.
Was vom DevOps-Gedanken in dieser Situation übrig bleibt, ist die Verantwortung des Entwicklerteams für die Umsetzung von Betriebsaspekten, wo sich Fach-Entwickler klassisch von Framework-Entwicklern abgrenzten. Das Team wird eingenständiger, was Reibungsverluste und Blockaden reduziert, die bei der Zusammenarbeit mit organisatorisch getrennten Spezialisten auftreten. Eine agile Arbeitsweise wird so erst ermöglicht.
Wir müssen uns also mit einem Betreiber auseinandersetzen und im Dialog aus den wechselseitigen Anforderungen zwischen Betrieb und Entwicklung gute Lösungen entwickeln, wie wir das auch bei Nutzeranforderungen zu tun gewohnt sind.
Zwar sind viele der hier betrachteten Fragen auch für den Betrieb einer SaaS-Lösung und echtes DevOps anwendbar, jedoch ist der Problembereich dort weiter gefasst und schließt die Gestaltung der Betriebslandschaft (Systeme und Prozesse) ein, mit der man sich herkömmlicherweise als Entwickler nicht auseinanderzusetzen hat. Übrigens macht SaaS auch abseits des Betriebs eine Erweiterung des Betrachtungsfelds notwendig, z.B. im Produktmanagement.
Betreiberseitige Unterschiede
Größere Unternehmen und solche mit starker IT-Orientierung unterhalten Rechenzentren mit großem Mitarbeiterstab und entsprechender Spezialisierung. Diese haben meist eine genaue Vorstellung davon, wie eine bestimmte Anforderung umzusetzen ist und geben für alle Aspekte ein System aus Infrastruktur und Prozessen vor.
Kundenspezifische Entwicklungsprojekte werden durch diese Vorgaben sogar unterstützt, findet sie doch umsetzungsfähige Blaupausen vor, oder können in Teilaspekten (z.B. Usermanagement, Monitoring) auf eine Umsetzung in der eigenen Anwendung verzichten und auf vorhandene Infrastruktur zurückgreifen.
Wird jedoch ein Standard-Produkt in Betrieb übergeben, das die Betriebsanforderungen bereits abdeckt, so findet dieses nun Randbedingungen vor, die im Koflikt zur eigenen Produktgestaltung stehen können. Sicherheits- und Lizenz-Bedenken können Ablaufumgebungen oder Libraries einschränken, oder es werden Vorgaben bezüglich der eingesetzten Systeme und Protokolle gemacht. Letzteres kann eine spezifische Integrationsschicht erfordern.
Ein weiteres Kennzeichen dieses Umgebungstyps ist der, mit einer großen Organisation verbundene bürokratische Aufwand und die Schwierigkeit, Veränderung herbeizuführen. Neben der Dauer von Entscheidungs- bzw. Genehmigungsprozessen ist für Kunden, welche selber Dienstleister sind, die Einbindung ihrer eigenen Kunden ein Grund für lange Laufzeiten und zwingt zu frühzeitigen Festlegungen, was z.B. folgende Probleme bereitet:
Die Anforderung von Infrastrukturkomponenten (z.B. Messaging-Systeme) benötigt einen sehr großen Vorlauf.
Verbindliche Aussagen zum Hardware-Sizing werden für Genehmigungs- und Bestellungprozsse notwendig, bevor ein lasttestfähiger Prototyp bereitsteht.
Die Vorbereitung von Informations- oder Schulungsmaterial für End-Kunden benötigt frühzeitig verbindliche UI-Designs oder gar Prototypen.
Anforderungen für die Prozessautomatisierung (insbesondere Betriebshandbücher und Konfigurationsbeschreibungen) müssen sehr früh bereitstehen.
Bei kleineren Unternehmen steht ein „einfacher Betrieb“ im Zentrum der Anforderungen. Die begrenzten Personalresourcen einer IT-Abteilung sind auf fertige Lösungen angewiesen, die mit möglichst geringen Infrastruktur-Voraussetzungen betrieben werden können.
Die oben geschilderten Vor- und Nachteile verkehren sich ins Gegenteil: Wo man im Rechenzentrum auf vorhandene Prozesse bauen kann, müssen diese Anforderungen hier durch die Applikation selbst abgedeckt werden. Dafür hat man im Rahmen der oben beschriebenen Einschänkungen größere Freiheiten bei der Gestaltung der Lösung und weniger Vorlauf bei Entscheidungen.
Und um Missverständnissen vorzubeugen: Flexibiltät bei der Lösung bedeutet nicht, das es geringere Anforderungen gibt. Geschäftskritische Anwendungen in regulierten Branchen müssen auch in diesem Kunden-Segment harte aufsichtsrechtliche Vorgaben erfüllen, aber bei der Gestaltung der Lösung ist man freier.
Typische Anforderungen dieses Betreibertyps sind in der Regel:
Zur Abdeckung der Betriebsanforderungen werden möglichst wenige zusätzliche Tools vorausgesetzt.
Betriebliche Prozesse (Installation, Migration etc.) sind mit möglichst geringem Aufwand verbunden.
Technologien die neu oder aufwändig sind, sind ggf. nicht verfügbar. Dies schließt Plattformen (z.B. Kubernetes), sowie Protokolle (z.B. OIDC) ein.
Falls es um die Erneuerung eines bestehenden Systems geht, bevorzugt traditionell jeder Betreiber die Beibehaltung des Status Quo. Den natürlichen Gegensatz zwischen Entwickelern, die Veränderung betreiben und Betreibern, die nach dem Motto „Never change a running system“ operieren war schließlich eine der Motivationen für die DevOps-Bewegung.
Anwendungsseitige Unterschiede
Momentan wird fast jeder Beitrag zum Thema Anwendungs-Architektur von der grundsätzlichen Unterscheidung zwischen monolithische Anwendungen und Microservices eingeleitet. Auch beim Thema Betriebsanforderungen besteht ein fundamentaler Unterschied: Traditionelle Anwendungen – monolithisch ist etwas kurz gefasst, weil diese ja durchaus auf Komponenten verteilt sein können – decken viele Betriebsanforderungen in der Anwendung ab, während bei einer Microservice-Architektur diese Aufgabe und die damit verbundene Komplexität auf die Deployment-Ebene verschoben wird.
Ein Beispiel ist die Umsetzung von HTTPS. In einer Microservice-Umgebung fungiert ein Webserver als Ingress-Server und routet eingehende HTTPS-Verbindungen an APIs im Cluster, die typischerweise HTTP nutzen (siehe hier). In einer traditionellen Anwendung wird HTTPS in der Applikations-API selbst umgesetzt. Ein anderes Beispiel ist die Forderung nach Mandantenfähigkeit. Eine traditionellen Applikation kann in einer Instanz mehrere Mandanten verarbeiten und schaltet z.B. abhängig vom Aufrufer zwischen mehreren Datenbanken um. Im Microservice-Deployment hat man hingegen eine Instanz pro Mandant und die Aufrufe werden abhängig vom Aufrufer zu der entsprechenden Instanz geleitet.
Nebenbei bemerkt: Die Einfachheit des einzelnen Microservice ist optimal für DevOps. Eine traditionelle Anwendung, die möglicherweise mit verschiedenen Integrationsschichten für eine ganze Palette an Betriebsumgebungen entwickelt wird, überfordert ein Entwicklungsteam, wenn es selbst auch Betriebsaspekte all dieser Umgebungen verantwortet (Deployments Umgebungsmanagement, Automatisierung). In der Folge wird die Produktivität bei der Entwicklung fachlicher Features sinken.
Die Szenarien
Bei der Darstellung der betreiberseitigen Unterschiede habe ich große und kleine Unternehmen gegenübergestellt, da dieser Unterschied augenfällig ist – allerdings hatte ich den großen Unternehem schon solche mit starker IT-Orientierung zur Seite gestellt. Auch das Geschäftsmodell des Kunden spielt also eine Rolle.
Ich teile die Szenarien daher lieber nach komplexem und einfachem Betrieb und nicht nach Kundensegment auf, als harte Kriterien wie Unternehmensgröße zu verwenden. Es bleibt dem Leser
Anwendungsseitig haben wir die Unterschiede von traditionellen Anwendungen gegenüber Micorservices betrachtet. Da die Betriebsanforderungen bei letzteren typischerweise nicht innerhalb der Anwendung erfüllt werden trifft die Abhängigkeit vom Betriebsszenario die traditionellen Anwendungen. Microservices sind hier quasi eine eigene Welt.
Es ergeben sich die folgenden Szenarien:
Traditionelle Anwendungen in einem komplexen Betriebsumfeld
Traditionelle Anwendungen in einem einfachen Betriebsumfeld
Microservice-Umgebungen
Wie angekündigt, werde ich in Folgebeiträgen verschiedene Betriebsaspekte entlang dieser Szenarien beleuchten. Szenario 2 ist vermutlich auch interessant für den privaten „Betrieb“ von Side-Projekten zum Hobby oder als Referenz.
Der Ruf nach der „Eierlegenden Wollmilchsau“
In Zeiten des Übergangs zu SaaS Angeboten stehen Anbieter traditioneller Produkte vor der Herausforderung, ihre Anwendung umzubauen, so dass sie möglichst ohne Unterbrechung und zu möglichst geringen Kosten „evolutionär“ in Richtung einer modernen (Microservice-)Architektur weiterentwickelt wird, während sie gleichzeitig in traditioneller Weise an kleine und größere Kunden ausgeliefert wird.
Der Umbau monolithischer Anwendungen in eine modernere Architektur ist ein gesondertes Thema, aber im Aspekt Betriebsanforderung läuft es darauf hinaus, alle drei Szenarien zu bedienen, was an das sprichwörtliche Nutztier erinnert. Ich werde Ansätze besprechen, um diese Anforderung zu erfüllen. Wichtig ist, dass diese Flexibilität seinen Preis hat. Moderne Architekturen sind zwar in der Lage sind, sich flexibel anzupassen und manchen Widerspruch auflösen, doch das ist nicht immer eine gute Idee. Effizienz entsteht erst durch Fokussierung. Wer nie „nein“ sagt, wird sich verzetteln und in dem was er tut, keine Exzellenz ausbilden.
Kategorisierung aber kein Schubladendenken
Einen Vorbehalt zum Abschluss: Kategorisierungen sind wertvoll, weil wir ohne diese in der Flut von Informationen ertrinken würden – selbstverständlich muss man sich aber bewust sein, dass es sich um Muster handelt, von denen die Realität im Detail abweicht. Will man die Realität zwanghaft an die Kategorien anpassen, betreibt man „Schubladendenken“. Die meisten Projekte werden nicht in jedem Aspekt einem Szenario folgen – Aber wenn ich meine Sache gut gemacht habe, helfen die Szenarien, die Komplexität des Einzelfalls zu erfassen.