Verwaltung Cloud-nativer Microservices mit Istio – Teil 2

Eine Einführung in Istio: Der Bookstore – Tools in Hülle und Fülle

Michael Hofmann, Markus Lackner

© Shutterstock.com / ProStockStudio

Je umfangreicher und verflechteter eine Microservices-Architektur wird, desto unübersichtlicher wird es. Man spricht hierbei vom sogenannten „Service Mesh“. Viele solcher Architekturen werden heutzutage nativ in der Cloud entwickelt und an diejenigen, die diese Microservices dann verwalten sollen, werden besondere Anforderungen gestellt. Das Tool Istio soll dabei behilflich sein, die Übersicht zu behalten.

Im ersten Teil dieser Artikelserie zu Istio haben wir uns mit den Themen Service Mesh und Istio als Werkzeug zur Beherrschung solcher Service Meshes beschäftigt. Nachdem zuvor die Architektur von Istio genauer beschrieben wurde, werden wir uns diesmal mit den zur Verfügung stehenden Tools von Istio beschäftigen. Anhand von Beispielen erläutern wir dabei die verschiedenen Konfigurationsmöglichkeiten, wozu die auf www.istio.io angebotene Bookstore-Anwendung dient, die in einem Kubernetes Cluster installiert wird. Die Bookstore-Anwendung stellt ein einfaches Service Mesh dar, bestehend aus einer Web-Applikation und mehreren REST Services in verschiedenen Versionen. Dies sollte ausreichen, um die verschiedene Aspekte von Istio genauer kennenzulernen.

Automatic Sidecar Injection

Ein wesentliches Feature von Istio ist die Möglichkeit, das Sidecar automatisch bereitzustellen, durch das alle Requests und Responses der Anwendung geleitet werden. Beim Deployment einer Anwendung wird anhand des benutzten Kubernetes Namespace festgelegt, ob ein Sidecar provisioniert werden soll oder nicht. Um beispielsweise den default Namespace in Kubernetes für Sidecar Injection zu aktivieren, muss das Label istio-injection in diesem default Namespace auf enabled gesetzt werden:

kubectl label namespace default istio-injection=enabled

Wird jetzt eine neue Anwendung installiert bzw. ein Pod neu erzeugt, stellt Istio automatisch einen Envoy Proxy als Sidecar zur Verfügung. Über iptables-Regeln wird der Datenverkehr dieses Pods über das Sidecar abgewickelt und Kubernetes kümmert sich ab sofort um die Verwaltung dieses Sidecars. Auf diese Weise erhält man die gesamte Bandbreite an Istio Features, ohne eine Zeile Code in der Anwendung verändern zu müssen.

Das Sidecar, das essentiell für den Betrieb von Istio ist, muss logischerweise überwacht werden. Diesen Part übernimmt Kubernetes, indem es auf Prozessebene die laufenden Container überwacht und sie, je nach konfigurierter Policy, gegebenenfalls neu startet. Darüber hinaus ruft Kubernetes, zur Erkennung von disfunktionalen Prozessen, regelmäßig die sogenannten Health Checks auf. Diese Requests werden ebenfalls durch das Sidecar geleitet, wodurch diese Services indirekt funktional überwacht werden. Im Fehlerfall wird ein Restart eines neuen Pods veranlasst, in dem auch wieder das Sidecar aktiviert ist.

Distributed Tracing

Kommt es in verteilten Systemen zu Problemen, ist es oft schwierig, die zugrundeliegende Ursache herauszufinden. Die Fehlersuche innerhalb eines Service Mesh stellt somit die Entwicklung und den Betrieb regelmäßig vor große Herausforderungen. Ein Request kann auf unterschiedlichen Pfaden über mehrere Service Aufrufe durch das Mesh traversieren. Dabei können Probleme an unterschiedlichen Stellen auftreten, wobei sich die Ursachenforschung insbesondere für Timeouts und Latenz-Probleme ohne entsprechende Unterstützung sehr schwierig gestaltet. Auch das Aufspüren von Fehlerkaskaden ist ohne ein geeignetes Werkzeug kaum zu bewerkstelligen. Istio nimmt hierzu Distributed Tracing Tools wie beispielsweise Zipkin oder auch Jaeger in die Pflicht. Dazu sammelt Istio über das Sidecar alle notwendigen Informationen und übermittelt diese an das konfigurierte Tracing-System. Konkret erfolgt das über spezielle Tracing Header (z.B. x-request-id und x-b3-traceid), welche über die gesamte Request-Kette von Service zu Service weitergeleitet werden müssen. Dieser sogenannte Span kann dann mittels Zipkin- oder Jaeger-GUI angezeigt werden.

In der Beispielanwendung Bookstore ist das Weiterleiten der Header-Parameter für den Span von Hand implementiert worden. Diese sollte jedoch von einem Framework transparent für den Entwickler übernommen werden. Zum Beispiel werden in einer auf MicroProfile basierenden Applikation diese Header-Daten automatisch verwaltet, sobald in der Anwendung das MicroProfile Open Tracing aktiviert worden ist. Somit wird der Entwickler, durch das Zusammenspiel von MicroProfile und Istio, von dieser rein technischen Aufgabe befreit.

Fault Injection

Hat man nun beispielsweise über einen Zipkin Trace ein Problem oder Fehlverhalten des Service Mesh festgestellt, kann es notwendig sein, dieses Verhalten in einer Testumgebung nachzustellen. Reproduktion von Fehlern bzw. das Testen der Resilienz einer Anwendung lässt sich oft nur mit speziellen Tools und Frameworks oder mittels aufwendiger Konfigurationen bewerkstelligen. Ohne zusätzlichen Code können solche Problemstellungen nur schwer oder gar nicht gelöst werden. Durch das zentrale Routing aller Requests durch das Sidecar wird Istio in die Lage versetzt, eine einfache Code-neutrale Lösung hierfür anzubieten. Über sogenannte Virtual Services kann der Envoy Proxy angewiesen werden, Fehler oder Verzögerungen zu erzeugen.

In folgendem Beispiel werden 90% aller Requests auf den Reviews-Service in der Version v1 um 7 Sekunden verzögert und die restlichen 10% mit dem Http Status Code 500 vom Sidecar beantwortet. Die restlichen 90% werden nach 7 Sekunden Verzögerung normal an den Service weitergeleitet und entsprechend verarbeitet:

# fault-injection-reviews.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews 
spec:
  hosts:
    - reviews
  http:
  - fault:
      delay:
        fixedDelay: 7s
        percent: 90 
      abort:
        percent: 10
        httpStatus: 500
    route:
      - destination:
          host: reviews
          subset: v1
  - route:
      - destination:
          host: reviews
          subset: v1

Wie an der apiVersion (hier config.istio.io/v1alpha3) ersichtlich, ist das API in diesem Stadium noch nicht endgültig fixiert. In der aktuellen Version (0.8.0) wurden zum Beispiel RouteRule-Objekte durch VirtualService ersetzt. Der Aufbau ist zwar weitgehend identisch, allerdings gibt es nur mehr eine virtuelle Service-Definition. Durch die Definition einer DestinationRule werden verschiedene Deployments eines Services, hier anhand des Version-Labels, zu einem Subset gruppiert. Auf dieses Subset kann dann der virtuelle Service referenzieren. Dies dient dazu, die verschiedenen Instanzen eines Services zu logischen Gruppen (nach Version, Stage usw.) zu gruppieren und für den virtuellen Service bereitzustellen.

Diese Regel kann in Form einer yaml-Datei mit folgendem Befehl im Service Mesh installiert werden:

istioctl create -f fault-injection-reviews.yaml

Weitere Eingriffe in den Service Mesh sind nicht erforderlich und ab sofort kann die verteilte Anwendung auf diverse Latenz- bzw. Fehler-Szenarien getestet und deren Verhalten analysiert werden.

Circuit Breaker

Grundlegender Bestandteil jedes Service Mesh ist die Verwendung des Circuit Breakers zur Absicherung der einzelnen Services untereinander gegen Überlastung. Der Circuit Breaker stellt somit sicher, dass ein Problem möglichst lokal begrenzt bleibt und sich nicht auf andere Services ausbreitet. Die Etablierung eines Circuit Breaker erfolgt in Istio über die Erweiterung der DestinationRule:

# reviews-circuit-breaker.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
   - name: v1
     labels:
       version: v1
     trafficPolicy:
       connectionPool:
         http:
           http1MaxPendingRequests: 2
         tcp:
           maxConnections: 5
       outlierDetection:
         http:
           baseEjectionTime: 15m
           consecutiveErrors: 3
           interval: 1m
           maxEjectionPercent: 100

Nach Ausführung des entsprechenden Istio-Befehls (analog zum obigen Beispiel) erlaubt der reviews v1-Service nun maximal 5 aktive und weitere 2 Verbindungen in Wartestellung. Jede zusätzliche Anfrage wird mit dem Http Status Code 503 (Service unavailable) beantwortet. Des Weiteren wird jede Service-Instanz nach 3 aufeinander folgenden 5xx-Fehlern für 15 Minuten aus dem Routing Pool entfernt.

Im Gegensatz zu anderen Frameworks, in denen der Circuit Breaker beim Aufrufer definiert wird, kann der auf Istio basierende Circuit Breaker an die Destination gebunden werden. Damit ist es möglich, direkt am aufgerufenen Service festzulegen, welches Verhalten im Fehler- bzw. Überlastungsfall angewandt werden soll. Dies geschieht somit über alle Aufrufquellen hinweg. Auf diese Weise können genauere und auf den Service zugeschnittene Definitionen der Verbindungen angewandt werden, unabhängig von den Clients. In herkömmlichen Frameworks müssen dazu alle aktiven Circuit Breaker in den Clients mit ihren individuellen Konfigurationen betrachtet werden, was sich in der Praxis oft recht schwierig gestaltet. Wer dennoch an diesem Client-zentrierten Ansatz festhalten möchte, kann das mit der Definition unterschiedlicher Regeln im virtuellen Service inklusive entsprechender DestinationRules erreichen.

Monitoring

Eine der zentralen Fragen über den Zustand der einzelnen Services lässt sich in herkömmlichen Infrastrukturen mitunter nicht so einfach beantworten. Im Istio-Umfeld übermitteln alle Sidecar Proxys ihre Telemetriedaten an die Istio-Mixer-Komponente. An diese wiederum können verschiedene Monitoring-Systeme angebunden werden, sodass sich Telemetriedaten beispielsweise mit Prometheus als Time-Series-Datenbank bzw. Monitoring Tool auswerten lassen.

Eine der Stärken von Prometheus ist die mächtige Query-Sprache, welche eine Vielzahl von Auswertungsmöglichkeiten bietet. Es können so an die jeweiligen Anforderungen angepasste Alerts definiert und durch die automatisch bereitgestellten Telemetriedaten alle Services lückenlos überwacht werden. Als grafisches Frontend stellt Grafana, basierend auf Prometheus-Daten, ein Istio Dashboard mit der Basisinstallation zur Verfügung und bietet eine übersichtliche Darstellung des Service Mesh.

Content Based Routing

Der Wunsch, mittels Canary Release neue Funktionen einer kleinen Gruppe von Anwendern zur Verfügung zu stellen, wird bei großen Service-Mesh-Anwendungen immer häufiger geäußert. Vor allem dann, wenn die verteilte Anwendung so komplex geworden ist, dass es kaum möglich ist, eine zweite Umgebung parallel zu betreiben. Hierzu bietet Istio die Möglichkeit, das Routing über die Auswertung von Request-Feldern wie Header, URIs oder Cookies zu steuern:

# reviews-content-based-routing.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews 
spec:
  hosts:
    - reviews
  http:
  - match:
    - headers:
        cookie:
          regex: "^(.*?;)?(user=jason)(;.*)?$"
    route:
      - destination:
          host: reviews
          subset: v2
  - route:
      - destination:
          host: reviews
          subset: v1

Das Beispiel oben leitet anhand des Cookies (die Bookstore-Applikation speichert den angemeldeten User in einem Cookie) Requests des Users jason an die Version v2 des reviews-Services weiter. Mit einfachen Konfigurationen könnte man beispielsweise alle Requests von einem iPhone Client mit dem User Agent iphone aufgrund eines Fehlers wieder an die vorherige Version des review-Services routen. Alle anderen Client Requests wären davon nicht betroffen. Eine weitere nützliche Funktion besteht in der Möglichkeit, das aufgerufene Request URI per Rewrite-Regel umzuschreiben, um somit Requests eines Services an verschiedene Endpunkte verteilen zu können.

Service Graph

Wird das Routing komplexer, kann recht schnell die Übersicht verloren gehen. Hier stellt das Service Graph Add-on, basierend auf Prometheus-Metriken, eine passende Visualisierung zur Verfügung.

Quelle: Istio

Auch Zipkin erstellt anhand der Tracing-Daten den zugehörigen Service-Graphen.

Mit Jaeger, als Alternative zu Zipkin, kann man sich die Zusammenhänge zwischen den Services, inklusive der Anzahl der Requests, darstellen lassen. Zusätzlich sind hier auch die beteiligten Istio-Komponenten ersichtlich:

Request Mirroring

Trotz aller Bemühungen schon während der Testphase möglichst viele Fehler zu entdecken, gibt es immer noch Situationen, die so nur in Produktion auftreten können. Alle Konstellationen in Unit-, Regressions- und Abnahmetests abzubilden, ist nicht immer möglich und ein Restrisiko, einen wesentlichen Fehlerfall nicht abgedeckt zu haben, besteht trotzdem noch. Istio bietet hier die Möglichkeit, jeden Request auf eine zusätzlichen Service-Version zu spiegeln. Außerhalb des kritischen original Request-Pfads wird der Request dupliziert und durchgeführt. Die dabei entstehende Antwort vom duplizierten Request wird von Istio verworfen. Erweitert man eine Routing Regel um

...
spec:
  hosts:
  - reviews
  http:
  - mirror:
      host: reviews
      subset: v2
    route:
    - destination:
        host: reviews
        subset: v1
      weight: 100
...

wird jeder Request an v1 auch an v2 gespiegelt. Auf elegante Art und Weise wird somit der zweite Service getestet.

Retries und Timeouts

Während der Circuit Breaker eine Absicherung durch Limitierung der Parallelität bzw. Backpressure installiert, ist für aufrufende Services eine Limitierung der maximalen Aufrufdauer erforderlich. Dazu kann ein Virtual Service um Timeouts erweitert werden:

...
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
    timeout: 3s
...

Somit werden Requests, die mehr als 3 Sekunden benötigen, mit dem HTTP Error Code 504 beantwortet. Für eine individuelle Steuerung pro Request steht der Request Header x-envoy-upstream-rq-timeout-ms zur Verfügung bzw. kann der Regelsatz über die verschiedenen Kriterien entsprechend angepasst werden, etwa beispielsweise anhand der Quelle des Requests.

Eine Alternative bietet das Konfigurieren von automatischen Wiederholungen:

...
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
    retries:
      attempts: 3
      perTryTimeout: 2s 
...

Nun wird der Service bis zu 3-mal mit einem Timeout von 2 Sekunden pro Versuch aufgerufen. Dieses Pattern lässt sich ohne weitere Probleme bei idempotente-Services anwenden und sorgt somit für ein stabileres Verhalten der Anwendung. Auch hier kann für eine individuelle Steuerung ein spezieller Request Header (x-envoy-max-retries) eingesetzt werden. Eine Möglichkeit Fallbacks im Fehlerfall zu realisieren, ist in Istio derzeit leider noch nicht implementiert. Hierfür muss auf andere Frameworks zurückgegriffen werden.

Externe Requests

Standardmäßig ist der Zugriff auf externe Services (außerhalb des Kubernetes Clusters) deaktiviert. Über Kubernetes-Objekte vom Typ ServiceEntry kann der Zugriff per Service oder auch über Wildcards freigegeben werden.

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP

Externe Services können, analog zu Virtual Services, innerhalb des Clusters angelegt werden. Dadurch wird die Verwendung von RequestTimeouts, FaultInjection oder auch Mirroring auf die externen Services möglich:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-ext
spec:
  hosts:
    - httpbin.org
  http:
  - timeout: 3s
    route:
      - destination:
          host: httpbin.org

Auf Seite der Infrastruktur lassen sich so, wiederum unabhängig von Applikation und Implementierung, externe Services zentral konfigurieren.

Security

Istio unterstützt mittlerweile auch ein breites Spektrum an Security Features. Mit der Aktivierung von Mutual TLS Authentifizierung werden beispielsweise über die Citadel-Komponente (früher Istio-CA bzw. Istio-auth) Zertifikate und Identitätsschlüssel für alle Envoy Proxys zur Verfügung gestellt und so für eine sichere, verschlüsselte und authentifizierte Kommunikation im Service Cluster gesorgt. Weitere Features umfassen unter anderen den Support von White- und Blacklists, JWT Tokens oder auch Role Based Access Control (RBAC).

Schlussbemerkung

Der Schwerpunkt von Istio liegt ganz eindeutig in der Verwaltung und Konfiguration von Service Meshes. Durch die Vielfalt der angebotenen Funktionalitäten wird definitiv ein Mehrwert für verschiedenste Applikationen generiert. Das stimmt sowohl für neue als auch für bereits vorhandene Applikationen. Einfach gesagt lässt sich alles, was in einem Kubernetes Pod läuft und geeignete Kommunikations-Protokolle verwendet, besser überwachen und beeinflussen. Folglich kann eine moderne, auf Microservices basierende Anwendung, am meisten von den Istio-Features profitieren. In Kombination mit beispielsweise MicroProfile befreit Istio die Anwendungen von dem sonst oft notwendigen technischen Boiler Plate Code.

Meist erkennt man erst nach einer gewissen Zeit und Erfahrung mit dem jeweiligen Service Mesh die notwendigen Werte für die korrekte Konfiguration und Implementierung der Resilienz. Da Tests dieser Funktionen in der Regel schwierig sind, ergeben sich die benötigten Einstellungen erst in Produktion, wo sich dann aber die Behebung oft aufwändiger gestaltet. Mit Istio gelingen sich solche Anpassungen sehr viel einfacher.

DevOpsCon Whitepaper 2018

Free: 40+ pages of DevOps expert knowledge

Learn about Containers,Continuous Delivery, DevOps Culture, Cloud Platforms & Security with articles by experts like Kai Tödter (Siemens), Nicki Watt (OpenCredo), Tobias Gesellchen (Europace AG) and many more.

Für das komplexe Feld der Release-Strategien und der unterschiedlichen Tests-Szenarien bietet Istio passende Unterstützung und erleichtert somit dem DevOps-Team das Leben. Zu guter Letzt hilft Istio mit seinen vielen Auswertungsmöglichkeiten das Verhalten der verteilten Anwendung zu verstehen, um somit die richtigen Maßnahmen zum Betreiben des Service Mesh zu ergreifen.

Der Reifegrad für den Produktionseinsatz von Istio ist derzeit noch nicht erreicht (derzeit ist Version 0.8.0 die aktuellste). Trotzdem kann es jetzt schon lohnenswert sein, sich mit den Möglichkeiten zu befassen. Die breite Unterstützung des Projekts, unter anderem durch IBM, Google und Lyft lässt auf eine schnelle Fertigstellung hoffen.

Die Bezeichnungen Kubernetes und Istio haben ihren Ursprung übrigens im Griechischen und bedeuten Steuermann bzw. Segel. Mit Kubernetes als verlässlichen Partner am Steuer setzt man mit Istio die Segel und steuert das Schiff sicher, auch durch unruhige und stürmische Gewässer, in den sicheren Hafen. Ahoi!

Geschrieben von
Michael Hofmann
Michael Hofmann
Michael Hofmann ist freiberuflich als Berater, Coach, Referent und Autor tätig. Seine langjährigen Projekterfahrungen in den Bereichen Softwarearchitektur, Java Enterprise und DevOps hat er im deutschen und internationalen Umfeld gesammelt. Mail: info@hofmann-itconsulting.de
Markus Lackner
Markus Lackner
Markus Lackner ist Softwareentwickler beim österreichischen Bankenrechenzentrum ARZ. Seit mehr als 18 Jahren arbeitet er in verschiedenen Projekten an Design, Architektur und Entwicklung komplexer Java Enterprise Applikationen. Momentane Schwerpunkte liegen auf Microservice Architekturen, docker, kubernetes und Aufbau von CI/CD Umgebungen. Mail: markus.lackner@tetraeder.at
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: