Redaktion – Die siebenteilige Artikelserie ist nun komplett:
Sie können den kompletten Artikelsatz sowie Informationen zur Implementierung von Microservices mit NGINX Plus auch als E-Book herunterladen – Microservices: Vom Entwurf bis zur Bereitstellung . Und sehen Sie sich unsere Serie zur Microservices-Referenzarchitektur und die Seite mit Microservices-Lösungen an.
Dies ist der sechste Artikel einer Reihe über das Erstellen von Anwendungen mit Microservices. Der erste Artikel stellt das Microservices-Architekturmuster vor und diskutiert die Vorteile und Nachteile der Verwendung von Microservices. Die folgenden Artikel behandeln verschiedene Aspekte der Microservices-Architektur: Verwendung eines API-Gateways , Interprozesskommunikation , Service Discovery und ereignisgesteuertes Datenmanagement . In diesem Artikel betrachten wir Strategien zur Bereitstellung von Microservices.
Das Bereitstellen einer monolithischen Anwendung bedeutet, dass mehrere identische Kopien einer einzelnen, normalerweise großen Anwendung ausgeführt werden. Normalerweise stellen Sie N Server (physisch oder virtuell) bereit und führen auf jedem davon M Instanzen der Anwendung aus. Die Bereitstellung einer monolithischen Anwendung ist nicht immer ganz unkompliziert, aber viel einfacher als die Bereitstellung einer Microservices-Anwendung.
Eine Microservices-Anwendung besteht aus Dutzenden oder sogar Hunderten von Diensten. Dienste werden in verschiedenen Sprachen und Frameworks geschrieben. Bei jeder handelt es sich um eine Minianwendung mit eigenen spezifischen Anforderungen an Bereitstellung, Ressourcen, Skalierung und Überwachung. Beispielsweise müssen Sie je nach Nachfrage für jeden Dienst eine bestimmte Anzahl von Instanzen ausführen. Außerdem muss jede Serviceinstanz mit den entsprechenden CPU-, Speicher- und E/A-Ressourcen ausgestattet werden. Eine noch größere Herausforderung besteht darin, dass die Bereitstellung der Dienste trotz dieser Komplexität schnell, zuverlässig und kostengünstig sein muss.
Es gibt einige verschiedene Bereitstellungsmuster für Microservices. Sehen wir uns zunächst das Muster „Mehrere Serviceinstanzen pro Host“ an.
Eine Möglichkeit zum Bereitstellen Ihrer Microservices ist die Verwendung des Musters „Mehrere Serviceinstanzen pro Host“ . Wenn Sie dieses Muster verwenden, stellen Sie einen oder mehrere physische oder virtuelle Hosts bereit und führen auf jedem mehrere Dienstinstanzen aus. In vielerlei Hinsicht ist dies der traditionelle Ansatz zur Anwendungsbereitstellung. Jede Dienstinstanz wird auf einem bekannten Port auf einem oder mehreren Hosts ausgeführt. Die Host-Rechner werden üblicherweise wie Haustiere behandelt .
Das folgende Diagramm zeigt die Struktur dieses Musters.
Es gibt mehrere Varianten dieses Musters. Eine Variante besteht darin, dass jede Serviceinstanz ein Prozess oder eine Prozessgruppe ist. Sie können beispielsweise eine Java-Dienstinstanz als Webanwendung auf einem Apache Tomcat- Server bereitstellen. Eine Node.js- Dienstinstanz kann aus einem übergeordneten Prozess und einem oder mehreren untergeordneten Prozessen bestehen.
Die andere Variante dieses Musters besteht darin, mehrere Dienstinstanzen im selben Prozess oder in derselben Prozessgruppe auszuführen. Sie könnten beispielsweise mehrere Java-Webanwendungen auf demselben Apache Tomcat-Server bereitstellen oder mehrere OSGI-Pakete im selben OSGI-Container ausführen.
Das Muster „Mehrere Serviceinstanzen pro Host“ hat sowohl Vorteile als auch Nachteile. Ein großer Vorteil ist die relativ effiziente Ressourcennutzung. Mehrere Dienstinstanzen teilen sich den Server und sein Betriebssystem. Noch effizienter ist es, wenn ein Prozess oder eine Prozessgruppe mehrere Dienstinstanzen ausführt, beispielsweise mehrere Webanwendungen, die denselben Apache Tomcat-Server und dieselbe JVM gemeinsam nutzen.
Ein weiterer Vorteil dieses Musters ist, dass die Bereitstellung einer Serviceinstanz relativ schnell erfolgt. Dazu kopieren Sie den Dienst einfach auf einen Host und starten ihn. Wenn der Dienst in Java geschrieben ist, kopieren Sie eine JAR- oder WAR-Datei. Für andere Sprachen, wie Node.js oder Ruby, kopieren Sie den Quellcode. In beiden Fällen ist die Anzahl der über das Netzwerk kopierten Bytes relativ gering.
Da außerdem kein Overhead entsteht, ist das Starten eines Dienstes im Allgemeinen sehr schnell. Wenn der Dienst ein eigener Prozess ist, starten Sie ihn einfach. Andernfalls, wenn der Dienst eine von mehreren Instanzen ist, die im selben Containerprozess oder in derselben Prozessgruppe ausgeführt werden, stellen Sie ihn entweder dynamisch im Container bereit oder starten Sie den Container neu.
Trotz seiner Attraktivität weist das Muster „Mehrere Serviceinstanzen pro Host“ einige erhebliche Nachteile auf. Ein großer Nachteil besteht darin, dass die Serviceinstanzen kaum oder gar nicht isoliert sind, es sei denn, jede Serviceinstanz ist ein separater Prozess. Sie können zwar die Ressourcenauslastung jeder Serviceinstanz genau überwachen, Sie können jedoch die von jeder Instanz genutzten Ressourcen nicht begrenzen. Es ist möglich, dass eine fehlerhaft funktionierende Serviceinstanz den gesamten Speicher oder die gesamte CPU des Hosts verbraucht.
Es gibt überhaupt keine Isolierung, wenn mehrere Serviceinstanzen im selben Prozess ausgeführt werden. Beispielsweise könnten alle Instanzen denselben JVM-Heap gemeinsam nutzen. Eine fehlerhaft funktionierende Serviceinstanz könnte leicht die anderen im selben Prozess ausgeführten Dienste beschädigen. Darüber hinaus haben Sie keine Möglichkeit, die von jeder Serviceinstanz verwendeten Ressourcen zu überwachen.
Ein weiteres erhebliches Problem bei diesem Ansatz besteht darin, dass das Betriebsteam, das einen Dienst bereitstellt, die genauen Einzelheiten dazu kennen muss. Dienste können in zahlreichen Sprachen und Frameworks geschrieben werden, daher gibt es zahlreiche Details, die das Entwicklungsteam mit dem Betrieb teilen muss. Diese Komplexität erhöht das Fehlerrisiko während der Bereitstellung.
Wie Sie sehen, weist das Muster „Mehrere Serviceinstanzen pro Host“ trotz seiner Vertrautheit einige erhebliche Nachteile auf. Sehen wir uns nun andere Möglichkeiten zur Bereitstellung von Microservices an, bei denen diese Probleme vermieden werden.
Eine weitere Möglichkeit zum Bereitstellen Ihrer Microservices ist das Muster „Serviceinstanz pro Host“ . Wenn Sie dieses Muster verwenden, führen Sie jede Dienstinstanz isoliert auf ihrem eigenen Host aus. Es gibt zwei verschiedene Spezialisierungen dieses Musters: Serviceinstanz pro virtueller Maschine und Serviceinstanz pro Container.
Wenn Sie das Muster „Serviceinstanz pro virtueller Maschine“ verwenden, verpacken Sie jeden Service als Image einer virtuellen Maschine (VM), z. B. ein Amazon EC2 AMI . Jede Serviceinstanz ist eine VM (z. B. eine EC2-Instanz), die mit diesem VM-Image gestartet wird. Das folgende Diagramm zeigt die Struktur dieses Musters:
Dies ist der primäre Ansatz, den Netflix zum Bereitstellen seines Video-Streaming-Dienstes verwendet. Netflix verpackt jeden seiner Dienste mit Aminator als EC2 AMI. Jede laufende Serviceinstanz ist eine EC2-Instanz.
Es gibt eine Vielzahl von Tools, mit denen Sie Ihre eigenen VMs erstellen können. Sie können Ihren Continuous Integration (CI)-Server (z. B. Jenkins ) so konfigurieren, dass Aminator aufgerufen wird, um Ihre Dienste als EC2 AMI zu verpacken. Packer.io ist eine weitere Option für die automatische Erstellung von VM-Images. Im Gegensatz zu Aminator unterstützt es eine Vielzahl von Virtualisierungstechnologien, darunter EC2, DigitalOcean, VirtualBox und VMware.
Das Unternehmen Boxfuse bietet eine überzeugende Methode zum Erstellen von VM-Images, die die im Folgenden beschriebenen Nachteile von VMs überwindet. Boxfuse verpackt Ihre Java-Anwendung als minimales VM-Image. Diese Images lassen sich schnell erstellen und booten schnell. Außerdem sind sie sicherer, da sie nur eine begrenzte Angriffsfläche bieten.
Das Unternehmen CloudNative verfügt über die Bakery, ein SaaS-Angebot zum Erstellen von EC2-AMIs. Sie können Ihren CI-Server so konfigurieren, dass die Bakery nach den Tests für Ihren Microservice-Durchlauf aufgerufen wird. The Bakery verpackt Ihren Service dann als AMI. Wenn Sie ein SaaS-Angebot wie The Bakery verwenden, müssen Sie keine wertvolle Zeit mit der Einrichtung der AMI-Erstellungsinfrastruktur verschwenden.
Das Muster „Serviceinstanz pro virtueller Maschine“ bietet eine Reihe von Vorteilen. Ein großer Vorteil von VMs besteht darin, dass jede Serviceinstanz vollständig isoliert ausgeführt wird. Es verfügt über eine feste Menge an CPU und Speicher und kann keine Ressourcen von anderen Diensten stehlen.
Ein weiterer Vorteil der Bereitstellung Ihrer Microservices als VMs besteht darin, dass Sie eine ausgereifte Cloud-Infrastruktur nutzen können. Clouds wie AWS bieten nützliche Funktionen wie Lastausgleich und automatische Skalierung.
Ein weiterer großer Vorteil der Bereitstellung Ihres Dienstes als VM besteht darin, dass dadurch die Implementierungstechnologie Ihres Dienstes gekapselt wird. Sobald ein Dienst als VM verpackt wurde, wird er zu einer Blackbox. Die Verwaltungs-API der VM wird zur API für die Bereitstellung des Dienstes. Die Bereitstellung wird wesentlich einfacher und zuverlässiger.
Das Muster „Serviceinstanz pro virtueller Maschine“ weist jedoch einige Nachteile auf. Ein Nachteil ist die weniger effiziente Ressourcennutzung. Jede Serviceinstanz hat den Overhead einer gesamten VM, einschließlich des Betriebssystems. Darüber hinaus haben VMs in einem typischen öffentlichen IaaS feste Größen und es ist möglich, dass die VM nicht ausreichend ausgelastet ist.
Darüber hinaus werden bei einem öffentlichen IaaS für VMs normalerweise Gebühren berechnet, unabhängig davon, ob sie ausgelastet oder im Leerlauf sind. Ein IaaS wie AWS bietet automatische Skalierung, aber es ist schwierig, schnell auf Nachfrageänderungen zu reagieren . Folglich müssen Sie häufig eine Überbereitstellung von VMs durchführen, was die Bereitstellungskosten erhöht.
Ein weiterer Nachteil dieses Ansatzes ist, dass die Bereitstellung einer neuen Version eines Dienstes normalerweise langsam ist. Aufgrund ihrer Größe dauert das Erstellen von VM-Images normalerweise lange. Außerdem dauert die Instanziierung von VMs normalerweise lange, was wiederum an ihrer Größe liegt. Außerdem dauert der Start eines Betriebssystems normalerweise einige Zeit. Beachten Sie jedoch, dass dies nicht immer zutrifft, da es auch leichte VMs wie die von Boxfuse gibt.
Ein weiterer Nachteil des Musters „Serviceinstanz pro virtueller Maschine“ besteht darin, dass normalerweise Sie (oder jemand anderes in Ihrer Organisation) für einen Großteil undifferenzierter Schwerstarbeit verantwortlich sind. Sofern Sie kein Tool wie Boxfuse verwenden, das den Aufwand für das Erstellen und Verwalten der VMs übernimmt, liegt die Verantwortung bei Ihnen. Diese notwendige, aber zeitaufwändige Tätigkeit lenkt von Ihrem Kerngeschäft ab.
Sehen wir uns nun eine alternative Möglichkeit zum Bereitstellen von Microservices an, die leichter ist und dennoch viele der Vorteile von VMs bietet.
Wenn Sie das Muster „Serviceinstanz pro Container“ verwenden, wird jede Serviceinstanz in ihrem eigenen Container ausgeführt. Container sind ein Virtualisierungsmechanismus auf Betriebssystemebene . Ein Container besteht aus einem oder mehreren Prozessen, die in einer Sandbox ausgeführt werden. Aus Sicht der Prozesse verfügen sie über einen eigenen Port-Namespace und ein eigenes Root-Dateisystem. Sie können den Speicher und die CPU-Ressourcen eines Containers begrenzen. Einige Containerimplementierungen verfügen auch über eine Begrenzung der E/A-Rate. Beispiele für Containertechnologien sind Docker und Solaris Zones .
Das folgende Diagramm zeigt die Struktur dieses Musters:
Um dieses Muster zu verwenden, verpacken Sie Ihren Dienst als Container-Image. Ein Container-Image ist ein Dateisystem-Image, das aus den zum Ausführen des Dienstes erforderlichen Anwendungen und Bibliotheken besteht. Einige Container-Images bestehen aus einem vollständigen Linux-Root-Dateisystem. Andere sind leichter. Um beispielsweise einen Java-Dienst bereitzustellen, erstellen Sie ein Container-Image, das die Java-Runtime, möglicherweise einen Apache Tomcat-Server und Ihre kompilierte Java-Anwendung enthält.
Nachdem Sie Ihren Dienst als Container-Image verpackt haben, starten Sie einen oder mehrere Container. Normalerweise führen Sie auf jedem physischen oder virtuellen Host mehrere Container aus. Sie können zur Verwaltung Ihrer Container einen Cluster-Manager wie Kubernetes oder Marathon verwenden. Ein Cluster-Manager behandelt die Hosts als einen Ressourcenpool. Dabei wird basierend auf den vom Container benötigten und auf den auf jedem Host verfügbaren Ressourcen entschieden, wo jeder Container platziert wird.
Das Muster „Serviceinstanz pro Container“ hat sowohl Vorteile als auch Nachteile. Die Vorteile von Containern ähneln denen von VMs. Sie isolieren Ihre Serviceinstanzen voneinander. Sie können die von jedem Container verbrauchten Ressourcen einfach überwachen. Darüber hinaus kapseln Container wie VMs die Technologie, die zur Implementierung Ihrer Dienste verwendet wird. Die Containerverwaltungs-API dient auch als API zum Verwalten Ihrer Dienste.
Im Gegensatz zu VMs sind Container jedoch eine leichtgewichtige Technologie. Container-Images lassen sich normalerweise sehr schnell erstellen. Auf meinem Laptop dauert es beispielsweise nur 5 Sekunden, eine Spring Boot- Anwendung als Docker-Container zu verpacken. Container starten außerdem sehr schnell, da kein langwieriger Betriebssystem-Bootmechanismus vorhanden ist. Wenn ein Container gestartet wird, wird der Dienst ausgeführt.
Die Verwendung von Containern bringt einige Nachteile mit sich. Obwohl die Containerinfrastruktur schnell ausgereift ist, ist sie noch nicht so ausgereift wie die Infrastruktur für VMs. Zudem sind Container nicht so sicher wie VMs, da sie den Kernel des Host-Betriebssystems miteinander teilen.
Ein weiterer Nachteil von Containern besteht darin, dass Sie für die undifferenzierte, schwere Arbeit der Verwaltung der Container-Images verantwortlich sind. Sofern Sie keine gehostete Containerlösung wie Google Container Engine oder Amazon EC2 Container Service (ECS) verwenden, müssen Sie außerdem die Container-Infrastruktur und möglicherweise die VM-Infrastruktur verwalten, auf der sie ausgeführt wird.
Außerdem werden Container häufig auf einer Infrastruktur bereitgestellt, bei der die Preisgestaltung pro VM erfolgt. Folglich entstehen Ihnen, wie bereits zuvor beschrieben, wahrscheinlich zusätzliche Kosten durch die Überbereitstellung von VMs, um Lastspitzen bewältigen zu können.
Interessanterweise dürfte die Unterscheidung zwischen Containern und VMs verschwimmen. Wie bereits erwähnt, lassen sich Boxfuse-VMs schnell erstellen und starten. Das Projekt Clear Containers zielt darauf ab, leichtgewichtige VMs zu erstellen. [Anm. d. Red.: Wie im Dezember 2017 angekündigt, wird die Entwicklung von Clear Containers nun im Open-Source-Projekt Kata Containers fortgesetzt.] Es besteht auch ein wachsendes Interesse an Unikernels . Docker, Inc. hat vor Kurzem Unikernel Systems übernommen.
Es gibt auch das neuere und immer beliebter werdende Konzept der serverlosen Bereitstellung, ein Ansatz, der die Entscheidung zwischen der Bereitstellung von Diensten in Containern oder VMs umgeht. Sehen wir uns das als Nächstes an.
AWS Lambda ist ein Beispiel für serverlose Bereitstellungstechnologie. Es unterstützt Java-, Node.js- und Python-Dienste. Um einen Microservice bereitzustellen, verpacken Sie ihn als ZIP-Datei und laden Sie ihn auf AWS Lambda hoch. Sie geben außerdem Metadaten an, die unter anderem den Namen der Funktion angeben, die zum Verarbeiten einer Anforderung (auch als Ereignis bezeichnet) aufgerufen wird. AWS Lambda führt automatisch genügend Instanzen Ihres Microservices aus, um Anfragen zu verarbeiten. Die Abrechnung für jede Anforderung erfolgt einfach auf Basis der benötigten Zeit und des verbrauchten Speichers. Natürlich steckt der Teufel im Detail und Sie werden gleich feststellen, dass AWS Lambda Einschränkungen hat. Aber die Vorstellung, dass weder Sie als Entwickler noch sonst jemand in Ihrem Unternehmen sich um irgendeinen Aspekt von Servern, virtuellen Maschinen oder Containern kümmern muss, ist unglaublich verlockend.
Eine Lambda-Funktion ist ein zustandsloser Dienst. Normalerweise verarbeitet es Anfragen durch den Aufruf von AWS-Diensten. Beispielsweise könnte eine Lambda-Funktion, die aufgerufen wird, wenn ein Bild in einen S3-Bucket hochgeladen wird, ein Element in eine DynamoDB-Bildertabelle einfügen und eine Nachricht in einem Kinesis-Stream veröffentlichen, um die Bildverarbeitung auszulösen. Eine Lambda-Funktion kann auch Webdienste von Drittanbietern aufrufen.
Es gibt vier Möglichkeiten, eine Lambda-Funktion aufzurufen:
cron
-ähnlichen ZeitplanWie Sie sehen, ist AWS Lambda eine praktische Möglichkeit, Microservices bereitzustellen. Durch die anfragebasierte Preisgestaltung zahlen Sie nur für die Arbeit, die Ihre Dienste tatsächlich leisten. Da Sie außerdem nicht für die IT-Infrastruktur verantwortlich sind, können Sie sich auf die Entwicklung Ihrer Anwendung konzentrieren.
Es gibt jedoch einige erhebliche Einschränkungen. Es ist nicht für die Bereitstellung von Diensten mit langer Laufzeit vorgesehen, z. B. eines Dienstes, der Nachrichten von einem Nachrichtenbroker eines Drittanbieters verbraucht. Anfragen müssen innerhalb von 300 Sekunden abgeschlossen sein. Dienste müssen zustandslos sein, da AWS Lambda theoretisch für jede Anfrage eine separate Instanz ausführen könnte. Sie müssen in einer der unterstützten Sprachen verfasst sein. Außerdem müssen die Dienste schnell gestartet werden, da es sonst zu einer Zeitüberschreitung kommen und sie beendet werden können.
Die Bereitstellung einer Microservices-Anwendung ist eine Herausforderung. Es gibt Dutzende oder sogar Hunderte von Diensten, die in verschiedenen Sprachen und Frameworks geschrieben sind. Bei jeder handelt es sich um eine Minianwendung mit eigenen spezifischen Anforderungen an Bereitstellung, Ressourcen, Skalierung und Überwachung. Es gibt mehrere Bereitstellungsmuster für Microservices, darunter „Serviceinstanz pro virtueller Maschine“ und „Serviceinstanz pro Container“. Eine weitere interessante Option zum Bereitstellen von Microservices ist AWS Lambda, ein serverloser Ansatz. Im nächsten und letzten Teil dieser Reihe schauen wir uns an, wie man eine monolithische Anwendung in eine Microservices-Architektur migriert.
Redaktion – Die siebenteilige Artikelserie ist nun komplett:
Sie können den kompletten Artikelsatz sowie Informationen zur Implementierung von Microservices mit NGINX Plus auch als E-Book herunterladen – Microservices: Vom Entwurf bis zur Bereitstellung . Und sehen Sie sich unsere Serie zur Microservices-Referenzarchitektur und die Seite mit Microservices-Lösungen an.
Gastblogger Chris Richardson ist der Gründer des ursprünglichen CloudFoundry.com , einer frühen Java PaaS (Platform as a Service) für Amazon EC2. Heute berät er Organisationen bei der Verbesserung der Entwicklung und Bereitstellung von Anwendungen. Unter https://microservices.io bloggt er außerdem regelmäßig über Microservices.
„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."