Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Wissenschaftliches Cloud Computing im globalen Maßstab mit Kubernetes und Terraform (1/2)

By Matthew PorterSep 19, 202112 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Kommt Ihnen folgendes Szenario aus Ihrem Unternehmen bekannt vor?

Ein Wissenschaftler hat ein Analyse-Tool oder eine Pipeline fertig entwickelt, doch im Team weiß niemand so recht, wie sich das Ganze produktiv ausrollen lässt – idealerweise so, dass die Bereitstellung automatisiert abläuft, die zugrunde liegenden Compute-Ressourcen kostenoptimiert mitskalieren (nach oben wie nach unten), Ausfallsicherheit gegenüber unerwarteten Hardware- oder Softwarefehlern besteht und sich Updates sowie das Auslaufen alter Versionen problemlos einspielen lassen.

Wie soll Ihr wissenschaftliches Team angesichts so vieler offener Engineering-Fragen den Sprung schaffen – weg vom Test- und Entwicklungsmodus auf einer akademischen Spielwiese, hin zu wirklich verlässlichen, produktionstauglichen wissenschaftlichen Tools – und das mit möglichst geringem Personalaufwand für Aufbau und Betrieb?

Möchten Sie solche Workloads in GCP oder AWS betreiben, um vom globalen Maßstab, der extrem hohen Infrastrukturzuverlässigkeit und der Wirtschaftlichkeit dieser Plattformen gegenüber On-Prem-Compute-Clustern zu profitieren?

Vielleicht möchten Sie auch verstehen, welche zentralen DevOps-Prinzipien dem Betrieb solcher Workloads in der Cloud zugrunde liegen?

Anhand einer ausführlichen Demo zeige ich Ihnen, wie Sie ein lauffähiges Beispiel sowohl auf der Google Cloud Platform (GCP) als auch auf Amazon Web Services (AWS) einsetzen. Es demonstriert die Ausführung eines realen wissenschaftlichen Workloads (Bioinformatik) auf Basis mehrerer moderner DevOps-Prinzipien. Ohne Umschweife – legen wir los.

Auf einen Blick zeigt der Beispielcode:

  • wie sich gängige Bioinformatik-Tools (FastQC und BWA) in Container-Images verpacken und ins Image-Repository der jeweiligen Cloud hochladen lassen
  • wie sich die gesamte für die Workloads benötigte Cloud-Infrastruktur per Terraform, einem Infrastructure-as-Code-Tool, hochfahren (und ebenso schnell wieder abbauen) lässt
  • wie sich eine typische Workflow-Pipeline auf Basis dieser Images und der bereitgestellten Cloud-Infrastruktur in den vollständig verwalteten Kubernetes-Diensten beider Clouds ausrollen lässt – einem System zur Verwaltung und Orchestrierung der Container-Ausführung. Argo übernimmt dabei die Orchestrierung eines Workflows bzw. einer Task-Pipeline auf einem Kubernetes-Cluster.

Mit Docker, Terraform, Kubernetes und Argo lernen Sie:

  • wie Sie die für Ihre Workloads nötige Infrastruktur per einfacher Befehle hochfahren, aktualisieren und wieder abbauen
  • wie Sie End-to-End-Analyse-Workloads als DAG-gesteuerte Workflows ausführen, mit automatischen Wiederholungen einzelner Schritte bei unerwarteten Fehlern oder Infrastrukturausfällen
  • wie Sie Logging und Metriken Ihrer Workloads in cloud-native Log- und Metrik-Tools zentralisieren
  • wie Sie Workloads auf einer Infrastruktur betreiben, die je nach Bedarf automatisch hoch- (bis in den globalen Maßstab) und herunterskaliert (auf wenige oder gar keine Compute-Ressourcen), sodass Sie nur für die CPU-/RAM-/Storage-Ressourcen zahlen, die Ihre Aufgaben tatsächlich erfordern. Vorbei sind die Zeiten, in denen ungenutzte Server liefen und unnötig Kosten verursachten.
  • wie Sie neue Softwareversionen nahtlos ausrollen und alte Versionen schrittweise auslaufen lassen

Den Code, der diese Prinzipien demonstriert, finden Sie hier, und in Teil 2 erfahren Sie, wie Sie ihn nutzen. Wenn Sie den Überblick über die DevOps-Prinzipien überspringen und direkt in den Code einsteigen möchten, können Sie hier mit dem Lesen von Teil 1 aufhören.

Wenn Sie hingegen eine geführte Tour durch die Technologien hinter diesen DevOps-Zielen wünschen, bleiben Sie dran. Ich empfehle dringend, eine starke Tasse Kaffee bereitzustellen – Sie werden sie brauchen, um diesen ausführlichen Artikel durchzuhalten.

Im Folgenden erwartet Sie ein Crashkurs zu DevOps, Containern, Kubernetes, Terraform und dazu, warum deren Einsatz für den realen Betrieb von Software – ob Bioinformatik oder anderes – entscheidend ist.

Container: Skalierbares Deployment von Tools

Das Kernproblem, das dieser Beitrag und der zugehörige Code lösen, ist DevOps – Development Operations. Dockerisierung, Kubernetes und Terraform arbeiten gemeinsam auf dasselbe Ziel hin: das zuverlässige Deployment von Programmen im großen Maßstab zu vereinfachen. Wer die Grundlagen modernen DevOps verstehen will, braucht ein grundlegendes Verständnis von Containern. Beginnen wir damit.

Den meisten Bioinformatikern dürfte dieses Problem nur allzu vertraut sein: Sie wollen ein Open-Source-Programm einsetzen – häufig im akademischen Umfeld entstanden – mit veralteten Abhängigkeiten. Die Installation wird zum Albtraum für die Paketverwaltung, weil dessen Versionsanforderungen an Python, Perl und andere Abhängigkeiten mit den lokal installierten neueren Versionen kollidieren.

Vermutlich haben Sie sich bisher mit virtuellen Umgebungen über Anaconda oder Pythons venv-Befehl beholfen. Doch leider hat dieser Ansatz seine Tücken. Irgendwann wird diese Vorgehensweise unhaltbar, und der Pflegeaufwand übersteigt den Nutzen. In Entwicklungs- und Testumgebungen mögen virtuelle Umgebungen vertretbar sein – im großen Maßstab sind sie schlicht nicht tragfähig.

Ich habe viele Unternehmen – große wie kleine – auf die harte Tour zu dieser Erkenntnis kommen sehen. Sie haben viel zu lange daran festgehalten, bevor sie das Handtuch warfen.

Auftritt der Containerisierung

Ein Container ist ein isolierter Software-Prozess, der den gesamten Code und alle Abhängigkeiten enthält, die zum schnellen Ausführen der Anwendung nötig sind – mit nahezu Bare-Metal-Performance in jeder Computing-Umgebung.

Ein Container- bzw. Docker-Image ist eine Definition, wie ein Container aufgebaut und ausgeführt werden soll. Container sind Prozesse, die die im Container-Image verpackten Anweisungen ausführen.

Ein Container-Image entsteht aus einem einfachen Dockerfile, das Folgendes definiert:

  • das Basis-OS, auf dem das Image basiert (das hindert einen auf dem Image basierenden Container nicht daran, auf anderen Host-Betriebssystemen zu laufen)
  • die zu installierenden Pakete
  • die Shell-Befehle, die beim Start eines auf dem Image basierenden Containers ausgeführt werden

Aus diesem Dockerfile erzeugt docker build ein unveränderliches Image.

Wie sorgen Sie für Redundanz und horizontale Skalierung des im Image verpackten Dienstes?

Indem Sie ausführbare Instanzen eines Container-Images über mehrere Container auf mehreren Hosts in mehreren verschiedenen Rechenzentren betreiben und den Traffic per Load Balancing zwischen diesen Containern verteilen. Angesichts der globalen Reichweite der Cloud-Anbieter ist das Potenzial für horizontale Skalierbarkeit und Ausfallsicherheit mit Containern praktisch grenzenlos.

Sie könnten beispielsweise ein Container-Image erstellen, das ein Tool wie FastQC samt aller Abhängigkeiten bündelt. Aus diesem FastQC-Image gestartete Container lassen sich dann praktisch in jeder Compute-Umgebung in beliebiger Größenordnung betreiben – unabhängig von Hardware, Software oder Betriebssystem des Hostsystems. Ein echter Segen.

Ein solcher Container wird ähnlich ausgeführt wie das FastQC-Tool selbst. Es ist beispielsweise unkompliziert, Eingabedateien an einen FastQC-Container zu übergeben und die Ausgabedateien wieder zurückzubekommen.

Jeder Cloud-Anbieter verfügt über eine eigene Container-Registry, in die Sie Images pushen und aus der Sie Images pullen können. Das funktioniert ähnlich wie das Pushen und Pullen von git-Repository-Branches. Sie führen zum Beispiel docker pull <image_name> aus, um ein in der Container-Registry Ihrer Cloud gehostetes FastQC-Image herunterzuladen. So lässt sich das Image auf eine Vielzahl von Compute-Ressourcen in Ihrer Cloud-Umgebung ziehen, sodass FastQC sofort mit docker run <image_name> einsatzbereit ist – ohne kryptische Methoden zur Verwaltung der Abhängigkeitsinstallation.

Die Ähnlichkeit zu git-Repos endet hier nicht. Container lassen sich auch mit Tags versehen, etwa latest oder v1.0.1, sodass Sie nachvollziehen können, welches Container-Image welcher Code-Version entspricht. Mit docker run <image_name>:<tag_name> ziehen und starten Sie einen Container basierend auf einer bestimmten Image-Version.

Container vs. virtuelle Maschinen (VMs)

Falls Container für Sie stark nach virtuellen Maschinen klingen – die ja ebenfalls Software bündeln und in unterschiedlichsten Compute-Umgebungen ausführbar machen – fragen Sie sich vielleicht, worin die Unterschiede bestehen und warum Container für skalierbares DevOps bevorzugt werden.

  • Sowohl Container als auch VMs lassen sich auf einem einzelnen Host betreiben, doch Container schaffen das ohne den großen Performance-Verlust, den VMs durch das Mitführen eines eigenen Betriebssystems zusätzlich zur verpackten Software einbüßen. Container teilen sich dagegen das Host-OS und sind dadurch leichtgewichtig; typischerweise sehen Sie nur etwa 0,5 % Performance-Einbuße.

Bei einer virtuellen Maschine auf einem modernen Hypervisor können Sie froh sein, wenn der Performance-Verlust nur bei 1–3 % liegt. In größerem Maßstab haben diese Unterschiede einen erheblichen, spürbaren Einfluss auf die Kosten.

  • Container lassen sich deutlich schneller bauen und deployen. Sie ermöglichen einen agilen Entwicklungsprozess, der mit VMs schlicht nicht funktioniert – diese können Minuten oder Stunden zum Bauen brauchen.
  • Container sind unveränderlich und durch eine einzige Datei definiert, was die Continuous Integration zuverlässiger macht. Beispiel: Sie können bei Bedarf auf eine vorherige, nachweislich funktionierende Softwareversion zurückrollen – in der Gewissheit, dass diese seit dem letzten Deployment nicht verändert worden sein kann.
  • Container fördern die bewährte Architekturpraxis, lose gekoppelte, verteilte Microservices gegenüber monolithischen Anwendungen zu bevorzugen. Das ermöglicht schnellere Deployments, wenn ein Fix oder ein neues Feature ausgerollt werden muss, und verhindert, dass ein einzelnes Problem in einer monolithischen Anwendung sämtliche anderen Bereiche mit in den Abgrund reißt, die gar nicht zwingend eng verzahnt sein müssten.

Idealerweise sollte jedes Programm, das im großen Maßstab betrieben wird, in ein eigenes Container-Image verpackt werden – so kann die Anzahl der bereitgestellten Container für dieses Programm unabhängig nach oben und unten skalieren, je nach Ressourcenbedarf.

  • Container erleichtern Logging und Metrik-Monitoring auf OS- und Anwendungsebene erheblich, da sie sich die Ressourcen des Host-OS teilen und von Grund auf mit detaillierter Log- und Metrikerfassung im Hinterkopf entworfen wurden.

Verwaltung der Container-Ausführung

Angenommen, Sie haben ein Programm wie FastQC containerisiert und mit passenden Tags wie latest und v0.11.9 versehen. Was machen wir nun damit, um eine fehlertolerante und skalierbare Ausführung von FastQC nicht nur zu ermöglichen, sondern erheblich zu vereinfachen?

Auftritt Kubernetes

Kubernetes (oft als K8s abgekürzt) ist eine 2014 von Google ins Leben gerufene Open-Source-Plattform – ursprünglich als Open-Source-Job-Scheduler und Cluster-Management-System gestartet, um die Community-Adaption automatisierten Managements containerisierter Workloads zu fördern. Es hat enorme Verbreitung erlangt, weil es Operationen im globalen Maßstab vergleichsweise einfach ermöglicht.

Kubernetes ist heute, gemessen an Autoren und Issues, das zweitbeliebteste GitHub-Repo – nur der Linux-Kernel liegt davor.

Die K8s-Dokumentation bietet eine ausgezeichnete, aber lange Erklärung, warum Kubernetes so nützlich ist. Kurz zusammengefasst ermöglicht K8s:

  • Automatisches Bin Packing: Sie stellen Kubernetes einen Cluster aus Nodes (Cloud-Servern) bereit, den es zur Ausführung containerisierter Tasks nutzen kann. Sie geben Kubernetes vor, wie viel CPU, Arbeitsspeicher und Storage jeder Container benötigt. Kubernetes platziert die Container so auf Ihren Nodes, dass die Ressourcennutzung optimiert wird.
  • Self-Healing: Kubernetes startet ausgefallene Container neu (sei es durch Softwarefehler oder Hardwareausfälle), ersetzt Container, beendet Container, die nicht auf den von Ihnen definierten Health Check reagieren, und meldet sie Clients erst, wenn sie bereit zur Auslieferung sind.
  • Service Discovery und Load Balancing: Kubernetes kann einen Container über seinen DNS-Namen oder seine eigene IP-Adresse exponieren. Bei hoher Last verteilt Kubernetes den Netzwerk-Traffic per Load Balancing auf mehrere Instanzen dieses Containers, sodass das Container-Deployment auch im großen Maßstab stabil bleibt.
  • Automatisierte Rollouts und Rollbacks: Sie beschreiben in Kubernetes den gewünschten Zustand Ihrer bereitgestellten Container, und Kubernetes überführt den tatsächlichen Zustand kontrolliert in den gewünschten. So können Sie Kubernetes etwa anweisen, neue Container für Ihr Deployment anzulegen (z. B. Container mit dem Tag \"v2\"), bestehende Container zu entfernen (z. B. solche mit \"v1\") und deren Compute-Ressourcen auf den neuen Container zu übertragen. Diese Migration auf einen neueren Container kann auf einen Schlag oder schrittweise erfolgen – etwa in einer Rate von 5 % aller bestehenden Container alle fünf Minuten. Stellen Sie während des Rollouts eine erhöhte Fehlerrate fest, ist das Zurückrollen des neuen Deployments nur einen einzigen Befehl entfernt.

Vollständig verwaltetes Kubernetes

Erfreulicherweise wird Kubernetes von den führenden Cloud-Anbietern als vollständig verwalteter Service angeboten. Dazu zählen:

  • GKE (Google Kubernetes Engine) in Google Cloud
  • EKS (Elastic Kubernetes Service) in Amazon Web Services

Diese Angebote vereinfachen die Kubernetes-Installation und das Hardware-Provisioning und abstrahieren die Erstellung des Kubernetes-Clusters. Damit können Sie sich darauf konzentrieren, skalierbare Workloads gegen einen K8s-Cluster zu starten, dessen Erstellung nur Minuten und wenige Klicks in der Konsole benötigt.

Sowohl GKE als auch EKS verfügen über eine serverlose Cluster-Control-Plane-Maschine. Sie fungiert quasi als Master-Knoten, mit mehreren Worker-Maschinen, sogenannten Nodes. Sie reichen Container zur Ausführung an die Control Plane ein, die diese dann zur Ausführung auf Nodes plant. Auf Nodes ausgeführt, werden diese Tasks als Pods bezeichnet.

Zur Wiederholung: Pods (typischerweise nur ein einzelner Container) laufen auf Nodes, und die Skalierung der Pod-Anzahl wird von der Control-Plane-Maschine gesteuert. Die vollständig verwalteten K8s-Angebote automatisieren auch die Einrichtung der Node-Auto-Skalierung (in GKE ist Auto-Scaling eingebaut; in AWS legt EKS EC2-Auto-Scaling-Templates an). Letztlich erreichen Sie mit GKE und EKS sowohl Pod- als auch Node-Auto-Scaling mit minimalem Aufwand.

Beim Einrichten eines K8s-Clusters sollten Sie zudem optionale Node-Gruppen definieren – also die Hardware-Ressourcenfamilien, die in Ihrem Cluster zum Einsatz kommen. Bei wissenschaftlichen Computing-Workloads werden Node-Gruppen typischerweise explizit angegeben.

Sie könnten beispielsweise eine \"high cpu\"-Node-Gruppe anlegen, die CPU-starke bzw. CPU-kostenoptimierte Maschinenfamilien wie die c2-Familie auf GCP oder die c5-Familie auf AWS nutzt, und dieser Node-Gruppe CPU-intensive Workloads zuweisen. Daneben könnten Sie eine separate \"GPU\"-Node-Gruppe einrichten, die die a2-highgpu-Familie auf GCP oder die p3-Familie auf AWS verwendet und an die GPU-intensive Workloads gehen. So skalieren unterschiedliche Maschinentypen unabhängig voneinander hoch und herunter – passend zu den Workloads, die auf ihnen laufen sollen.

Wenn Sie statt des Standard-Modus den Autopilot-Modus von GKE verwenden, müssen Sie für Ihren Cluster keine Node-Gruppen festlegen. Die Bereitstellung von Hardware-Ressourcen wird abstrahiert – GKE Autopilot ist Vorreiter der DevOps-Branche und führt das K8s-Ökosystem stärker in Richtung eines serverlosen Ansatzes für Computing im globalen Maßstab. Mit GKE Autopilot reichen Sie einfach Ihren K8s-Job ein, in dem die CPU-/RAM-/Storage-Anforderungen für den Container definiert sind, und GKE skaliert die Compute-Ressourcen im Hintergrund nach Bedarf hoch und herunter, um diese Container-Aufgabe gemäß Ihren Vorgaben auszuführen. ECS Fargate ist das AWS-Pendant zu GKE Autopilot.

Reproduzierbare und automatisierte Infrastruktur

Was Containerisierung und robuste Container-Ausführung auf Kubernetes zusammenhält, ist Terraform – ein Open-Source-Tool für Infrastructure as Code, mit dem sich der gewünschte Zustand Ihrer Cloud-Infrastruktur in leicht lesbarem YAML-Text definieren lässt. Mit einem einzigen Befehl können Sie Ihre Cloud-Infrastruktur erstellen, aktualisieren oder abbauen.

Während Container Softwareversionen unveränderlich, leicht reproduzierbar und leicht skalierbar machen, sorgt Terraform dafür, dass die cloudbasierte Infrastruktur, auf der diese Container laufen, ebenso leicht replizierbar, aktualisierbar oder löschbar ist – und dabei vollständig versionierbar bleibt.

Indem Sie Ihre Software in Container überführen, Ihr Workload-Management auf Kubernetes umstellen und Ihre Cloud-Infrastruktur mit Terraform kodifizieren, schaffen Sie die Grundlagen für ein Unternehmen, das vom Stealth-Modus bis zum globalen Betrieb skalieren kann – und sich zudem schnell von typischen Fehlerquellen wie fehlerhaften Releases und Hardware-Problemen erholt, mit nur sehr wenigen Kernänderungen im DevOps-Ökosystem.

Das neue Wissen in die Praxis umsetzen

Vielen Dank für Ihre Zeit – ich hoffe aufrichtig, dieser Artikel hat Ihr Verständnis von DevOps verbessert. Wenn Sie das Gelernte praktisch anwenden möchten, indem Sie einer funktionsfähigen Demo-Codebasis folgen, geht es hier zum zweiten Teil dieser Serie.

Danke fürs Lesen! Bleiben Sie mit uns in Verbindung über den DoiT Engineering Blog , den DoiT LinkedIn-Kanal und den DoiT Twitter-Kanal . Karrieremöglichkeiten finden Sie unter https://careers.doit-intl.com .