Blog

Microservices Summit
Das große Trainingsevent für Microservices, DevOps, Continuous Delivery, Docker & Clouds
27. - 29. September 2017, Berlin
11
Dez

Interview mit Uwe Friedrichsen zum Thema Resilient Software Design

Uwe Friedrichsen

Microservices und DevOps sind derzeit in aller Munde – und das zu Recht – denn Sie läuten eine neue Ära in der Softwareentwicklung ein. Im Microservices Summit erhalten Sie fundierte Antworten und tiefgehendes Know-how von den bekanntesten Praktikern der DevOps-Welt. Im Interview auf den Microservices Summit 2017 in Berlin stellt sich Uwe Friedrichsen den Fragen von Mirko Hillert.

Uwe Friedrichsen (codecentric) beschreibt unter anderem die Inhalte, die er in seinem Workshop zum Thema Resilient Software Design vermittelt. Zudem beantwortet er uns Fragen zur Bedeutung von Resilient Software Design für verteilte Anwendungen und den Herausforderungen, die beim ersten Arbeiten mit Resilient Software Design entstehen?

 

 

Mirko Hillert: Was genau vermittelst du in deinem Workshop zum Thema Resilient Software Design?

Uwe Friedrichsen: Wenn man anfängt, in verteilten Systemen zu arbeiten, stellt man in Produktion relativ schnell fest, dass diese Systeme einige andere Eigenschaften haben, als man es von Monolithen gewohnt ist. Wenn man also sicherstellen möchte, dass diese Systeme zuverlässig und robust in Produktion laufen, dann muss man zusätzliche Maßnahmen ergreifen, die man früher nicht hätte ergreifen müssen. Man muss sicherstellen, dass man all die Fehler, die in verteilten Systemen passieren können, erkennt und sinnvoll darauf reagiert, mit dem Ziel, ein robustes System zu designen. Genau diese verschiedenen Aspekte, die es zu berücksichtigen gilt, werden Inhalt meines Workshops sein.

Mirko Hillert: Welche Bedeutung hat Resilient Software Design für verteilte Anwendungen?

Uwe Friedrichsen: Es geht um Robustheit und Zuverlässigkeit. Wenn ich in einem klassischen Monolithen, also einem sinnvollen und legitimen Architekturmuster, irgendeinen anderen Teil meines Systems aufrufe und dieser Teil keine sinnvolle Antwort gibt, dann bin auch ich meistens nicht mehr selbst in der Lage, darauf zu reagieren. Man kann also sagen: „Man stirbt zusammen. Man lebt zusammen.“ In einem verteilten System ist das nicht so.

Es kann auf der Netzwerkverbindung sehr viel schief gehen, es kann auf der anderen Seite sehr viel schief gehen. Ich stehe dann als ein Systemteil in einem verteilten System da und muss damit umgehen. Meistens habe ich eine Anfrage von außen bekommen und möchte dementsprechend eine sinnvolle Antwort geben. Dann mache ich einen Aufruf an eine weitere Stelle. Manchmal kommt eine Antwort, manchmal kommt keine. Manchmal kommt eine, aber eigentlich dauert das viel zu lange. Oder es kommt einfach eine falsche Antwort. All diese Dinge können passieren. Das liegt in der Natur von verteilten Systemen.

Wenn ich dem Anwender am Ende dennoch ein gutes Benutzererlebnis im Sinne eines zuverlässigen, zeitnah und auch richtig antwortenden Systems bieten möchte, dann muss ich mehr Aufwand betreiben, als ich das früher gemacht habe. Das bedeutet, ich muss wirklich explizit damit umgehen. Ist etwas schief gegangen? Was ist schief gegangen? Wie kann ich darauf reagieren? Kann ich das direkt korrigieren? Muss ich eine Art „Plan B“ fahren? In Neudeutsch nennt man das heutzutage Graceful Degradation of Service – ein kontrolliertes Herabsetzen des Servicelevels, weil ein Teil meines Systems gerade nicht funktioniert.

Ein Beispiel hierfür wäre, dass ich gerade nicht in die Datenbank schreiben kann. Dann nehme ich zwar noch Leseanfragen entgegen, also Benutzerauskünfte, die ich aus Caches bedienen kann, kann jedoch die Daten zu dem Punkt nicht „wegschreiben“. Es kann aber auch sein, dass dieses Wegschreiben von Daten das Schreiben eines Auftrags ist, der so wertvoll für mich ist, dass ich von der Fachseite her sage: „Das muss ich aber können!“ Dann sieht mein Plan B vielleicht nochmal anders aus. Ich sage nicht: „Ich kann jetzt im Moment nicht“, sondern stelle extra dafür noch ein zweites System daneben, das im Falle eines Ausfalls des ersten Systems die Anfragen entgegennimmt, zwischenpuffert und dem Benutzer eine entsprechende Antwort liefert. Da gibt es unzählige Möglichkeiten und viele Situationen, die zu berücksichtigen sind.

Der Kern ist aber: Verteilte Systeme sind von Natur aus nicht deterministisch, und mit diesem unvorhersehbaren Fehlverhalten muss man zur Laufzeit umgehen. Das kann man, anders als früher, nicht einfach der Infrastruktur in die Hand geben. Das muss man explizit in die Anwendungen einbauen.

Mirko Hillert: Welche besonderen Herausforderungen siehst du, wenn man beginnt, sich mit Resilient Software Design zu beschäftigen oder versucht, es neu in Projekte zu implementieren?

Uwe Friedrichsen: Es gibt drei Richtungen der Herausforderungen, die ich wahrnehme. Die erste ist, das Bewusstsein dafür zu schaffen. Man sieht es in Projekten noch immer sehr häufig, dass die Beteiligten, sowohl von der Fachseite als auch von der technischen Seite, in diese 100-Prozent-Verfügbarkeitsfalle hineinlaufen. Das bedeutet, sie haben dieses „Alles rundherum wird schon funktionieren“-Denken. Das gleiche Denken, das man in einem Monolithen haben darf. Denn wenn in einem Monolithen ein anderes Systemteil stirbt, ist man selbst auch tot. Solange also alle leben, ist auch alles verfügbar. Das ist in dieser Umgebung nicht mehr gegeben: Dinge sind teilweise kaputt. Das erstmal in die Köpfe hineinzubekommen, ist eine dieser Herausforderungen.

Das ist sowohl auf der Fach- als auch auf der Entwicklerseite zu beobachten. Auf der Fachseite heißt es dann „Technische Fehler dürfen nicht passieren, das ist ja euer Problem!“ Nein, technische Fehler werden passieren. Die Frage ist nicht, ob sie passieren, sondern wann. Meistens passieren sie dann, wenn man gut Geld mit seinen Systemen machen könnte und richtig viel Last drauf ist, weil Murphy solche Situationen in verteilten Systemen liebt und genau dann zuschlägt.

Auf der Entwicklerseite sieht man immer wieder Folgendes: „Der andere Service ist so wichtig, da brauche ich nicht zu prüfen, ob der da ist oder nicht. Der wird schon da sein. Denn wenn er nicht da ist, haben wir ein anderes Problem.“ Nein, jetzt habt ihr gerade ein Problem. Das Problem ist, dass ihr euch darauf verlasst, dass das der Service da ist. Er wird nicht immer da sein. Und selbst wenn er immer da ist, wird die Netzwerkverbindung ab und zu kaputt sein. Und selbst wenn die Netzwerkverbindung funktioniert, wird vielleicht die Infrastruktur, die ihr nutzt, dazwischen irgendwo kaputt sein – das ist die eine Dimension.

Die zweite Dimension ist, dass ein gutes fachliches Design der Anwendung die Grundvoraussetzung ist, um ein robustes System zur Laufzeit herzustellen. Wenn ich also diese gesamte Fachlichkeit ungünstig schneide, sodass ich eine schlechte Kohäsion und damit eine relativ hohe Kopplung zwischen diesen Systemteilen habe, habe ich nicht nur Wartungs- und Weiterentwicklungsprobleme in der Entwicklung. Es kann auch sein, dass ich zur Laufzeit fast nicht in der Lage bin, ein wirklich robustes System hinzubekommen. Der Effekt, den ich nämlich meistens erziele ist, dass ich einen Systemteil habe, der auf fachlicher Ebene nicht gut von dem anderen Systemteil entkoppelt ist. Das heißt, damit der eine Antwort liefern kann, muss der andere Systemteil ebenfalls seine Antwort liefern. Wenn das jedoch nicht passiert, ist das Einzige, was der vorne machen kann, die weiße Fahne zu hissen und zu sagen: „Ich kann nicht mehr!“ Damit habe ich einen kaskadierenden Fehler im System und damit ist meine Robustheit hinüber. Ich bin nämlich nun abhängig von allen anderen, die ich aufrufe, alle müssen gleichzeitig funktionieren. Nun kann man die Verfügbarkeiten (Zahlen kleiner als eins) für jedes Einzelteil ausmultiplizieren. Eine Zahl kleiner 1 multipliziert mit einer Zahl kleiner 1 ist insgesamt kleiner als die beiden ursprünglichen Zahlen. Das bedeutet, dass die Verfügbarkeit, umso mehr Teile beteiligt sind, immer weiter runter geht. Dass eine saubere, fachliche Entkopplung die Grundvoraussetzung ist, wird sehr häufig unterschätzt.

Die dritte Ebene ist dann, die tatsächlichen Muster und Maßnahmen zu kennen. Das ist dann natürlich schon Fleißarbeit. Es geht dabei darum zu wissen: „Was kann ich auf technischer Ebene machen, um das zu erkennen, abzumildern oder sogar zu beheben?“

Mirko Hillert: Welche Trends siehst du derzeit ganz allgemein für die Verwendung von Microservices?

Uwe Friedrichsen: Die gehen tatsächlich in verschiedene Richtungen. Zum einen sehe ich immer mehr, dass die Unternehmen nach dem ersten Hype etwas ernüchtert feststellen, dass alles gar nicht so einfach ist, wie man es sich zunächst erhofft hatte:„Hurra, wir machen jetzt nur noch kleine Dinge, die wir schreiben und alles wird gut! Unser Traum wir wahr, wir müssen uns mit nichts mehr auseinandersetzen! Wir können unsere Meetingräume abschaffen und einfach vor uns hin programmieren.“

In der Realität ist das jedoch nicht ganz so einfach. Lassen wir mal die Aspekte außen vor, wie man koordiniert, dass verschiedenste autonome Teams trotzdem an einer Zielrichtung arbeiten und das Ganze zur Laufzeit hinbekommen. Plötzlich bekommt man einen riesen Haufen an neuer Infrastruktur, und diese Services muss man jetzt mit allen Abhängigkeiten irgendwie verpacken: „Hallo, Container!“ Jetzt muss ich erstmal Container lernen und das Ganze zur Laufzeit irgendwie betreiben. Von Hand macht das keinen rechten Spaß, also: „Hallo, Scheduler!“ Also Kubernetes, DC/OS oder die verschiedensten anderen, die man als Scheduler verwenden kann. Dann brauche ich noch ein gutes Monitoring, aber Monitoring im klassischen Sinne reicht ja auch nicht mehr. Man braucht richtige Observability mit Traceability, also wie ich die richtigen Metriken bekomme, wie ich die Logs kriege und wie ich das aggregiere. Dann kommt noch das ganze Routing, Deployment und das Traffic Shaping. Dafür gibt es Service Meshes wie Istio und wie sie alle heißen, vielleicht hilft das an der Stelle weiter … Ich habe also plötzlich einen riesigen Rattenschwanz an Infrastruktur, damit irgendwo da drin meine Logik läuft. Das ist erstmal eine Menge, die zu lernen ist.

Dann kommt wieder diese Sache mit dem Full Stack Developer dazu, was dann wirklich anstrengend wird. Meines Erachtens nach versuchen zurzeit die Unternehmen, beziehungsweise der Markt, diese Lernkurve ein wenig abzumildern. Oder: Der deutlich radikalere Ansatz, den ich zurzeit wahrnehme und der im englischsprachigen Raum schon einen sehr starken Aufschwung erlebt hat (der deutschsprachige ist jedoch noch ein wenig hinterher), ist Serverless. Serverless nicht im Sinne von „Ich schreibe mal eine Lambda bei AWS, eine Azure oder Google Function“, sondern Serverless im Sinne von Function as a Service (FaaS) und Backend as a Service (BaaS). Ich richte keinen NoSQL-Datenbankcluster mehr ein, sondern ich mache einen Knopfdruck und dann ist das Ding da (inklusive Betrieb, Monitoring, Security-Fixes, Lastverteilung und Multi-Data-Center). Ich brauche ein Data Warehouse: klick – es ist da. Ich brauche ein Ticketingsystem: klick – es ist da. Ich brauche ein komplettes CRM: klick – es ist da. Wenn ich diese verschiedensten Sachen nun ineinander integrieren möchte, dann kommt FaaS eigentlich als der Integrationscode zwischen den verschiedenen Teilen. Das heißt, früher hatte wir diese „Make or Buy“-Überlegungen, wo man seine Fertigungstiefe reduzierte, indem man Commercial Software off the Shelf (COTS-Software, im Deutschen eine komische Abkürzung, bei der immer ganz falsche Assoziationen aufkommen) nutzte. Das, was ich früher mit den klassischen Standardsoftwareanbietern gemacht habe, finde ich heute in BaaS- und SaaS-Angeboten wieder, die ich per API konsumieren kann. Damit bekomme ich, wenn ich es denn richtig mache, eine ganz andere Art von Produktivität. Der Fairness halber muss man sagen, es ist kein Selbstläufer. Integration kann auch aufwendig sein. Trotzdem können Firmen damit häufig ganz andere Reaktionsgeschwindigkeiten und eine ganz andere Produktivität schaffen.

Man sieht teilweise sogar, wie auch sehr traditionelle Unternehmen die Microservices-Welle aufgrund der Komplexität ausgelassen und einfach übersprungen haben, und dann die Cloud im Serverless-Gedanken aufnehmen. Der Entwickler muss sich nicht darum kümmern, wie es funktioniert, da das die Infrastruktur dahinter tut. Er kann sich wirklich darauf fokussieren, Integrations- und Anwendungslogik zu schreiben. Wenn es mal zu kompliziert für Serverless, also reine FaaS, wird, dann habe ich immer noch die Container als Ausweisfläche, aber die kann ich dann immer noch in den gesamten Betriebsmodi liefern, die mir die Cloud-Anbieter liefern. Damit komme ich wieder auf ein ganz anderes Entwicklungs- und auch Produktivitätsmodell. Ich denke, in die Richtung wird eine Menge passieren. Vor allem, weil man auf der anderen Seite auch sieht, dass Backend auch immer mehr zur Commodity wird und Frontends in stark umkämpften Märkten immer kriegsentscheidender werden. Das heißt also für die nächsten JavaScript Days: Es macht schon Sinn, sich das Ganze anzuschauen und zu verstehen – so sehr ich persönlich auch Backends liebe.

Mirko Hillert: Uwe, vielen Dank für das Interview.

 

 

Interviewt von: Mirko Hillert

Mirko Hillert verantwortet seit 2007 als Leiter der Entwickler Akademie den Trainingsbereich bei Software & Support Media. Er studierte Betriebswirtschaft an der Westsächsischen Hochschule Zwickau und der Universidad Valencia sowie Marketing an der Westfälischen Wilhelms-Universität Münster. Als ehemaliger Dozent und Ausbilder für Managementprozesse treibt er seit vielen Jahren die fundierte Aus- und Weiterbildung von Entwicklern und Softwarearchitekten im IT-Markt voran, unter anderem mit innovativen Eventformaten und hochwertigen Trainingsinhalten.