Hallo Microservice-Bereitstellung Teil 1: Docker

Dieser Artikel ist der erste einer dreiteiligen Reihe über die Bereitstellung von Microservices.

Bevor wir anfangen, möchte ich einen kurzen Haftungsausschluss machen: Es gibt Affiliate-Links zu Büchern in dieser Reihe. Die Bücher sind relevant und sie waren für mich persönlich sehr nützlich.

In diesem Teil behandeln wir die Grundlagen, welche Probleme Microservices lösen (und deren Ursachen), ein Docker-Image erstellen und es lokal als Container ausführen.

In Teil 2 bringen wir unsere Anwendung online, indem wir sie in einem Kubernetes-Cluster bereitstellen, den wir selbst in Google Cloud eingerichtet haben. Außerdem beschäftigen wir uns mit den Grundlagen der Skalierung und Aktualisierung unserer Anwendung.

In Teil 3 verwenden wir Drone.io, um eine einfache CI/CD-Pipeline einzurichten. Es testet unsere Anwendung und führt alle Änderungen aus, die wir am Master-Branch unseres Repos vornehmen.

Diese Serie ist ganz im Sinne der „Hallo Welt“ der Microservices. Wir werden nicht jeden Aspekt des Entwerfens, Bereitstellens, Testens und Überwachens Ihrer Dienste behandeln (es gibt ganze Bücher zu diesem Thema geschrieben), aber es gibt Ihnen eine ziemlich gute Vorstellung davon, wie Sie anfangen können. Ich weise Sie auch auf relevante Ressourcen hin, damit Sie Ihr Wissen selbst vertiefen können.

Mikro was?

Wenn Sie mit den Konzepten von Microservices vertraut sind, können Sie diesen Abschnitt gerne überspringen.

Am Anfang waren Monolithen.

Stellen Sie sich vor, Sie haben irgendwo in der Cloud eine Anwendung, die Folgendes erledigt: Authentifizierung, Autorisierung, E-Mails an Mitglieder senden, Gebühren und Mitgliedschaftsdetails verfolgen, große Mengen an unterhaltsamen und zum Nachdenken anregenden Videos verteilen, verfolgen, wer welches Video gesehen hat, und Abgeben von Empfehlungen an Mitglieder basierend auf ihrem Betrachtungsverlauf.

Nennen wir unsere völlig hypothetische Anwendung Webflix.

Nehmen wir an, wir schreiben die gesamte Webflix-Anwendung als eine einzige Anwendung – einen Monolithen. Da ist ziemlich viel los, was bedeutet, dass jedes Mal, wenn Änderungen vorgenommen werden, viele Tests durchgeführt werden müssten, z. B. wenn ich eine Änderung an dem für die Empfehlung verantwortlichen Code vornehme, sollte ich die gesamte Unit-Test-Suite ausführen .

Schließlich könnte es eine gewisse Kopplung zwischen dem Empfehlungscode und einem anderen Teil des Systems geben. Sobald meine Tests bestanden sind, kann ich den Code bereitstellen, aber das ist auch ziemlich mühsam, weil es sicher eine Menge davon gibt.

Ich müsste absolut alles neu erstellen und eine große Sache bereitstellen oder etwas Komplexität einführen, indem ich eine Art Verpackungssystem für verschiedene Codeeinheiten einführe. Das Zurücksetzen ist ähnlich ineffizient oder komplex. Sobald unsere Anwendung live ist, gibt es weitere Ineffizienzen.

Nehmen wir an, wir haben ein paar Leute, die sich angemeldet und bezahlt haben, und sie beobachten an einem schönen Samstagnachmittag ihre jeweiligen Guilty Pleasures. Bei einer Anwendung wie Webflix würde man erwarten, dass sich zu bestimmten Zeiten und an bestimmten Tagen mehr Menschen Videos ansehen, daher wäre es sinnvoll, die Möglichkeit zum Hoch- und Herunterskalieren einzubauen.

Wenn wir jedoch skalieren, skalieren wir alles, da es sich um einen Monolithen handelt. Wäre es nicht schön, einfach die Teile des Systems hochzuskalieren, die in Spitzenzeiten tatsächlich unter Druck stehen? Beispielsweise sollte das Subsystem, das für die tatsächliche Bereitstellung von Videos verantwortlich ist, skaliert werden, die Funktion „Passwort vergessen“ jedoch nicht.

Geben Sie Mikrodienste ein.

Die Idee hinter Microservices ist, dass viele Anwendungen davon profitieren könnten, in kleinere Anwendungen (sogenannte Dienste) aufgeteilt zu werden, die gut zusammenarbeiten. Zurück zu Webflix hätten wir also einen Dienst für Empfehlungen, einen Dienst für die Verbreitung von Videos und einen Dienst für die Verwaltung von Zahlungen.

Jeder Dienst ist durch seinen Zweck und seine Verantwortlichkeiten definiert. Manchmal sind die Dinge jedoch nicht so klar. Sollten beispielsweise Authentifizierung und Autorisierung ein oder zwei Dienste sein? Darüber hinaus muss Webflix eine No-Pay-No-Watch-Richtlinie durchsetzen. Das bedeutet, dass das Autorisierungssystem das Zahlungssystem kennen muss. Und die Zahlungs-, Passwort- und Benutzerregistrierungssysteme müssen alle in der Lage sein, E-Mails zu senden.

Wir sehen also, dass die Grenzen zwischen Diensten manchmal ziemlich klar und offensichtlich sind. Aber manchmal können sie ein wenig verschwommen sein. Die Dienste müssen auch effektiv kommunizieren.

Nachdem wir die Dinge wie oben aufgeschlüsselt haben, lassen Sie uns einen einzelnen, gut definierten Dienst näher betrachten: das Empfehlungssystem. Wenn ich eine Änderung am Code des Empfehlungsdienstes vornehme, muss ich nur die Komponententests für dieses eine System ausführen, ich kann diesen Dienst einzeln bereitstellen und zurücksetzen und ich kann ihn individuell nach Bedarf skalieren. Ich kann sogar mehrere Versionen für einen Hauch von A/B-Tests bereitstellen.

Gewinnen! Aber warten Sie, es sind andere Systeme beteiligt – auf das Empfehlungssystem muss von einem Front-End aus zugegriffen werden, das dem Endbenutzer zugewandt ist, und es muss wissen, was der Benutzer bereits gesehen hat.

Dies führt zu einer gewissen Komplexität, da die Möglichkeit besteht, dass eine Änderung am Empfehlungsdienst die Kommunikation mit der Funktionalität, mit der er kommuniziert, unterbricht. Es gibt nur eine begrenzte Menge, die mit Mocks in Unit-Tests gemacht werden kann. Das bedeutet, dass wir eine neue Testebene aufbauen müssen, die sicherstellt, dass die verschiedenen Dienste tatsächlich gut funktionieren.

Dann gibt es noch das Problem der Versionskompatibilität. Nehmen wir an, wir führen „recommended_service:1.2.0“ und „video_history_service:1.3.0“ aus und diese spielen sich gut. Aber dann wird empfohlen_service:1.3.0 erstellt und das bricht das Gesamtsystem. Also wird ein Patch erstellt, um das Problem zu beheben (recommend_service:1.3.1), aber in der Zwischenzeit wird empfohlener_service:1.4.1 veröffentlicht.

In Ordnung, also sagen wir, wir bekommen alles bereitgestellt. Jetzt müssen diese Dienste effektiv kommunizieren. Wir könnten einfach HTTP verwenden, aber was ist, wenn das Netzwerk nur leicht unzuverlässig ist? Was ist, wenn sich ein Dienst mitten in einem Upgrade befindet? Was ist, wenn der Datenverkehr verloren geht oder wiedergegeben wird? Da muss man sich Gedanken machen.

Was passiert, wenn etwas kaputt geht, ohne eine Ausnahme auszulösen? Was ist zum Beispiel, wenn “Barney the Dinosaur” einem Hardcode-Zombie-Fan empfohlen wird? Der Verlauf, die Benutzerpräferenzen und die Empfehlungsdienste sollten alle untersucht werden. Was ist, wenn es eine Latenzspitze gibt? Es können beliebig viele Einzeldienste schuld sein.

Sowohl Microservices als auch Monolithe haben Vor- und Nachteile (Monolithen bringen natürlich nicht die Komplexität von Microservices mit sich – Kommunikation, Bereitstellung und Tests sind viel einfacher). Die beiden Stile eignen sich für unterschiedliche Projekte. Aber egal, welchen Weg Sie einschlagen, Sie müssen Ihren Code bereitstellen. Und genau darum geht es in diesem Tutorial.

Wir stellen Docker vor

Wenn Sie mit Docker vertraut sind, können Sie diesen Abschnitt gerne überspringen 😃

deploy-micro-metal.png

Sie führen ständig Anwendungen auf Ihrem Computer aus. Ihr Computer ist eine Bare-Metal-Maschine. Anwendungen laufen auf Ihrem Betriebssystem (OS) und das Betriebssystem verwaltet die Hardware. Es gibt eine ganze Reihe von Problemen bei der Bereitstellung von Anwendungen auf Bare-Metal, auf die ich hier nicht eingehen werde. Viele dieser Probleme werden durch virtuelle Maschinen (VMs) überwunden.

deploy-micro-VM.png

Eine VM wird auf einem Hypervisor ausgeführt, der Hypervisor wird auf dem Host-Betriebssystem ausgeführt und das Host-Betriebssystem steuert die Hardware. Auf einem einzelnen Hypervisor können mehrere unabhängige und isolierte VMs vorhanden sein. Die Aufgabe des Hypervisors besteht in erster Linie darin, den verschiedenen VMs Ressourcen zuzuweisen. Jetzt wird Ihre Anwendung auf einem Betriebssystem ausgeführt, das auf einer VM installiert ist.

Aber die VMs sind ziemlich schwer – wäre es nicht schön, all diese zusätzlichen Betriebssysteme zu entfernen? Container machen das. Ein Container kann als eine wirklich leichtgewichtige VM betrachtet werden.

deploy-micro-containers.png

Container sind standardmäßig viel kleiner als VMs – ein Container enthält kein vollständiges Betriebssystem, während eine VM dies tut. Container benötigen daher viel weniger Rechenleistung, um ausgeführt zu werden – sie sind in der Regel nur Megabyte groß und benötigen nur wenige Sekunden zum Starten.

VMs sind in der Regel Gigabyte groß und es kann Minuten dauern, bis sie gestartet werden, da Betriebssysteme groß sind! Container nutzen Bibliotheken und Pakete auf dem Host-Betriebssystem, um auf Ressourcen zuzugreifen. Sie verwenden dann ihre eigenen Bibliotheken und Pakete, um bei Bedarf separate Betriebssysteme zu emulieren, anstatt unnötigen Bloat zu installieren. Beispielsweise könnten Sie ein Ubuntu-Image auf Ihrem Windows-Computer ausführen, ohne Ubuntu installieren zu müssen.

VMs (wie Bare Metal) neigen dazu, undokumentierte Aufblähung anzusammeln, wenn Dienstprogramme installiert, aktualisiert und im Allgemeinen im laufenden Betrieb herumgepfuscht werden. Container sind vollständig im Code spezifiziert und einfach genug, um neu zu erstellen, sodass ein manuelles Herumspielen mit ihren Interna normalerweise überhaupt nicht erforderlich ist.

Die Kleinheit von Containern ist natürlich wirklich großartig, wenn es um Skalierungsanwendungen geht. Wenn wir „recommended_service“ als VM hosten und eine Traffic-Spitze hätten, müssten die Benutzer nur mit etwas zusätzlicher Latenz und möglicherweise einigen Fehlern fertig werden, während wir zusätzliche VMs online brachten. Wenn wir andererseits Container verwenden würden, könnten wir sie viel schneller online bringen und möglicherweise sogar zusätzliche Instanzen im Falle von Spitzen am Laufen halten, da Container billig sind.

Docker ist der De-facto-Industriestandard für Containerplattformen, und das ist diejenige, mit der wir uns in diesem Artikel befassen werden.

Praktisch: Lassen Sie uns eine API erstellen

Docker ermöglicht die Erstellung von Images. Bilder werden instanziiert, um Container zu erstellen (wenn Sie mit objektorientierter Programmierung vertraut sind, dann sind Bilder wie Klassen und Container wie Objekte). In diesem Abschnitt werden wir einen Container erstellen und ausführen, und der Container enthält einen Dienst, den wir bereitstellen möchten.

Wir beginnen mit der Erstellung eines einfachen Python Umarmung Anwendung und lokal ausführen. Hug ist ein Framework zum Erstellen superschneller, selbstdokumentierender APIs, unabhängig davon, wie diese APIs verfügbar gemacht werden.

In unserem Fall verwenden wir es, um eine einfache API über verfügbar zu machen HTTP. Grundsätzlich erstellen Sie einfache alte Python-Funktionen und definieren dann, wie (und ob) sie durch die Verwendung von Dekoratoren verfügbar gemacht werden sollen. Es ist ziemlich neu in der Szene, gilt aber als produktionsreif.

Dieses Tutorial geht davon aus, dass Sie Python3 installieren können, virtualenvwrapper, und Git alleine. Sie können mehr über virtuelle Python 3-Umgebungen erfahren hier. Virtualenvwrapper stellt einfach Tools bereit, um die Verwaltung Ihrer virtuellen Umgebungen zu vereinfachen.

Wir beginnen damit, unsere App lokal auszuführen. Wir eigentlich nicht brauchen um unsere App lokal zu installieren und auszuführen, aber es kann der Diskussion um die Erstellung unseres Images etwas Klarheit verleihen.

## create and activate your virtual environment. Application dependencies will be installed here instead of globally. This has nothing to do with containers really, it's just a special directory and path configuration. Also it is good practice

mkvirtualenv --python=`which python3` codementor_deployment_tutorial

## clone the application

git clone 

## install the application requirements

cd tutorial-codementor-deploying-microservices
pip install -r requirements.txt

## run the application

hug -f main.py

Sie sollten eine Ausgabe mit etwa der folgenden erhalten:

hug -f main.py                                                                                                                                          [11:03]

/#######################################################################\
          `.----``..-------..``.----.
         :/:::::--:---------:--::::://.
        .+::::----##/-/oo+:-##----:::://
        `//::-------/oosoo-------::://.       ###    ###  ###    ###    #####
          .-:------./++o/o-.------::-`   ```  ###    ###  ###    ###  ##
             `----.-./+o+:..----.     `.:///. #########  ###    ### ##
   ```        `----.-::::::------  `.-:::://. ###    ###  ###    ### ###   ####
  ://::--.``` -:``...-----...` `:--::::::-.`  ###    ###  ###   ###   ###    ##
  :/:::::::::-:-     `````      .:::::-.`     ###    ###    #####     ######
   ``.--:::::::.                .:::.`
         ``..::.                .::         EMBRACE THE APIs OF THE FUTURE
             ::-                .:-
             -::`               ::-                   VERSION 2.4.0
             `::-              -::`
              -::-`           -::-
\########################################################################/

 Copyright (C) 2016 Timothy Edmund Crosley
 Under the MIT License


Serving on :8000...
127.0.0.1 - - [07/Jun/2018 11:04:27] "GET /index HTTP/1.1" 200 26

Lassen Sie uns die Indexseite abfragen, um sicherzustellen, dass der Code tatsächlich ausgeführt wird. Öffnen Sie ein neues Terminal und dann:

curl 0.0.0.0:8000/index

Dies sollte die Antwort zurückgeben:

{"codementor": "so delicious"}

Ist das nicht schön?

Praktisch: Lassen Sie uns unsere API als Docker-Container ausführen

Da Sie nun wissen, dass die Anwendung grundsätzlich funktioniert, ist es an der Zeit, ein Image zu erstellen. Beachten Sie, dass das Repo eine Dockerfile enthält. In der Docker-Datei finden Sie Folgendes:

FROM python:3
EXPOSE 8080

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["gunicorn", "-b", "0.0.0.0:8080", "main:__hug_wsgi__"]

Wenn Sie mehr über das Ziehen und Verschieben von Bildern erfahren möchten, werfen Sie einen Blick auf die Dokumente.

Okay, jetzt können wir das Bild erstellen:

docker build -t codementor-tutorial:1 .

Und dann, um das Image auszuführen (d. h. einen Container zu erstellen):

docker run -p 8080:8080 codementor-tutorial:1

Sie sollten die Ausgabe wie folgt sehen:

[2018-06-15 07:11:59 +0000] [1] [INFO] Starting gunicorn 19.8.1
[2018-06-15 07:11:59 +0000] [1] [INFO] Listening at:  (1)
[2018-06-15 07:11:59 +0000] [1] [INFO] Using worker: sync
[2018-06-15 07:11:59 +0000] [9] [INFO] Booting worker with pid: 9

Öffnen Sie ein anderes Terminal, kräuseln Sie die API und stellen Sie sicher, dass alles noch funktioniert:

curl 0.0.0.0:8080/index

Dies gibt aus:

{"codementor": "so delicious"}

Beachten Sie, dass wir hier Port 8080 anstelle von 8000 verwenden. Sie können jeden gewünschten Port angeben.

Eine sehr kurze Einführung in die Versionen

Jetzt haben wir also ein Image, das wir auf jedem Computer (Bare Metal oder VM) ausführen können, der Docker-Images ausführen kann. Unser Image hat einen Namen und eine Versionsnummer. Wenn wir Änderungen an der Funktionalität unseres Bildes vornehmen wollten, würden wir diese Änderungen im Code angeben und dann das Bild mit einem anderen Versions-Tag neu erstellen, z.

docker build -t codementor-tutorial:2 .

Zusammenfassung

Gut erledigt 😃 Sie haben es geschafft, ein Docker-Image zu erstellen, zu markieren und als Container auszuführen.

Es gibt noch einiges mehr über Docker zu sagen, das den Rahmen dieses Textes sprengen würde. Ich schlage vor, Sie werfen einen Blick auf die amtliche Dokumentation wenn Sie weitere Einzelheiten benötigen.

Wenn Sie etwas Strukturierteres brauchen, gibt es viele wirklich hervorragende Bücher da draußen, die Ihnen weiterhelfen können.

Sind Sie bereit für den nächsten Schritt? In Teil 2 werden wir unsere kleine Anwendung in der Cloud bereitstellen, skalieren und aktualisieren!

Dieser Beitrag enthält Affiliate-Links zu Büchern, die mir wirklich gefallen, was bedeutet, dass ich möglicherweise eine Provision erhalte, wenn Sie etwas über diese Links kaufen.

Similar Posts

Leave a Reply

Your email address will not be published.