Cloud-native Anwendungen mit Kubernetes - QAware

Teil 4: Mesos: Das Betriebssystem der Cloud. Kubernetes ... großes Versprechen, das wir im Rahmen dieses Artikels beleuchten. ©. iStockphoto.com/gremlin.
1MB Größe 6 Downloads 281 Ansichten
JAVA Mag

Groove your ­Jenkins S. 44

Vert.x: Eine reaktive Lösung

S. 59

Cloud-native Anwendungen mit Kubernetes S. 76

magazin

Java | Architektur | Software-Innovation

Machine Learning

minfos Program 35! ab Seite

© iStockphoto.com/Vladyslav Otsiatsia

Warum sich Entwickler damit beschäftigen sollten

www.javamagazin.de

Ausgabe 8.2016

© iStockphoto.com/gremlin

Cloud Computing Anwendungen mit Kubernetes

Teil 3: Cloud-native Anwendungen mit Kubernetes

Steuermann, grüß mir die Wolken Kubernetes (griechisch „Steuermann“) ist ein Open-Source-Projekt aus der Feder von Google und im Prinzip ein Applikationsserver der Ära Cloud: für Microservices und alle anderen Anwendungen, die sich in Container zwängen lassen und auf einem Cluster laufen. Anwendungen sollen dabei so skalieren, resilient und effizient zu betreiben sein wie diejenigen von Google. Ein großes Versprechen, das wir im Rahmen dieses Artikels beleuchten.

von Dr. Josef Adersberger und Mario-Leander Reimer Kubernetes (kurz: K8s [1]) ist ein quelloffener Clusterorchestrierer, der maßgeblich von Google entwickelt wird und Mitte 2015 in der Version 1.0 erschienen ist. Das bedeutet, dass K8s für den Einsatz in Produktion freigegeben ist, was Google selbst und weitere Unternehmen wie die New York Times bereits tun. Rund um K8s hat sich mittlerweile die Cloud Native Computing Founda-

Artikelserie Teil 1: Der Cloud-Native-Stack Teil 2: Cloud-native Anwendungen mit Spring Cloud und Netflix OSS bauen Teil 3: Cloud-native Anwendungen mit Kubernetes Teil 4: Mesos: Das Betriebssystem der Cloud

2

javamagazin 8 | 2016

tion (CNCF [2]) unter dem Dach der Linux Foundation formiert. K8s ist die CNCF-Referenzimplementierung eines Clusterorchestrierers. Damit stehen nun hinter K8s neben Google auch weitere namhafte Unternehmen wie Mesosphere, Cisco, IBM und Intel. Doch was ist K8s genau? K8s ist ein Clusterorchestrierer, ein Applikationsserver, der Anwendungen auf einem potenziell sehr großen Cluster ausführt – auf einer Cloud. K8s agiert dabei auf der Abstraktionsebene von Anwendungen und ihren angebotenen Services, sitzt also exakt an der Schnittstelle zwischen Devs und Ops. Die Anwendungen sind dabei oft Microservices. Es sind jedoch auch andere Anwendungen gern auf K8s gesehen, Hauptsache man kann sie in einem Docker- oder rkt-Container verpacken, wie es auch für klassische JEE-Anwendungen gelingt. K8s betreibt Anwendungen automatisch. Er besitzt eine Steuerschnittstelle (REST-API, Kommandozeile

www.JAXenter.de

Cloud Anwendungen mit Kubernetes Computing

Abb. 1: Elemente einer Anwendungsblaupause

Abb. 2: Architektur von Kubernetes

und Web-UI), mit der die Automatismen angestoßen werden können und der aktuelle Status abgerufen werden kann. Was K8s automatisiert: • Container auf dem Cluster ausführen • Netzwerkverbindungen zwischen Containern aufbauen • Persistenten Speicher (Persistent Volumes) für zustandsbehaftete Container bereitstellen • Konfigurationsparameter, Schlüssel und Passwörter definieren, ändern und bereitstellen • Roll-out-Workflows wie Canary Roll-outs automatisieren • Performance und Verfügbarkeit von Serviceendpunkten überwachen und Container bei zu geringer Performance skalieren (Auto-Scaling) und im Fehlerfall reschedulen (Self-Healing) • Services managen: Service Discovery, Naming und Load Balancing

Anwendungen deklarativ beschreiben K8s macht all das auf Basis einer Anwendungsblaupause, die beim Deployment einer Anwendung mit überreicht wird. Diese Anwendungsblaupause beschreibt den Zielzustand einer Applikation im Cluster. Aufgabe von K8s ist es dann, das Cluster vom aktuellen Zustand in den Zielzustand zu überführen. Die Anwendungsblaupause macht dabei keine Annahmen über das Cluster, sondern stellt lediglich Ressourcenforderungen. Somit sind Anwendungen portierbar: Egal ob K8s nur einen Laptop, eine Private Cloud oder gar die große weite Public Cloud unter seinen Fittichen hat, solange genügend Ressourcen zur Verfügung stehen, läuft die Anwendung ohne Anpassung überall. Eine Anwendungsblaupause besteht bei K8s aus den folgenden Elementen, die über YAML- oder JSON-Dateien beschrieben werden (Abb. 1): • Pod: Gruppe an Containern, die auf demselben Knoten laufen und sich eine Netzwerkschnittstelle inklusive einer dedizierten IP, persistente Volumes und Umgebungsvariablen teilen. Ein Pod ist die atomare Scheduling-Einheit in K8s. Ein Pod kann über

www.JAXenter.de

sogenannte Labels markiert werden. Das sind frei definierbare Schlüssel-Wert-Paare. • Service: Endpunkt unter einem definierten DNS-Namen, der Aufrufe an Pods verteilt. Die für einen Service relevanten Pods werden über ihre Labels selektiert (z. B. role = apache, env != test, tier in (web, app)). • ReplicaSet (bzw. veraltetes Konstrukt Replication Controller): Stellt sicher, dass eine spezifizierte Anzahl an Instanzen pro Pod ständig läuft. Ist für Reaktionen im Fehlerfall, Skalierung und Roll-outs zuständig. • Deployment: Klammer um einen gewünschten Zielzustand im Cluster in Form eines Pods mit dazugehörigem ReplicaSet. Ein Deployment bezieht sich nicht auf Services, da diese in der K8s-Philosophie einen von Deployments unabhängigen Lebenszyklus haben.

Die Architektur von Kubernetes K8s selbst ist eine verteilte Anwendung. Abbildung  2 zeigt die Architektur von K8s. Der Masterknoten ist das Gehirn des Clusters, die normalen Nodes die Muskeln. Der Master orchestriert das Cluster, auf den Nodes laufen die Pods und Services. Auf dem Masterknoten läuft ein API-Server, über den die K8s-Automatismen per REST-API angestoßen werden können. Er bietet die zentrale Administrationsschnittstelle. Der Controllermanager steuert die eigentliche Orchestrierung. Er führt Deployments und ReplicaSets (bzw. ReplicationController) aus. Der Scheduler ist dafür zuständig, Pods auf die passenden Knoten zu verteilen. Er macht das entsprechend des Ressourcenbedarfs der Pods und den noch freien Ressourcen auf den Nodes. etcd ist ein verteilter konsistenter Konfigurationsspeicher – das Gedächtnis von K8s, in dem der aktuelle Clusterzustand verwaltet wird. Auf den Nodes laufen drei Komponenten: Der kube-­ proxy, der sich um die Weiterleitung von Ser­ vice­ auf­ rufen an die passenden Container kümmert; die Container-Engine, im Default ist es Docker, das Container ausführt; und das kubelet, ein Agent des Masters, der den Knoten verwaltet, steuert und mit dem Master kommuniziert. Neben K8s als Kernplattform ist mittler-

javamagazin 8 | 2016

3

Cloud Computing Anwendungen mit Kubernetes

Abb. 3: Dockerized Zwitscher

Abb. 4: Kubernized Zwitscher

weile ein breites Ökosystem an Werkzeugen, Erweiterungen und Frameworks rund um K8s entstanden: • Prometheus [3], Weave Scope [4] und sysdig [5] für das Monitoring und die Fehlerdiagnose • Helm [6] als Paketmanager für wiederverwendbare Applikationsteile • Maven- und Gradle-Plug-ins sowie ein Java-API zur Fernsteuerung von K8s (http://fabric8.io)

zu können und um die Last auf die beiden laufenden Pods zu verteilen. Über das Templateelement im Deployment wird der Pod definiert. Dabei können auch mehrere Container mit angegeben werden. Das ReplicaSet wird implizit über die Anzahl der Replicas definiert.

Die Kubernetisierung von Zwitscher

Im nächsten Schritt wollen wir nun unseren ZwitscherShowcase aus den vorherigen Artikeln dieser Serie mithilfe von K8s orchestrieren. Der erste Schritt ist die Erste Schritte: Hands-on Kubernetes Containerisierung: Hier werden die einzelnen MicroserDer Schritt hin zu ersten Erfahrungen mit K8s ist ein vices in Docker-Container gepackt und per Docker Releichter. Es stehen mehrere vorgefertigte K8s-Instal- gistry verfügbar gemacht. Listing 4 zeigt beispielhaft das lationen zur Verfügung, z. B. über die Google-Contai- Dockerfile für einen Zwitscher-Microservice, Listing 5, ner-Engine [7], für diverse Public IaaS Clouds oder als wie man daraus einen Container erzeugt und in eine Vagrant-Boxen  [8]. Unter dem URL https://get.k8s.io Docker Registry lädt. Abbildung 3 zeigt alle Container steht ein Shellskript für die automatische Installation be- sowie die verwendeten Basis-Images von Zwitscher. reit. Listing 1 zeigt ein Skript, mit dem ein lokales K8sBei der Containerisierung sollte ein Punkt dringend Cluster per Vagrant erstellt wird. Das funktioniert nur beachtet werden: Kenne dein Basisimage! So bringt z. B. unter Linux und Mac, eine Installation unter Windows das java:8-Docker-Basis-Image stolze 800 MB auf die wird aktuell noch nicht unterstützt. Waage. Bei sechs Microservices-Containern ist das eine Sobald die Installation erfolgreich abgeschlossen ist, Menge Holz, der Download der Images durch K8s daukann anschließend über das kubectl.sh-Kommandozei- ert später unnötig lange. Abhilfe schafft hier z. B. die lenwerkzeug mit dem Cluster interagiert werden (Lis- dockerisierte und kubernetisierte Version von Alpine ting 2). In Zeile 1 und 2 werden sowohl Informationen Linux [9] oder ein eigenes maßgeschneidertes Image wie über das Cluster als auch die aktuell laufenden Pods, in Listing 5 zu sehen. Services und Deployments abgefragt. In Zeile 4 legen wir Nun steht die eigentliche Kubernetisierung unserer ein Deployment und einen Service an und in Zeile 6 ska- Anwendung an. Hierfür teilen wir unsere Container lieren wir das Deployment von zwei auf fünf Replicas. auf Pods auf. Alle Container eines Pods teilen dasselbe Listing  3 zeigt die hierfür verwendete Anwendungs- Schicksal: Sie werden gemeinsam gestartet und gemeinblaupause: einen nginx-Server in zwei Instanzen. Zusätz- sam skaliert. Stirbt ein Container, sterben alle. Welche lich zum eigentlichen Deployment ist ein Service definiert, Container man zusammen in einen Pod packt, ist eine um auf die nginx-Instanzen über eine externe IP zugreifen Architekturentscheidung: Welche Container haben einen gemeinsamen Lebenszyklus? Welche Container sollten möglichst nah beieinander laufen, um Overhead zu vermeiden? Listing 1 Listing 2 Ein bekanntes Pattern ist das Sidekickkubectl.sh cluster-info #!/bin/sh Pattern, bei dem neben einem funktionalen kubectl.sh get pods,services,deployments export KUBERNETES_PROVIDER=vagrant Container (wie einem Microservice) noch export KUBE_ENABLE_CLUSTER_MONITORING=none weitere Container laufen, die dessen Funkkubectl.sh create -f nginx-deployment.yml curl -sS https://get.k8s.io | bash tionalität anreichern (z. B. ein Monitoringkubectl.sh get pods,services,deployments Agent oder ein Logextraktor). kubectl.sh scale deployment nginx – replicas=5 Abbildung 4 zeigt die Pods sowie die dekubectl.sh get pods finierten Services und Abhängigkeiten von

4

javamagazin 8 | 2016

www.JAXenter.de

Cloud Anwendungen mit Kubernetes Computing

Zwitscher in der Übersicht. Listing 6 zeigt die Definition für den Service und das Deployment unserer ZwitscherApplikation. Der Service kann später direkt über seinen Namen angesprochen werden. Beim Typ stehen ClusterIP, Node­Port und LoadBalancer zur Auswahl. ClusterIP exponiert den Service nur innerhalb des Clusters. Im Fall von NodePort werden die Containerports eines Pods auf jeden K8s Node exportiert. Bei Verwendung von LoadBalancer wird eine externe, lastverteilte IP für diesen Service akquiriert, sofern das vom Cluster unterstützt wird. Das Deployment definiert die Eigenschaften der Pods mit ihren Containern. In unserem Beispiel setzen wir die initiale Anzahl an Replicas auf drei. Über die Template Spec definieren wir das Docker Image, die benötigten Ressourcen, Ports, Liveness Probes und die Umgebungsvariablen unserer Container. Die Liveness Probes nutzt K8s, um herauszufinden, ob ein Pod erfolgreich gestartet ist. Alle YAML-Dateien für das kubernetisierte Zwitscher sind im Zwitscher-GitHub-Repository zu finden [10].

Listing 3 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx spec: replicas: 2 template: metadata: labels: app: nginx tier: web spec: containers: - name: nginx image: nginx ports: - containerPort: 80 --apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx tier: web spec: # external load-balanced IP if provider supports it type: LoadBalancer ports: - port: 80 selector: app: nginx tier: web

www.JAXenter.de

Nun können wir die Deployments und Services in K8s per Kommandozeile erzeugen (Listing 7). K8s kennt derzeit keine Startabhängigkeiten und keine Startreihenfolge der einzelnen Deployments. Dennoch benötigt der Zwitscher-Microservice eine gewisse Spring-

Listing 4 FROM qaware-oss-docker-registry.bintray.io/base/debian8-jre8 MAINTAINER QAware GmbH RUN mkdir -p /opt/zwitscher-service COPY build/libs/zwitscher-service-1.1.0.jar /opt/zwitscher-service/zwitscher-service.jar COPY src/main/docker/zwitscher-service.* /opt/zwitscher-service/ RUN chmod 755 /opt/zwitscher-service/zwitscher-service.jar; chmod 755 /opt/ zwitscher-service/zwitscher-service.sh

Listing 5 EXPOSE 8761 ENTRYPOINT exec /opt/zwitscher-service/zwitscher-service.sh $ docker build -t zwitscher-service:1.1.0 . $ docker tag qaware-oss-docker-registry.bintray.io/zwitscher/ zwitscher-service:1.1.0 $ docker push qaware-oss-docker-registry.bintray.io/zwitscher/zwitscher-service:1.1.0

Listing 6 apiVersion: v1 kind: Service metadata: name: zwitscher-service labels: zwitscher: service spec: # use NodePort to be able to access the # port on each node type: NodePort ports: - port: 8080 selector: zwitscher: service --apiVersion: extensions/v1beta1 kind: Deployment metadata: name: zwitscher-service spec: replicas: 3 minReadySeconds: 30 template: metadata: labels: zwitscher: service spec:

containers: - name: zwitscher-service image: "qaware-oss-dockerregistry.bintray.io/zwitscher/ zwitscher-service:1.1.0" imagePullPolicy: IfNotPresent resources: requests: memory: "256Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" ports: - containerPort: 8080 livenessProbe: httpGet: path: /admin/health port: 8080 initialDelaySeconds: 90 timeoutSeconds: 30 env: - name: EUREKA_HOST value: zwitscher-eureka - name: JAVA_OPTS value: -Xmx196m

javamagazin 8 | 2016

5

Cloud Computing Anwendungen mit Kubernetes

Cloud-Infrastruktur, um sauber zu starten, wie Eureka und den Configuration-Service. Eine Lösung für dieses Problem ist es, den Start der Services per Retry-Mechanismus resilienter zu gestalten (Listing 8). Spring versucht dann in definierten, immer länger werdenden Abständen den Configuration-Service zu erreichen. Erst wenn man eine maximale Anzahl an Versuchen erreicht, schlägt der Start fehl. Eine Alternative zu dieser Lösung ist es, nichts weiter zu tun: Sollte ein Service aufgrund von fehlenden Infrastrukturdiensten nicht starten, wird K8s nach einer gewissen Zeit den Dienst neu starten, da er nicht antwortet. Das passiert so lange, bis alle Dienste die benötigte Infrastruktur vorfinden und sauber starten. Das klingt zunächst merkwürdig, aber das System heilt sich quasi allein. Für Zwitscher haben wir die gesamte Spring-CloudInfrastruktur eins zu eins auf K8s portiert. Alternativ dazu kann auch auf Teile der Spring-Cloud-Infrastruktur verzichtet und stattdessen die K8s-Infrastruktur genutzt werden. Der Netflix-Eureka-Server kann durch den K8s-DNS-Server ersetzt werden. Der Zuul-EdgeServer lässt sich außerdem durch Ingress von K8s ersetzen, etcd kann als Konfigurationsmechansimus genutzt werden. Es gibt ein Spring-Cloud-Projekt, das die K8sBausteine besser integrieren soll, um genau so etwas zu bewerkstelligen. Es befindet sich aktuell in einem sehr frühen Stadium und ist noch nicht für ernsthafte Projekte geeignet.

nur in der Google Cloud verfügbar sind. Das wird sich aber schnell ändern. Wir konnten nicht auf alle spannenden Features eingehen. Bei Interesse sei auf die K8sDokumentation [1] und das Buch von Kelsey Hightower zu K8s verwiesen [11], das demnächst erscheinen wird. Dr. Josef Adersberger ist technischer Geschäftsführer der QAware GmbH, einem IT-Projekthaus mit Schwerpunkt auf Cloud-native ­Anwendungen und Softwaresanierung. Er hält seit mehr als zehn Jahren Vorlesungen und publiziert zu Themen des Software-Engineerings, aktuell insbesondere zu Cloud Computing. Mario-Leander Reimer ist Cheftechnologe bei der QAware. Er ist Spezialist für den Entwurf und die Umsetzung von komplexen System- und Softwarearchitekturen auf Basis von Open-Source-Technologien. Als Mitglied im Java Community Process (JCP) ist sein Ziel, die Java-Plattform weiter zu verbessern und praxistaugliche Spezifikationen zu entwickeln.

Links & Literatur [1] Kubernetes-Dokumentation: http://kubernetes.io/docs [2] Cloud Native Computing Foundation: https://cncf.io [3] Prometheus: https://prometheus.io [4] Weave Scope: https://github.com/weaveworks/scope [5] sysdig: http://www.sysdig.org [6] Helm by Deis: https://github.com/helm/helm [7] Kubernets in der GCE: http://kubernetes.io/v1.1/docs/getting-startedguides/gce.html [8] Kubernetes mit Vagrant: http://kubernetes.io/v1.1/docs/gettingstarted-guides/vagrant.html

Fazit Da K8s unter dem Dach der Linux Foun­dation weiterentwickelt wird, ist es wahrscheinlich, dass sich damit ein Standard für Clusterorchestrierung etabliert. Die Aufmerksamkeit und die Entwicklungsgeschwindigkeit ist bei K8s schon jetzt sehr hoch. K8s ist produktionsreif, wenngleich auch manche Features wie Auto-Scaling

[9] Alpine Linux Image: https://github.com/Trunkplatform/docker-alpinenode-kubernetes [10] C  loud Native Zwitscher Showcase: https://github.com/qaware/cloudnative-zwitscher [11] Kelsey Hightower: „Kubernetes: Up and Running“, O’Reilly, 2015 [12] Kubernetes Secrets: http://kubernetes.io/docs/user-guide/secrets/ [13] Fabric8 Kubeflix: https://github.com/fabric8io/kubeflix

Listing 7 $ kubectl.sh create -f zwitscher-eureka/k8s-zwitscher-eureka.yml $ kubectl.sh create -f zwitscher-config/k8s-zwitscher-config.yml $ kubectl.sh create -f zwitscher-service/k8s-zwitscher-service.yml ... $ kubectl.sh get deployments,pods $ kubectl.sh scale deployment zwitscher-service --replicas=3 $ kubectl.sh get deployments,pods

QAware GmbH [email protected]

Listing 8 spring: cloud: config: enabled: true failFast: true retry: initialInterval: 1500

6

javamagazin 8 | 2016

maxInterval: 5000 maxAttempts: 5 multiplier: 1.5 discovery: enabled: true serviceId: ZWITSCHER-CONFIG

Aschauer Straße 32 81549 München Tel.: +49 (0) 89 23 23 15 - 0 Rheinstraße 4D 55116 Mainz Tel.: +49 (0) 6131 215 69 - 0

www.JAXenter.de