In der modernen Softwareentwicklung haben sich Microservices zu einer zentralen Architektur entwickelt, die Skalierbarkeit, Flexibilität und effizientes Management komplexer Systeme ermöglicht.

Microservices sind kleine, unabhängige Anwendungen, die bestimmte Aufgaben erfüllen und eine flexible Bereitstellung und Skalierung ermöglichen. Dieser modulare Ansatz für das Softwaredesign lockert die Kopplung zwischen den Komponenten und verbessert die Flexibilität und Verwaltbarkeit während der Entwicklung.

Der Artikel gibt einen Überblick über Microservices, ihre Funktionen und ihre Erstellung mit Python. Außerdem wird gezeigt, wie du deine Microservices mithilfe eines Dockerfiles auf Kinsta bereitstellst.

Was sind Microservices?

Microservices sind unabhängige, autonome Dienste innerhalb einer Anwendung, die jeweils spezifische Geschäftsanforderungen erfüllen. Sie kommunizieren über leichtgewichtige APIs oder Message-Broker und bilden so ein umfassendes System.

Im Gegensatz zu monolithischen Systemen, die sich ganz nach Bedarf skalieren lassen, ermöglichen Microservices die Skalierung einzelner Komponenten mit hohem Verkehrsaufkommen. Diese Architektur erleichtert das Fehlermanagement und die Aktualisierung von Funktionen und wirkt den monolithischen Einschränkungen entgegen.

Der Einsatz von Microservices bietet mehrere Vorteile, wie z. B.:

  • Flexibilität und Skalierbarkeit – Durch die Entkopplung einzelner Dienste kannst du die Anzahl der Knoten erhöhen, auf denen eine Instanz eines bestimmten Dienstes mit hohem Datenverkehr läuft.
  • Modularität des Codes – Jeder Dienst kann einen eigenen Technologie-Stack verwenden, sodass du die besten Entwicklungswerkzeuge für jeden Dienst auswählen kannst.

Microservice-Architekturen sind jedoch mit einigen Herausforderungen verbunden:

  • Überwachung mehrerer Dienste – Die Überwachung der einzelnen Dienste in einem System wird schwierig, wenn Instanzen eines bestimmten Dienstes über mehrere Knotenpunkte verteilt sind. Diese Schwierigkeit wird besonders bei Netzwerkausfällen oder anderen Systemproblemen deutlich.
  • Kosten – Die Entwicklung von Microservice-Anwendungen kann aufgrund der Kosten, die mit der Verwaltung mehrerer Dienste verbunden sind, deutlich teurer sein als der Aufbau monolithischer Systeme. Jeder Dienst benötigt eine eigene Infrastruktur und eigene Ressourcen, was teuer werden kann – vor allem, wenn das System vergrößert wird.

Wie man einen Microservice mit Python entwickelt

Jetzt, wo du die Vorteile einer Microservice-Architektur kennst, ist es an der Zeit, eine solche mit Python zu entwickeln.

In diesem Beispiel nimmst du an, dass du eine E-Commerce-Webanwendung erstellen willst. Die Website hat mehrere Komponenten, darunter den Produktkatalog, eine Liste der Bestellungen, ein Zahlungssystem und Protokolle, die du jeweils als unabhängigen Dienst implementieren musst. Außerdem musst du eine Kommunikationsmethode von Dienst zu Dienst einrichten, um Daten zwischen diesen Diensten effizient zu übertragen, wie z. B. HTTP.

Lass uns einen Microservice mit Python erstellen, um einen Produktkatalog zu verwalten. Der Microservice wird Produktdaten aus einer bestimmten Quelle abrufen und die Daten im JSON-Format zurückgeben.

Voraussetzungen

Um diesem Tutorial zu folgen, musst du Folgendes haben

1. Erstelle dein Projekt

  1. Um loszulegen, erstelle einen Ordner für dein Projekt mit dem Namen flask-microservice und das aktuelle Verzeichnis im Projektverzeichnis.
  2. Führe als Nächstes python3 --version aus, um sicherzustellen, dass Python korrekt auf deinem Computer installiert ist.
  3. Installiere virtualenv, um eine isolierte Entwicklungsumgebung für den Flask-Microservice zu schaffen, indem du den folgenden Befehl ausführst:
    pip3 install virtualenv
  4. Erstelle eine virtuelle Umgebung, indem du den folgenden Befehl ausführst:
    virtualenv venv
  5. Schließlich aktivierst du die virtuelle Umgebung mit einem der folgenden Befehle, je nach Betriebssystem deines Computers:
    # Windows: 
    .\venv\Scripts\activate
    # Unix or macOS:
    source venv/bin/activate

2. Einen Flask-Server einrichten

Erstelle im Stammverzeichnis eine Datei requirements.txt und füge diese Abhängigkeiten hinzu.

flask
requests

Führe die pip3 command on your terminal to install the dependencies.

pip install -r requirements.txt

Als nächstes erstellst du einen neuen Ordner im Stammverzeichnis und nennst ihn services. In diesem Ordner erstellst du eine neue Datei, products.py, und fügst den unten stehenden Code hinzu, um einen Flask-Server einzurichten.

import requests
import os
from flask import Flask, jsonify
app = Flask(__name__)
port = int(os.environ.get('PORT', 5000))

@app.route("/")
def home():
    return "Hello, this is a Flask Microservice"
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=port)

Mit dem obigen Code wird ein einfacher Flask-Server eingerichtet. Er initialisiert eine Flask-Anwendung, definiert eine einzelne Route für die Stamm-URL ("/") und zeigt beim Aufruf die Meldung "Hello, this is a Flask Microservice" an. Der Server läuft auf einem bestimmten Port, der über eine Umgebungsvariable ermittelt wird, oder standardmäßig auf dem Port 5000 und startet im Debugging-Modus, damit er eingehende Anfragen bearbeiten kann.

3. Definiere die API-Endpunkte

Nachdem der Server konfiguriert ist, erstellst du einen API-Endpunkt für einen Microservice, der Produktdaten von einer öffentlich verfügbaren API abruft. Füge diesen Code in die Datei products.py ein:

BASE_URL = "https://dummyjson.com"
@app.route('/products', methods=['GET'])
def get_products():
    response = requests.get(f"{BASE_URL}/products")
    if response.status_code != 200:
        return jsonify({'error': response.json()['message']}), response.status_code
    products = []
    for product in response.json()['products']:
        product_data = {
            'id': product['id'],
            'title': product['title'],
            'brand': product['brand'],
            'price': product['price'],
            'description': product['description']
        }
        products.append(product_data)
    return jsonify({'data': products}), 200 if products else 204

Der obige Code erstellt einen /products Endpunkt im Flask-Server. Wenn er über eine GET-Anfrage aufgerufen wird, holt er die Produktdaten von einer Dummy-API ab. Bei Erfolg verarbeitet er die abgerufenen Daten, extrahiert Produktdetails und gibt die Informationen im JSON-Format zurück. Bei Fehlern oder wenn keine Daten verfügbar sind, antwortet sie mit einer entsprechenden Fehlermeldung und einem Statuscode.

4. Teste den Microservice

Jetzt hast du erfolgreich einen einfachen Microservice eingerichtet. Um den Dienst zu starten, fahre den Entwicklungsserver hoch, der unter http://localhost:5000 gestartet wird.

flask --app services/products run

Dann kannst du mit dem Postman-Client eine GET -Anfrage an den Endpunkt /products stellen. Du solltest eine ähnliche Antwort wie auf dem Screenshot unten sehen.

Erfolgreiche GET-Anfrage an den Endpunkt des Dummy-API-Produkts in Postman
Testen der HTTP API GET Anfrage in Postman

Wie man Authentifizierung und Autorisierung in einem Python Microservice implementiert

Wenn du Microservices entwickelst, ist es wichtig, robuste Sicherheitsmaßnahmen wie Authentifizierung und Autorisierung zu implementieren. Die Absicherung deines Microservices stellt sicher, dass nur autorisierte Nutzer/innen auf den Dienst zugreifen und ihn nutzen können, um sensible Daten zu schützen und bösartige Angriffe zu verhindern.

Eine wirksame Methode zur Implementierung sicherer Authentifizierung und Autorisierung in Microservices sind JSON Web Tokens (JWTs).

JWT ist ein weit verbreiteter offener Standard, der eine sichere und effiziente Methode zur Übermittlung von Authentifizierungsinformationen zwischen Clients und Servern bietet. Es handelt sich dabei um kompakte, verschlüsselte und digital signierte Token, die du mit HTTP-Anfragen weitergibst. Wenn du jeder Anfrage ein JWT beifügst, kann der Server schnell die Identität und die Berechtigungen eines Nutzers überprüfen.

Um die JWT-Authentifizierung in einem Microservice zu implementieren, musst du Folgendes tun:

  1. Füge das Python-Paket pyjwt zu deiner requirements.txt-Datei hinzu und installiere die Abhängigkeiten mit pip install -r requirements.txt neu.
  2. Da der Dienst keine eigene Datenbank hat, erstelle eine users.json Datei im Hauptverzeichnis deines Projekts, um eine Liste der autorisierten Benutzer zu speichern. Füge den folgenden Code in die Datei ein:
    [
        {   
            "id": 1,
            "username": "admin",
            "password": "admin"
        
        }
    ]

  3. Ersetze dann in deiner services/products.py-Datei die import-Anweisungen durch die folgenden:
    import requests 
    from flask import Flask, jsonify, request, make_response
    import jwt
    from functools import wraps
    import json
    import os
    from jwt.exceptions import DecodeError

    Du importierst diese Module, um HTTP-Anfragen zu bearbeiten, eine Flask-Anwendung zu erstellen, JSON-Daten zu verwalten, eine JWT-basierte Authentifizierung zu implementieren und Ausnahmen zu behandeln, was eine Vielzahl von Funktionen innerhalb des Flask-Servers ermöglicht.

  4. Füge den folgenden Code nach der Erstellung der Flask-Anwendungs-Instanz ein, um einen geheimen Schlüssel zu erzeugen, der zum Signieren der JWT-Tokens verwendet wird.
    app.config['SECRET_KEY'] = os.urandom(24)
  5. Um JWTs zu verifizieren, erstellst du eine Decorator-Funktion und fügst den folgenden Code oberhalb der API-Routen in deinem Flask-Servercode ein. Diese Dekorfunktion authentifiziert und validiert die Nutzer, bevor sie auf geschützte Routen zugreifen.
    def token_required(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            token = request.cookies.get('token')
            if not token:
                return jsonify({'error': 'Authorization token is missing'}), 401
            try:
                data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
                current_user_id = data['user_id']
            except DecodeError:
                return jsonify({'error': 'Authorization token is invalid'}), 401
            return f(current_user_id, *args, **kwargs)
        return decorated

    Diese Decorator-Funktion prüft eingehende HTTP-Anfragen auf ein JWT-Autorisierungs-Token, das in den Anfrage-Headern oder Cookies enthalten sein sollte. Wenn das Token fehlt oder ungültig ist, sendet der Dekorator eine unauthorized status code Nachricht als Antwort.

    Ist hingegen ein gültiges Token vorhanden, extrahiert der Dekorator nach der Entschlüsselung die Benutzer-ID. Dieser Prozess schützt die geschützten API-Endpunkte, indem er nur autorisierten Nutzern den Zugang gewährt.

  6. Definiere einen API-Endpunkt für die Benutzerauthentifizierung mithilfe des folgenden Codes.
    with open('users.json', 'r') as f:
        users = json.load(f)
    @app.route('/auth', methods=['POST'])
    def authenticate_user():
        if request.headers['Content-Type'] != 'application/json':
            return jsonify({'error': 'Unsupported Media Type'}), 415
        username = request.json.get('username')
        password = request.json.get('password')
        for user in users:
            if user['username'] == username and user['password'] == password:
                token = jwt.encode({'user_id': user['id']}, app.config['SECRET_KEY'],algorithm="HS256")
                response = make_response(jsonify({'message': 'Authentication successful'}))
                response.set_cookie('token', token)
                return response, 200
        return jsonify({'error': 'Invalid username or password'}), 401

    Um Nutzer zu authentifizieren und zu autorisieren, überprüft der /auth API-Endpunkt die Anmeldeinformationen im JSON-Payload der POST Anfrage mit der Liste der zugelassenen Nutzer. Wenn die Anmeldedaten gültig sind, wird ein JWT-Token mit der ID des Nutzers und dem geheimen Schlüssel der Anwendung erstellt und als Cookie in der Antwort gespeichert. Mit diesem Token können die Nutzer nun weitere API-Anfragen stellen.

    Nachdem du den Endpunkt /auth erstellt hast, schickst du mit Postman eine HTTP-Anfrage POST an http://localhost:5000/auth. In den Request Body fügst du die Anmeldedaten des von dir erstellten Pseudo-Admin-Benutzers ein.

    Die Postman-Anfrage zeigt den Request Body
    Die Postman-Anfrage zeigt den Request Body

    Wenn die Anfrage erfolgreich ist, generiert die API ein JWT-Token, legt es in den Cookies von Postman ab und sendet eine authentifizierte Erfolgsmeldung.

  7. Aktualisiere abschließend den GET API-Endpunkt, um das JWT-Token mit dem folgenden Code zu prüfen und zu verifizieren:
    @app.route('/products', methods=['GET'])
    @token_required
    def get_products(current_user_id):
        headers = {'Authorization': f'Bearer {request.cookies.get("token")}'}    
        response = requests.get(f"{BASE_URL}/products", headers=headers)
        if response.status_code != 200:
            return jsonify({'error': response.json()['message']}), response.status_code
        products = []
        for product in response.json()['products']:
            product_data = {
                'id': product['id'],
                'title': product['title'],
                'brand': product['brand'],
                'price': product['price'],
                'description': product['description']
            }
            products.append(product_data)
        return jsonify({'data': products}), 200 if products else 204

Wie man Python Microservices mit Docker containerisiert

Docker ist eine Plattform, die Anwendungen und ihre Abhängigkeiten in einer isolierten Entwicklungsumgebung verpackt. Das Verpacken von Microservices in Containern vereinfacht ihre Bereitstellung und Verwaltung auf Servern, da jeder Dienst unabhängig in seinem Container läuft und ausgeführt wird.

Um einen Microservice in einen Container zu packen, musst du ein Docker-Image aus einer Dockerdatei erstellen, in der die Abhängigkeiten angegeben sind, die für die Ausführung der Anwendung in einem Container erforderlich sind. Erstelle ein Dockerfile im Stammverzeichnis deines Projekts und füge diese Anweisungen hinzu:

FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "./services/products.py"]

Bevor du das Image erstellst, solltest du dir diese Befehle ansehen:

  • FROM – Weist Docker an, welches Basis-Image verwendet werden soll. Ein Basis-Image ist eine vorgefertigte Instanz, die die Software und Abhängigkeiten enthält, um die Flask-Anwendung in einem Container auszuführen.
  • WORKDIR – Legt das angegebene Verzeichnis innerhalb des Containers als Arbeitsverzeichnis fest.
  • COPY requirements.txt ./ – Kopiert die Abhängigkeiten aus der Datei requirements.txt in die Datei requirements.txt des Containers.
  • RUN – Führt den angegebenen Befehl aus, um die für das Image erforderlichen Abhängigkeiten zu installieren.
  • COPY . . – Kopiert alle Dateien aus dem Stammverzeichnis des Projekts in das Arbeitsverzeichnis des Containers.
  • EXPOSE – Legt den Port fest, an dem der Container auf Anfragen lauschen soll. Docker veröffentlicht den Port jedoch nicht auf dem Host-Rechner.
  • CMD – Legt den Standardbefehl fest, der beim Starten des Containers ausgeführt werden soll.

Als Nächstes fügst du eine .dockerignore-Datei in das Stammverzeichnis deines Projekts ein, um die Dateien anzugeben, die das Docker-Image ausschließen soll. Wenn du den Inhalt des Images einschränkst, verringert sich seine endgültige Größe und die damit verbundene Erstellungszeit.

/venv
/services/__pycache__/
.gitignore

Führe nun den folgenden Befehl aus, um das Docker-Image zu erstellen:

docker build -t flask-microservice .

Sobald das Image erstellt ist, kannst du den Microservice mit dem folgenden Befehl in einem Docker-Container ausführen:

docker run -p 5000:5000 flask-microservice

Mit diesem Befehl wird ein Docker-Container gestartet, in dem der Microservice ausgeführt wird, und der Port 5000 des Containers wird für den Port 5000 des Host-Rechners freigegeben, so dass du HTTP-Anfragen von deinem Webbrowser oder Postman über die URL http://localhost:5000 stellen kannst.

Python Microservices mit Kinsta bereitstellen

Kinsta bietet Managed-Hosting-Lösungen für Webanwendungen und Datenbanken – du kannst deine Python-Microservices und Backend-APIs nahtlos in einer Produktionsumgebung bereitstellen und verwalten.

Befolge diese Schritte, um deinen Flask-Microservice für die Bereitstellung mit MyKinsta zu konfigurieren:

  1. Erstelle zunächst ein neues Procfile im Stammverzeichnis und füge den unten stehenden Code ein. Er gibt den Befehl an, um den Flask-Microservice auf dem Gunicorn WSGI HTTP Server für Python-Anwendungen von Kinsta auszuführen.
    web: gunicorn services.wsgi
  2. Füge in deiner Datei requirements.txt die Gunicorn-Abhängigkeit hinzu:
    gunicorn==20.1.*
  3. Als nächstes erstellst du eine neue Datei services/wsgi.py und fügst den folgenden Code hinzu.
    from services.products import app as application
    if __name__ == "__main__":
                    application.run()
  4. Erstelle eine .gitignore-Datei im Stammverzeichnis des Projekts und füge Folgendes hinzu:
    services/__pycache__
    venv
  5. Zum Schluss erstellst du ein neues Repository auf GitHub und veröffentlichst deine Projektdateien.

Sobald dein Repository fertig ist, befolge diese Schritte, um den Flask-Microservice auf Kinsta bereitzustellen:

  1. Logge dich ein oder erstelle ein Konto, um dein MyKinsta-Dashboard zu sehen.
  2. Autorisiere Kinsta mit deinem Git-Anbieter (Bitbucket, GitHub oder GitLab).
  3. Klicke in der linken Seitenleiste auf Anwendungen und dann auf Anwendung hinzufügen.
  4. Klicke auf dem Dashboard auf Dienst hinzufügen und wähle Anwendung.
  5. Wähle das Repository und den Zweig aus, von dem aus du die Anwendung bereitstellen möchtest.
  6. Gib deiner Anwendung einen eindeutigen Namen und wähle einen Standort im Rechenzentrum.
  7. Um die Build-Umgebung zu konfigurieren, wähle die Option zur Verwendung einer Dockerdatei um das Container-Image zu erstellen.
  8. Gib den Pfad zu deiner Dockerdatei und den Kontext an.
  9. Überprüfe die anderen Informationen und klicke auf Anwendung erstellen.

Teste den Microservice

Nach erfolgreicher Bereitstellung klickst du auf die angegebene URL, um den Microservice zu testen, indem du HTTP-Anfragen in Postman stellst. Stelle eine GET Anfrage an den Root-Endpunkt.

Erfolgreiche GET-Anfrage an die bereitgestellte Microservice-URL für die Home-Route
HTTP API GET Anfrage an den product Endpunkt des Microservices

Um dich zu authentifizieren und ein JWT-Token zu generieren, sende eine POST Anfrage an den /auth API-Endpunkt und übergebe die Admin-Anmeldedaten im Body der Anfrage.

Eine POST-Anfrage an einen Microservice Auth Endpoint zur Authentifizierung in Postman
HTTP-API-Anfrage POST an den Endpunkt des Microservices auth

Nachdem du dich erfolgreich authentifiziert hast, stellst du eine GET Anfrage an den /products Endpunkt, um Daten abzurufen.

Erfolgreiche HTTP-API-GET-Anfrage an den Produkt-Endpunkt in Postman
HTTP API GET Anfrage an einen Microservice products Endpunkt

Zusammenfassung

Da Anwendungen immer größer und komplexer werden, ist es wichtig, Architekturmuster zu verwenden, die es ermöglichen, Softwaresysteme zu skalieren, ohne die verfügbaren Ressourcen zu überlasten.

Die Microservice-Architektur bietet Skalierbarkeit, Entwicklungsflexibilität und Wartungsfreundlichkeit und erleichtert dir die Verwaltung komplexer Anwendungen.

Kinsta vereinfacht den Prozess des Hostings deiner Microservices. Du kannst mühelos deine bevorzugte Datenbank verwenden und sowohl deine Anwendung als auch deine Datenbank bequem über ein einheitliches Dashboard hosten.

Jeremy Holcombe Kinsta

Content & Marketing Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems ;).