Microservice-Deployment ganz einfach mit Kubernetes

2 Kommentare

Im meinem letzten Artikel “Microservice-Deployment ganz einfach mit Giant Swarm” habe ich die PaaS-Lösung Giant Swarm als Rundum-sorglos-Paket für das Deployment von Microservice-Anwendungen vorgestellt. Mit Giant Swarm kann man die komplette Kontrolle von der Bereitstellung bis zur Verwaltung der Hardware und den Betrieb der Microservice-Anwendung abgegeben und braucht sich nicht mehr selbst um Infrastruktur-Aspekte zu kümmern.

Möchte man sich eine eigene Cloud mit eigener Hardware aufbauen, weil seine Daten nicht in die öffentliche Cloud gelangen dürfen, aber trotzdem ähnlich mit der Cloud arbeiten können, wie mit Giant Swarm empfiehlt es sich ein Clustermanagementwerkzeug zu benutzen. Ein solches Clustermanagementwerkzeug ist Kubernetes (griech. Steuermann). Kubernetes wird von Google entwickelt und ist ähnlich wie Giant Swarm für das Betreiben von Microservice-Anwendungen gedacht, die aus mehreren Container bestehen und über mehre Rechner hinweg laufen. Kubernetes stellt unter anderen Funktionalitäten bereit, um Anwendungen auszuliefern, zu betreiben und zu skalieren.

Kubernetes verfolgt dabei die Idee, dass der eigentliche Benutzer sich nicht darum kümmern soll, wo seine Arbeit im Cluster ausgeführt wird. Man gibt dem Cluster einfach Arbeit in Form eines Docker-Container und das Kubernetes-Cluster kümmert sich selbstständig darum, wo die Arbeit ausführt wird.

Was muss man nun tun, um ein Kubernetes-Cluster aufzubauen? Auf den Rechnern, auf denen die Docker-Container laufen sollen, muss der so genannten Kubelet-Daemon installiert werden. Diese Rechner werden auch Minions genannt. Weiterhin braucht man noch einen Rechner auf dem verschiedene Master-Komponeten (APIs, Scheduler, usw.) installiert werden. Eine wichtige Komponente stellt dabei der so genannte API-Server dar, der REST-Endpunkte bereitstellt. Diese werden unter anderem vom Konsolen-Programm kubectl benutzt, können aber auch von eigenen Programmen angesprochen werden, um das Cluster zu steuern. Kubelet-Daemon und API-Server benutzen zur Kommunikation den verteilten Key-Value-Speicher Etcd. Ein Architektur-Überblick ist in der nachfolgenden Darstellung zu sehen.

In einem Kubernetes-Cluster werden Docker-Container immer innerhalb von so genannten Pods gruppiert. Ein Pod kann ein oder mehrere Docker-Container enthalten. Die Docker-Container, die in einem Pod gruppiert werden, laufen garantiert auf dem selben Rechner im Cluster. Die Docker-Container, die von einem Pod gruppiert werden, können sich Daten über ein so genanntes Volume teilen. Ein Volume ist ein Verzeichnis, auf das die Docker-Container gemeinsam zugreifen können.

Wenn ein Docker-Container die Ausführung abbricht, sorgt der Kubelet-Agent automatisch dafür, dass der Container neu gestartet wird. Ein Container wird aber nicht automatisch auf einen anderen Rechner bewegt, wenn ein ganzer Rechner ausfällt. Das folgende Listing zeigt die Definition eines Pods für den Warenkorb-Service der Microservice-Anwendung aus meinem Artikel “Microservices und die Jagd nach mehr Konversion”:

{ "id": "cart",
  "kind": "Pod",
  "apiVersion": "v1beta1",
  "desiredState": {
    "manifest": {
      "version": "v1beta1",
      "id": "cart",
      "containers": [ {
          "name": "cart",
          "image": "zutherb/cart-service",
          "ports": [ {
              "containerPort": 18100 } ] } ] } },
  "labels": {
    "name": "cart",
    "role": "service” } }

Wenn man die Ausfallsicherheit der laufenden Container verbessern möchte, sollte man das Erstellen und Verwalten von Pods nicht selbst in die Hand nehmen, sondern einen Replication-Controller dafür verwenden. Ein Replication-Controller sorgt dafür, dass beliebig viele Kopien von einem Pod automatisch erstellt werden oder beim Ausfall eines Rechners der Pod auf einen anderen Knoten kopiert und ausgeführt wird. Das folgende Listing zeigt die Definition eines Replication-Controllers:

{ "id": "cart",
  "kind": "ReplicationController",
  "apiVersion": "v1beta1",
  "desiredState": {
    "replicas": 2,
    "replicaSelector": {"name": "cart"},
    "podTemplate": {
      "desiredState": {
        "manifest": {
          "version": "v1beta1",
          "id": "cart",
          "containers": [ {
              "name": "cart",
              "image": "zutherb/cart-service",
              "ports": [ {
                  "containerPort": 18100 } ] } ] } },
      "labels": {
        "name": "cart",
        "uses": "redis" } } },
  "labels": {
    "name": "cart",
    "role": "backend" } }

Moderne Web-Anwendungen bestehen häufig aus einer Vielzahl von verschiedenen Microservices. Wie es der Online-Shop in meinem Artikel “Microservices und die Jagd nach mehr Konversion” zeigt, kann es mehrere Benutzerschnittstellen geben, die auf verschiedene Backend-Services zugreifen und mit verschiedenen Datenbanken kommunizieren. Pods sind nicht zwangsläufig immer an der selben Stelle, spätestens wenn ein Rechner ausfällt, muss ein Pod bewegt werden. Pods können also kommen und gehen. Gerade wenn Replication-Controller verwendet werden. Jeder Pod bekommt eine eigene IP-Adresse, die aber veränderlich ist.

Kubernetes Service - https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/services.md

Kubernetes Service – https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/services.md

Wenn ein Pod Backendfunktionalitäten für einen Frontend-Pod bereitstellt, stellt sich die Frage, wie das Frontend einen einheitlichen Zugriff auf den Backend-Pod bekommt. Zum einen kann sich die IP eines Pods ändern und zum anderen wird ein zentraler Zugriffspunkt benötigt, wenn Replikationen eines Pods auf mehrere Rechner verteilt sind. Hierzu braucht man eine Instanz, die die Last auf die Pods verteilt. Eine solche Instanz ist ein Kubernetes-Service, der einen Satz an Pods über einen Selector auswählt. Das Ziel eines Kubernetes-Service ist es auch eine Verbindung zwischen nicht-Kubernetes-Anwendungen, also beliebigen Clients und den entsprechenden Pods zur Verfügung zu stellen. Im folgenden Listing ist die Definition des Warenkorb-Service zusehen, der als Loadbalancer auf die Pods unseres vorher definierten Replication-Controller fungiert:

{ "id": "cart",
  "kind": "Service",
  "apiVersion": "v1beta1",
  "port": 18100,
  "containerPort": 18100,
  "selector": { "name": "cart" },
  "labels": {
    "name": "cart",
    "role": "backend" } }

Wenn man mit virtuellen Maschinen ausprobieren möchte, wie sich die Arbeit mit Kubernetes selbst anfühlt, gibt es in meinem Github-Repository eine Vagrant-Datei mit der man ein Kubernetes-Cluster aufbauen kann, in dem man meinen Microservice-basierten Online-Shop betreiben kann. Um das virtuelle Cluster zu erstellen, muss man einfach die Befehle ausführen, die in der ReadMe beschrieben sind. Die Vagrant-Datei ist in der Lage ein Kubernetes-Cluster mit beliebig vielen Knoten aufzubauen, dazu muss man nur die Umgebungsvariable NUM_INSTANCES entsprechend anpassen.

Als Betriebssystem für das virtuelle Kubernetes-Cluster wird CoreOS verwendet. CoreOS ist eine spezielle Linux-Distribution um große, skalierbare Cluster auf unterschiedlichen Infrastrukturen einfach zu managen. CoreOS basiert auf ChromeOS und stellt ein sehr leichtgewichtiges Hostsystem bereit, das Docker-Container zum Ausführen von Applikationen benutzt. Damit bietet CoreOS eine gute Grundlage für ein Kubernetes-Cluster.

Damit man sieht, wie einfach die Arbeit mit einem Kubernetes-Cluster ist, kann man sich das nachfolgende Video ansehen, das ich erstellt habe. Darin wird gezeigt, wie man das Replikationsverhalten des Cluster bezüglich eines Katalog-Frontend im laufenden Betrieb mit dem Konsolen-Programm kubectl ändern kann. Außerdem sieht man, wie man einen A/B Test im Cluster deployen kann. Abschließend wird die Selbstheilungsfähigkeit des Clusters gezeigt, wenn ein Container beendet wird.

Google selbst sagt über Kubernetes noch: “Kubernetes is in pre-production beta!”. Trotzdem macht Googles Kubernetes schon einen recht ordentlichen Eindruck. Das Konsolen-Programm stellt Kommandos bereit um Pods zu skalieren, die Log-Dateien der Pods zu betrachten oder Rolling-Updates der einzelen Pods durchzuführen, wie man in nächsten Video sehen kann. Gerade das Rolling Update ist eine sehr schöne Funktion, die ich mir gerade für den Betrieb in meinen Projekten schon oft gewünscht habe, da es Zero-Downtime-Deployments stark vereinfacht und man Reaktionszeit bekommt, wenn bei einem Deployment doch etwas schief geht.

Die einzelnen Features funktionieren schon ganz gut, allerdings ist Kubernetes noch recht neu und dem entsprechend ist das Ökosystem um Kubenetes noch recht klein. Man sollte sich aber definitiv damit beschäftigen und Integrationsmöglichkeiten in bestehende Lieferketten ausloten. Gerade im Bereich Continuous Delivery bekommt man ganz neue Möglichkeiten. Man kann Multi-Deployment-Pipelines aufbauen, mit denen man jeden einzelnen Feature-Branch sehr einfach als Pod deployen kann. Die Fachseite kann die Feature so schneller abnehmen und wenn alle Tests in der Produktionspipeline fehlerfrei durchlaufen, wird das Feature direkt in Produktion ausgeliefert, was die Lieferzeiten deutlich senkt.

Bernd Zuther

Kampferprobter Projektveteran, der immer fokussiert auf die Lieferung von funktionierender Software in der vorgegebenen Zeit ist. Zu seinen stärksten Waffen gehören ein sicherer Umgang mit agilen Methoden wie Scrum und sein breit gestreutes IT-Wissen, das er gezielt dazu einsetzt, auch die schwierigsten Problemstellungen pragmatisch und einfach zu lösen.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Sascha Vujevic

    8. Mai 2015 von Sascha Vujevic

    Hallo Herr Zuther,

    vielen Dank für den sehr interessanten Artikel.

    Mich würde interessieren, ob Sie Erfahrungen bzgl. Kubernetes und IPTables haben. Werden Einträge von nicht mehr existierenden Services wieder gelöscht, oder bleiben diese bestehen ?
    Dies könnte ja auf Dauer ein „Performance-Problem“ ergeben, wenn das auslesen der Tabelle lange dauert.

    Vielen Dank für Ihre Bemühungen.

    Gruß
    Sascha Vujevic

    • Bernd Zuther

      Hallo Herr Vujevic,

      mit IPTables kommt man eigentlich nicht direkt in Berührung, wenn man mit Kubenetes arbeitet. Für den Endbenutzer ist ein Service eine JSON-Datei und wenn der Service nach dem Erstellen mit dem Konsolen-Programm im Cluster verfügbar ist, läuft er auf einer IP-Adresse mit der man einfach arbeiten kann.

      Deshalb kann ich zur genauen Implementierung an der Stelle auch leider nichts sagen.

      Viele Grüße
      Bernd Zuther

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.