Dieses Tutorial ist eines von vier, die Konzepte aus „Microservices March 2022“ in die Praxis umsetzen: Kubernetes-Netzwerk :
Möchten Sie detaillierte Anleitungen zur Verwendung von NGINX für noch mehr Anwendungsfälle von Kubernetes-Netzwerken? Laden Sie unser kostenloses E-Book „Verwaltung des Kubernetes-Verkehrs mit NGINX“ herunter: Ein praktischer Leitfaden .
Ihre Organisation hat gerade ihre erste App und API in Kubernetes gestartet. Man hat Ihnen mitgeteilt, dass mit einem hohen Datenverkehr zu rechnen ist (und hat bereits eine automatische Skalierung implementiert, um sicherzustellen, dass der NGINX Ingress Controller den Datenverkehr schnell weiterleiten kann), es gibt jedoch Bedenken, dass die API das Ziel eines böswilligen Angriffs sein könnte. Wenn die API eine große Menge an HTTP-Anfragen empfängt – was bei Brute-Force-Angriffen zum Erraten von Passwörtern oder bei DDoS-Angriffen möglich ist –, sind sowohl die API als auch die App möglicherweise überlastet oder stürzen sogar ab.
Aber Sie haben Glück! Bei der als Rate Limiting bezeichneten Verkehrssteuerungstechnik handelt es sich um einen API-Gateway-Anwendungsfall, der die Rate eingehender Anfragen auf einen für echte Benutzer typischen Wert begrenzt. Sie konfigurieren den NGINX Ingress Controller, um eine Ratenbegrenzungsrichtlinie zu implementieren, die verhindert, dass die App und die API durch zu viele Anfragen überlastet werden. Gute Arbeit!
Dieser Blog begleitet das Labor für Einheit 2 von Microservices März 2022 – Verfügbarmachen von APIs in Kubernetes und zeigt, wie mehrere NGINX-Ingress-Controller mit Ratenbegrenzung kombiniert werden, um eine Überlastung von Apps und APIs zu verhindern.
Zum Ausführen des Lernprogramms benötigen Sie einen Computer mit:
Um den größtmöglichen Nutzen aus dem Labor und dem Lernprogramm zu ziehen, empfehlen wir Ihnen, vor dem Beginn Folgendes zu tun:
Sehen Sie sich die Aufzeichnung des live gestreamten konzeptionellen Überblicks an
Lesen Sie die Hintergrundblogs, Webinare und Videos
Sehen Sie sich die 18-minütige Videozusammenfassung des Labors an:
In diesem Tutorial werden die folgenden Technologien verwendet:
Die Anweisungen für jede Herausforderung enthalten den vollständigen Text der YAML-Dateien, die zum Konfigurieren der Apps verwendet werden. Sie können den Text auch aus unserem GitHub-Repository kopieren. Zusammen mit dem Text jeder YAML-Datei wird ein Link zu GitHub bereitgestellt.
Dieses Tutorial umfasst drei Herausforderungen:
In dieser Challenge stellen Sie einen Minikube-Cluster bereit und installieren Podinfo als Beispiel-App und API. Anschließend stellen Sie den NGINX Ingress Controller bereit , konfigurieren die Verkehrsführung und testen die Ingress-Konfiguration .
Erstellen Sie einen Minikube- Cluster. Nach einigen Sekunden bestätigt eine Meldung, dass die Bereitstellung erfolgreich war.
$ minikube start 🏄 Fertig! kubectl ist jetzt so konfiguriert, dass es standardmäßig den Cluster „minikube“ und den Namespace „default“ verwendet
Podinfo ist eine „mit Go erstellte Webanwendung, die Best Practices zum Ausführen von Microservices in Kubernetes demonstriert“. Aufgrund seines geringen Platzbedarfs verwenden wir es als Beispiel-App und API.
Erstellen Sie mit einem Texteditor Ihrer Wahl eine YAML-Datei namens 1-apps.yaml mit dem folgenden Inhalt (oder kopieren Sie sie von GitHub ). Es definiert eine Bereitstellung, die Folgendes umfasst:
API-Version: Apps/v1
Art: Bereitstellung
Metadaten:
Name: API
Spezifikation:
Selektor:
MatchLabels:
App: API
Vorlage:
Metadaten:
Labels:
App: API
Spezifikation:
Container:
-Name: API
Bild: stefanprodan/podinfo
Ports:
-ContainerPort: 9898
---
API-Version: v1
Art: Dienst
Metadaten:
Name: API
Spezifikation:
Ports:
-Port: 80
ZielPort: 9898
KnotenPort: 30001
Selektor:
App: API
Typ: LoadBalancer
---
API-Version: Apps/v1
Art: Bereitstellung
Metadaten:
Name: Frontend
Spezifikation:
Selektor:
MatchLabels:
App: Frontend
Vorlage:
Metadaten:
Labels:
App: Frontend
Spezifikation:
Container:
- Name: Frontend
Bild: stefanprodan/podinfo
Ports:
- ContainerPort: 9898
---
API-Version: v1
Art: Dienst
Metadaten:
Name: Frontend
Spezifikation:
Ports:
-Port: 80
ZielPort: 9898
KnotenPort: 30002
Selektor:
App: Frontend
Typ: Lastenausgleich
Stellen Sie die App und die API bereit:
$ kubectl apply -f 1-apps.yaml deployment.apps/api erstellt service/api erstellt deployment.apps/frontend erstellt service/frontend erstellt
Bestätigen Sie, dass die Pods für die Podinfo-API und das Podinfo-Frontend erfolgreich bereitgestellt wurden, wie durch den Wert „Wird ausgeführt“
in der Spalte „STATUS“
angezeigt.
$ kubectl get pods NAME BEREIT STATUS NEUSTART ALTER api-7574cf7568-c6tr6 1/1 Läuft 0 87s frontend-6688d86fc6-78qn7 1/1 Läuft 0 87s
Am schnellsten lässt sich NGINX Ingress Controller mit Helm installieren.
Installieren Sie NGINX Ingress Controller mit Helm in einem separaten Namespace ( nginx ).
Erstellen Sie den Namespace:
$ kubectl erstelle Namespace nginx
Fügen Sie das NGINX-Repository zu Helm hinzu:
$ helm repo add nginx-stable https://helm.nginx.com/stable
Laden Sie NGINX Ingress Controller herunter und installieren Sie es in Ihrem Cluster:
$ helm install main nginx-stable/nginx-ingress \ --set controller.watchIngressWithoutClass=true \ --set controller.ingressClass=nginx \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30010 \ --set controller.enablePreviewPolicies=true \ --namespace nginx
Bestätigen Sie, dass der NGINX Ingress Controller-Pod bereitgestellt wurde. Dies wird durch den Wert „Running“
in der Spalte „STATUS“
angezeigt (der Lesbarkeit halber ist die Ausgabe auf zwei Zeilen verteilt).
$ kubectl get pods -namespace nginx NAME BEREIT STATUS ... main-nginx-ingress-779b74bb8b-d4qtc 1/1 Wird ausgeführt ... ... STARTET DAS ALTER NEU ... 0,92 Sek.
API-Version: networking.k8s.io/v1
Art: Ingress
Metadaten:
Name: Vorname
Spezifikation:
IngressClassName: nginx
Regeln:
- Host: „example.com“
http:
Pfade:
- Backend:
Dienst:
Name: Frontend
Port:
Nummer: 80
Pfad: /
Pfadtyp: Präfix
- Host: "api.example.com"
http:
Pfade:
- Backend:
Dienst:
Name: API
Port:
Nummer: 80
Pfad: /
Pfadtyp: Präfix
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/zuerst erstellt
Um sicherzustellen, dass Ihre Ingress-Konfiguration wie erwartet funktioniert, testen Sie sie mit einem temporären Pod. Starten Sie einen verfügbaren BusyBox- Pod im Cluster:
$ kubectl run -ti --rm=true busybox --image=busybox Wenn keine Eingabeaufforderung angezeigt wird, drücken Sie die Eingabetaste. / #
Testen Sie die Podinfo-API , indem Sie eine Anfrage an den NGINX Ingress Controller-Pod mit dem Hostnamen api.example.com senden. Die angezeigte Ausgabe zeigt an, dass die API Datenverkehr empfängt.
/ # wget --header="Host: api.example.com" -qO- main-nginx-ingress.nginx { "Hostname": "api-687fd448f8-t7hqk", "Version": "6.0.3", "Revision": "", "Farbe": "#34577c", "Logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", "Nachricht": "Grüße von Podinfo v6.0.3", "goos": "Linux", "goarch": "arm64", "Runtime": "go1.16.9", "num_goroutine": "6", "num_cpu": "4" }
/ # wget --header="Host: beispiel.com" --header="User-Agent: Mozilla" -qO- main-nginx-ingress.nginx <!DOCTYPE html>
<html>
<head>
<title>frontend-596d5c9ff4-xkbdc</title>
# ...
Öffnen Sie Podinfo in einem anderen Terminal in einem Browser. Die Begrüßungsseite von Podinfo zeigt an, dass Podinfo ausgeführt wird.
$ Minikube-Dienst Podinfo
Glückwunsch! NGINX Ingress Controller empfängt Anfragen und leitet sie an die App und API weiter.
Beenden Sie die BusyBox-Sitzung im ursprünglichen Terminal:
/ # beenden $
Bei dieser Herausforderung installieren Sie Locust , ein Open-Source-Tool zur Lastgenerierung, und simulieren damit einen Verkehrsanstieg , der die API überlastet und zum Absturz der App führt.
Erstellen Sie mit einem Texteditor Ihrer Wahl eine YAML-Datei namens 3-locust.yaml mit dem folgenden Inhalt (oder kopieren Sie sie von GitHub ).
Das ConfigMap-
Objekt definiert ein Skript namens locustfile.py , das Anfragen generiert, die mit den richtigen Headern an den Pod gesendet werden. Der Datenverkehr ist nicht gleichmäßig zwischen der App und der API verteilt – die Anfragen werden verzerrt an die Podinfo-API weitergeleitet, wobei nur 1 von 5 Anfragen an das Podinfo-Frontend geht.
Die Bereitstellungs-
und Serviceobjekte
definieren den Locust-Pod.
API-Version: v1
Art: ConfigMap
metadata:
name: locust-script
data:
locustfile.py: |-
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
with self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) als Antwort:
if response.request_meta["response_time"] > 200:
response.failure("Frontend fehlgeschlagen")
sonst:
response.success()
@task(5)
def visit_api(self):
with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) als Antwort:
if response.request_meta["response_time"] > 200:
response.failure("API fehlgeschlagen")
sonst:
response.success()
---
apiVersion: apps/v1
kind: Bereitstellung
Metadaten:
Name: locust
Spezifikation:
Selektor:
MatchLabels:
App: locust
Vorlage:
Metadaten:
Labels:
App: locust
Spezifikation:
Container:
- Name: locust
Bild: locustio/locust
Ports:
- ContainerPort: 8089
volumeMounts:
- mountPath: /home/locust
Name: locust-script
volumes:
- Name: locust-script
configMap:
Name: locust-script
---
apiVersion: v1
kind: Dienst
Metadaten:
Name: locust
Spezifikation:
Ports:
-Port: 8089
ZielPort: 8089
KnotenPort: 30015
Selektor:
App: Heuschrecke
Typ: Lastenausgleich
Locust einsetzen:
$ kubectl apply -f 3-locust.yaml configmap/locust-script erstellt deployment.apps/locust erstellt service/locust erstellt
„kubectl
apply
“ ausgeführt und die Installation ist daher noch im Gange, wie durch den Wert „ContainerCreating“
für den Locust-Pod im Feld „STATUS“
angezeigt. Warten Sie, bis der Wert „Running“
lautet, bevor Sie mit dem nächsten Abschnitt fortfahren. (Die Ausgabe ist der Lesbarkeit halber auf zwei Zeilen verteilt.)$ kubectl get pods NAME BEREIT STATUS ... api-7574cf7568-c6tr6 1/1 Wird ausgeführt ... frontend-6688d86fc6-78qn7 1/1 Wird ausgeführt ... locust-77c699c94d-hc76t 0/1 Container wird erstellt ... ... STARTET DAS ALTER NEU ... 0,33 m ... 0,33 m ... 0 4 Sekunden
Öffnen Sie Locust in einem Browser.
$ Minikube-Dienst Heuschrecke
Geben Sie in die Felder folgende Werte ein:
Klicken Sie auf die Schaltfläche „Swarming starten“, um Datenverkehr an die Podinfo-API und das Podinfo-Frontend zu senden. Beobachten Sie die Verkehrsmuster auf den Registerkarten „Locust -Diagramme“ und „Fehler“ :
Dies ist problematisch, da ein einzelner böswilliger Akteur, der die API verwendet, nicht nur die API, sondern alle vom NGINX Ingress Controller bereitgestellten Apps lahmlegen kann!
In der letzten Herausforderung stellen Sie zwei NGINX Ingress Controller bereit, um die Einschränkungen der vorherigen Bereitstellung zu beseitigen, indem Sie für jeden einen separaten Namespace erstellen , separate NGINX Ingress Controller-Instanzen für Podinfo Frontend und Podinfo API installieren, Locust neu konfigurieren, um den Datenverkehr für die App und API an ihre jeweiligen NGINX Ingress Controller weiterzuleiten , und überprüfen, ob die Ratenbegrenzung wirksam ist . Sehen wir uns zunächst an, wie das Architekturproblem angegangen werden kann. In der vorherigen Herausforderung haben Sie den NGINX Ingress Controller mit API-Anfragen überlastet, was sich auch auf die App auswirkte. Dies geschah, weil ein einzelner Ingress Controller für die Weiterleitung des Datenverkehrs sowohl an die Web-App ( Podinfo Frontend ) als auch an die API ( Podinfo API ) verantwortlich war.
Durch Ausführen eines separaten NGINX Ingress Controller-Pods für jeden Ihrer Dienste wird verhindert, dass Ihre App durch zu viele API-Anfragen beeinträchtigt wird. Dies ist nicht unbedingt für jeden Anwendungsfall erforderlich, aber in unserer Simulation lassen sich die Vorteile des Ausführens mehrerer NGINX Ingress Controller leicht erkennen.
Der zweite Teil der Lösung, der eine Überlastung der Podinfo-API verhindert, besteht in der Implementierung einer Ratenbegrenzung durch die Verwendung des NGINX Ingress Controller als API-Gateway.
Durch die Ratenbegrenzung wird die Anzahl der Anfragen begrenzt, die ein Benutzer in einem bestimmten Zeitraum stellen kann. Um beispielsweise einen DDoS-Angriff abzuschwächen, können Sie mithilfe der Ratenbegrenzung die Rate eingehender Anfragen auf einen für echte Benutzer typischen Wert begrenzen. Wenn die Ratenbegrenzung mit NGINX implementiert wird, werden Clients, die zu viele Anfragen senden, auf eine Fehlerseite umgeleitet, damit sie die API nicht negativ beeinflussen können. Wie das funktioniert, erfahren Sie in der NGINX Ingress Controller-Dokumentation .
Ein API-Gateway leitet API-Anfragen von Clients an die entsprechenden Dienste weiter. Eine große Fehlinterpretation dieser einfachen Definition besteht darin, dass ein API-Gateway eine einzigartige Technologie sei. Das ist es nicht. Vielmehr beschreibt „API-Gateway“ eine Reihe von Anwendungsfällen, die über verschiedene Arten von Proxys implementiert werden können – am häufigsten ein ADC oder Load Balancer und Reverse Proxy, zunehmend aber auch ein Ingress-Controller oder Service Mesh. Die Ratenbegrenzung ist ein häufiger Anwendungsfall für die Bereitstellung eines API-Gateways. Erfahren Sie mehr über Anwendungsfälle von API-Gateways in Kubernetes unter „Wie treffe ich eine Auswahl?“ API-Gateway vs. Ingress-Controller vs. Service Mesh auf unserem Blog.
Bevor Sie die neue Architektur und Ratenbegrenzung implementieren können, müssen Sie die vorherige NGINX Ingress Controller-Konfiguration löschen.
$ kubectl delete -f 2-ingress.yaml ingress.networking.k8s.io "first" gelöscht
Erstellen Sie einen Namespace namens nginx‑web für das Podinfo-Frontend :
$ kubectl create namespace nginx-web namespace/nginx-web erstellt
Erstellen Sie einen Namespace namens nginx‑api für die Podinfo API :
$ kubectl create namespace nginx-api namespace/nginx-api erstellt
Installieren Sie den NGINX Ingress Controller:
$ helm installiere web nginx-stable/nginx-ingress --set controller.ingressClass=nginx-web \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30020 \ --namespace nginx-web
Erstellen Sie ein Ingress-Manifest namens 4-ingress-web.yaml für das Podinfo-Frontend (oder kopieren Sie es von GitHub ).
API-Version: k8s.nginx.org/v1 Art: Metadaten der Richtlinie: Name: Rate-Limit-Richtlinie, Spezifikation: RateLimit: Rate: 10r/s-Taste:${binary_remote_addr} Zonengröße: 10M --- API-Version: k8s.nginx.org/v1 Art: VirtualServer-Metadaten: Name: API-VS-Spezifikation: IngressClassName: Nginx-API-Host: API.example.com-Richtlinien: - Name: Rate-Limit-Richtlinie Upstreams: - Name: API-Dienst: API-Port: 80 Routen: - Pfad: / Aktion: Pass: API
Stellen Sie das neue Manifest bereit:
$ kubectl apply -f 4-ingress-web.yaml ingress.networking.k8s.io/frontend erstellt
Konfigurieren Sie nun Locust neu und überprüfen Sie Folgendes:
Führen Sie diese Schritte aus:
Ändern Sie das Locust-Skript so, dass:
Da Locust nur eine einzige URL im Dashboard unterstützt, codieren Sie den Wert im Python-Skript fest mithilfe der YAML-Datei 6-locust.yaml mit dem folgenden Inhalt (oder kopieren Sie ihn von GitHub ). Notieren Sie sich die URLs in jeder Aufgabe
.
API-Version: v1
Art: ConfigMap
metadata:
name: locust-script
data:
locustfile.py: |-
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
with self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) als Antwort:
if response.request_meta["response_time"] > 200:
response.failure("Frontend fehlgeschlagen")
sonst:
response.success()
@task(5)
def visit_api(self):
with self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) als Antwort:
if response.request_meta["response_time"] > 200:
response.failure("API fehlgeschlagen")
sonst:
response.success()
---
apiVersion: apps/v1
kind: Bereitstellung
Metadaten:
Name: locust
Spezifikation:
Selektor:
MatchLabels:
App: locust
Vorlage:
Metadaten:
Labels:
App: locust
Spezifikation:
Container:
- Name: locust
Bild: locustio/locust
Ports:
- ContainerPort: 8089
volumeMounts:
- mountPath: /home/locust
Name: locust-script
volumes:
- Name: locust-script
configMap:
Name: locust-script
---
apiVersion: v1
kind: Dienst
Metadaten:
Name: locust
Spezifikation:
Ports:
-Port: 8089
ZielPort: 8089
KnotenPort: 30015
Selektor:
App: Heuschrecke
Typ: Lastenausgleich
Stellen Sie die neue Locust-Konfiguration bereit. Die Ausgabe bestätigt, dass das Skript geändert wurde, die anderen Elemente jedoch unverändert bleiben.
$ kubectl apply -f 6-locust.yaml configmap/locust-script konfiguriert deployment.apps/locust unverändert service/locust unverändert
Löschen Sie den Locust-Pod, um ein Neuladen der neuen ConfigMap zu erzwingen. Um den zu entfernenden Pod zu identifizieren, wird das Argument des Befehls „kubectl
delete
pod
“ als Pipe-Befehle ausgedrückt, die den Locust-Pod aus der Liste aller Pods auswählen.
$ kubectl lösche Pod `kubectl hole Pods | grep locust | awk {'print $1'}`
Überprüfen Sie, ob Locust neu geladen wurde (der Wert für den Locust-Pod in der Spalte „AGE“
beträgt nur wenige Sekunden).
$ kubectl get pods NAME BEREIT STATUS ... api-7574cf7568-jrlvd 1/1 Wird ausgeführt ... frontend-6688d86fc6-vd856 1/1 Wird ausgeführt ... locust-77c699c94d-6chsg 0/1 Wird ausgeführt ... ... STARTET DAS ALTER NEU ... 0,9 Min. 57 Min. ... 0,9 Min. 57 Min. ... 0 6 Sekunden
Kehren Sie zu Locust zurück und ändern Sie die Parameter in diesen Feldern:
Klicken Sie auf die Schaltfläche „Swarming starten“, um Datenverkehr an die Podinfo-API und das Podinfo-Frontend zu senden.
Beobachten Sie, wie in der Titelleiste von Locust oben links mit der steigenden Anzahl von Benutzern in der Spalte „STATUS“ auch der Wert in der Spalte „FEHLER“ steigt. Allerdings stammen die Fehler nicht mehr vom Podinfo-Frontend, sondern von der Podinfo-API, da aufgrund der für die API festgelegten Ratenbegrenzung zu viele Anfragen abgelehnt werden. In der Ablaufverfolgung unten rechts können Sie sehen, dass NGINX die Nachricht zurückgibt503
Dienst
vorübergehend
nicht verfügbar
, was Teil der Ratenbegrenzungsfunktion ist und angepasst werden kann. Die API ist geschwindigkeitsbegrenzt und die Webanwendung ist immer verfügbar. Gut gemacht!
In der Praxis reicht eine Ratenbegrenzung allein nicht aus, um Ihre Apps und APIs vor böswilligen Akteuren zu schützen. Sie müssen mindestens eine oder zwei der folgenden Methoden zum Schutz von Kubernetes-Apps, -APIs und -Infrastruktur implementieren:
Diese und weitere Themen behandeln wir in Einheit 3 von Microservices März 2022 – Microservices-Sicherheitsmuster in Kubernetes . Um NGINX Ingress Controller für Kubernetes mit NGINX Plus und NGINX App Protect auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen . Um NGINX Ingress Controller mit NGINX Open Source auszuprobieren, können Sie den Quellcode der Version abrufen oder einen vorgefertigten Container von DockerHub herunterladen.
„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."