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 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
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:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: stefanprodan/podinfo
ports:
- containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
ports:
- port: 80
targetPort: 9898
nodePort: 30001
selector:
app: api
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: stefanprodan/podinfo
ports:
- containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
ports:
- port: 80
targetPort: 9898
nodePort: 30002
selector:
app: frontend
type: LoadBalancer
Stellen Sie die App und die API bereit:
$ kubectl apply -f 1-apps.yamldeployment.apps/api created
service/api created
deployment.apps/frontend created
service/frontend created
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 READY STATUS RESTARTS AGE
api-7574cf7568-c6tr6 1/1 Running 0 87s
frontend-6688d86fc6-78qn7 1/1 Running 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 create 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 READY STATUS ...
main-nginx-ingress-779b74bb8b-d4qtc 1/1 Running ...
... RESTARTS AGE
... 0 92s
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: first
spec:
ingressClassName: nginx
rules:
- host: "example.com"
http:
paths:
- backend:
service:
name: frontend
port:
number: 80
path: /
pathType: Prefix
- host: "api.example.com"
http:
paths:
- backend:
service:
name: api
port:
number: 80
path: /
pathType: Prefix
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/first created
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 If you don't see a command prompt, try pressing enter.
/ #
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": "",
"color": "#34577c",
"logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
"message": "greetings from podinfo v6.0.3",
"goos": "linux",
"goarch": "arm64",
"runtime": "go1.16.9",
"num_goroutine": "6",
"num_cpu": "4"
}
/ # wget --header="Host: example.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 service 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:
/ # exit
$
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.
apiVersion: v1
kind: 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) as response:
if response.request_meta["response_time"] > 200:
response.failure("Frontend failed")
else:
response.success()
@task(5)
def visit_api(self):
with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) as response:
if response.request_meta["response_time"] > 200:
response.failure("API failed")
else:
response.success()
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
spec:
selector:
matchLabels:
app: locust
template:
metadata:
labels:
app: locust
spec:
containers:
- name: locust
image: locustio/locust
ports:
- containerPort: 8089
volumeMounts:
- mountPath: /home/locust
name: locust-script
volumes:
- name: locust-script
configMap:
name: locust-script
---
apiVersion: v1
kind: Service
metadata:
name: locust
spec:
ports:
- port: 8089
targetPort: 8089
nodePort: 30015
selector:
app: locust
type: LoadBalancer
Locust einsetzen:
$ kubectl apply -f 3-locust.yaml configmap/locust-script created
deployment.apps/locust created
service/locust created
„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 READY STATUS ... api-7574cf7568-c6tr6 1/1 Running ...
frontend-6688d86fc6-78qn7 1/1 Running ... locust-77c699c94d-hc76t 0/1 ContainerCreating ...
... RESTARTS AGE
... 0 33m
... 0 33m
... 0 4s
Öffnen Sie Locust in einem Browser.
$ minikube service locust
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" deleted
Erstellen Sie einen Namespace namens nginx‑web für das Podinfo-Frontend :
$ kubectl create namespace nginx-web
namespace/nginx-web created
Erstellen Sie einen Namespace namens nginx‑api für die Podinfo API :
$ kubectl create namespace nginx-api
namespace/nginx-api created
Installieren Sie den NGINX Ingress Controller:
$ helm install 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 created
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
.
apiVersion: v1
kind: 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) as response:
if response.request_meta["response_time"] > 200:
response.failure("Frontend failed")
else:
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) as response:
if response.request_meta["response_time"] > 200:
response.failure("API failed")
else:
response.success()
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
spec:
selector:
matchLabels:
app: locust
template:
metadata:
labels:
app: locust
spec:
containers:
- name: locust
image: locustio/locust
ports:
- containerPort: 8089
volumeMounts:
- mountPath: /home/locust
name: locust-script
volumes:
- name: locust-script
configMap:
name: locust-script
---
apiVersion: v1
kind: Service
metadata:
name: locust
spec:
ports:
- port: 8089
targetPort: 8089
nodePort: 30015
selector:
app: locust
type: LoadBalancer
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 configured
deployment.apps/locust unchanged
service/locust unchanged
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 delete pod `kubectl get 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 READY STATUS ... api-7574cf7568-jrlvd 1/1 Running ...
frontend-6688d86fc6-vd856 1/1 Running ... locust-77c699c94d-6chsg 0/1 Running ...
... RESTARTS AGE
... 0 9m57s
... 0 9m57s
... 0 6s
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."