Ce tutoriel est l'un des quatre qui mettent en pratique les concepts de Microservices de mars 2022 : Réseau Kubernetes :
Vous souhaitez des conseils détaillés sur l’utilisation de NGINX pour encore plus de cas d’utilisation de réseau Kubernetes ? Téléchargez notre eBook gratuit, Gérer le trafic Kubernetes avec NGINX : Un guide pratique .
Votre organisation vient de lancer sa première application et API dans Kubernetes. On vous a dit de vous attendre à des volumes de trafic élevés (et vous avez déjà implémenté la mise à l’échelle automatique pour garantir que NGINX Ingress Controller puisse acheminer rapidement le trafic), mais il existe des inquiétudes quant au fait que l’API puisse être ciblée par une attaque malveillante. Si l'API reçoit un volume élevé de requêtes HTTP (ce qui peut arriver en cas de devinette de mot de passe par force brute ou d'attaques DDoS), l'API et l'application risquent d'être surchargées, voire de planter.
Mais tu as de la chance ! La technique de contrôle du trafic appelée limitation de débit est un cas d’utilisation de passerelle API qui limite le débit des requêtes entrantes à une valeur typique pour les utilisateurs réels. Vous configurez NGINX Ingress Controller pour implémenter une politique de limitation de débit, qui empêche l’application et l’API d’être submergées par trop de requêtes. Bon travail !
Ce blog accompagne le laboratoire de l'unité 2 de Microservices de mars 2022 - Exposition des API dans Kubernetes , démontrant comment combiner plusieurs contrôleurs d'entrée NGINX avec limitation de débit pour éviter que les applications et les API ne soient surchargées.
Pour exécuter le tutoriel, vous avez besoin d'une machine avec :
Pour tirer le meilleur parti du laboratoire et du tutoriel, nous vous recommandons, avant de commencer, de :
Regardez l' enregistrement de l'aperçu conceptuel diffusé en direct
Consultez les blogs de fond, le webinaire et la vidéo
Regardez le résumé vidéo de 18 minutes du laboratoire :
Ce tutoriel utilise ces technologies :
Les instructions pour chaque défi incluent le texte complet des fichiers YAML utilisés pour configurer les applications. Vous pouvez également copier le texte depuis notre dépôt GitHub . Un lien vers GitHub est fourni avec le texte de chaque fichier YAML.
Ce tutoriel comprend trois défis :
Dans ce défi, vous déployez un cluster Minikube et installez Podinfo en tant qu'exemple d'application et d'API. Vous déployez ensuite NGINX Ingress Controller , configurez le routage du trafic et testez la configuration Ingress .
Créez un cluster minikube . Après quelques secondes, un message confirme que le déploiement a réussi.
$ minikube start 🏄 Terminé ! kubectl est maintenant configuré pour utiliser le cluster « minikube » et l'espace de noms « default » par défaut
Podinfo est une « application Web réalisée avec Go qui présente les meilleures pratiques d'exécution de microservices dans Kubernetes ». Nous l'utilisons comme exemple d'application et d'API en raison de son faible encombrement.
À l’aide de l’éditeur de texte de votre choix, créez un fichier YAML appelé 1-apps.yaml avec le contenu suivant (ou copiez-le depuis GitHub ). Il définit un déploiement qui comprend :
apiVersion : apps/v1
type : Déploiement
métadonnées :
nom : api
spécification :
sélecteur :
matchLabels :
application : api
modèle :
métadonnées :
étiquettes :
application : api
spécification :
conteneurs :
- nom : api
image : stefanprodan/podinfo
ports :
- containerPort : 9898
---
apiVersion: v1
type: Service
métadonnées :
nom : api
spécification :
ports :
- port : 80
targetPort : 9898
NodePort : 30001
sélecteur :
application : api
type : Équilibreur de charge
---
apiVersion : apps/v1
kind : Déploiement
métadonnées :
nom : frontend
spécification :
sélecteur :
matchLabels :
application : frontend
modèle :
métadonnées :
étiquettes :
application : frontend
spécification :
conteneurs :
- nom : frontend
image : stefanprodan/podinfo
ports :
- containerPort : 9898
---
apiVersion: v1
type: Service
métadonnées :
nom : frontend
spécification :
ports :
- port : 80
targetPort : 9898
NodePort : 30002
sélecteur :
application : frontend
type : Équilibreur de charge
Déployer l’application et l’API :
$ kubectl apply -f 1-apps.yaml déploiement.apps/api créé service/api créé déploiement.apps/frontend créé service/frontend créé
Confirmez que les pods pour l’API Podinfo et le frontend Podinfo ont été déployés avec succès, comme indiqué par la valeur En cours d’exécution
dans la colonne STATUS
.
$ kubectl get pods NOM PRÊT ÉTAT RESTARTS ÂGE api-7574cf7568-c6tr6 1/1 En cours d'exécution 0 87 s frontend-6688d86fc6-78qn7 1/1 En cours d'exécution 0 87 s
Le moyen le plus rapide d'installer NGINX Ingress Controller est d'utiliser Helm .
Installez NGINX Ingress Controller dans un espace de noms distinct ( nginx ) à l'aide de Helm.
Créer l'espace de noms :
$ kubectl créer un espace de noms nginx
Ajoutez le référentiel NGINX à Helm :
$ helm repo ajouter nginx-stable https://helm.nginx.com/stable
Téléchargez et installez NGINX Ingress Controller dans votre cluster :
$ helm install main nginx-stable/nginx-ingress \ --set contrôleur.watchIngressWithoutClass=true \ --set contrôleur.ingressClass=nginx \ --set contrôleur.service.type=NodePort \ --set contrôleur.service.httpPort.nodePort=30010 \ --set contrôleur.enablePreviewPolicies=true \ --namespace nginx
Confirmez que le pod NGINX Ingress Controller est déployé, comme indiqué par la valeur En cours d'exécution
dans la colonne STATUS
(pour plus de lisibilité, la sortie est répartie sur deux lignes).
$ kubectl get pods -namespace nginx NOM ÉTAT PRÊT ... main-nginx-ingress-779b74bb8b-d4qtc 1/1 En cours d'exécution ... ... REDÉMARRE L'ÂGE... 0 92s
apiVersion : networking.k8s.io/v1
type : Entrée
métadonnées :
nom : premier
spécification :
ingressClassName : nginx
règles :
- hôte : « exemple.com »
http :
chemins :
- backend :
service :
nom : frontend
port :
numéro : 80
chemin : /
type de chemin : Préfixe
- hôte : « api.example.com »
http :
chemins :
- backend :
service :
nom : api
port :
numéro : 80
chemin : /
type de chemin : Préfixe
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/first créé
Pour vous assurer que votre configuration Ingress fonctionne comme prévu, testez-la à l’aide d’un pod temporaire. Lancez un pod BusyBox jetable dans le cluster :
$ kubectl run -ti --rm=true busybox --image=busybox Si vous ne voyez pas d'invite de commande, essayez d'appuyer sur Entrée. / #
Testez l'API Podinfo en émettant une requête au pod NGINX Ingress Controller avec le nom d'hôte api.example.com . La sortie affichée indique que l'API reçoit du trafic.
/ # wget --header="Hôte : api.example.com" -qO- main-nginx-ingress.nginx { "nom d'hôte": "api-687fd448f8-t7hqk", "version": "6.0.3", "révision": "", "couleur": "#34577c", "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", "message": "salutations de podinfo v6.0.3", "goos": "linux", "goarch": "arm64", "runtime": "go1.16.9", "num_goroutine": "6", "num_cpu": "4" }
/ # wget --header="Hôte : exemple.com" --header="Agent utilisateur : Mozilla" -qO-main-nginx-ingress.nginx <!DOCTYPE html>
<html>
<head>
<title>frontend-596d5c9ff4-xkbdc</title>
# ...
Dans un autre terminal, ouvrez Podinfo dans un navigateur. Les salutations de la page podinfo indiquent que Podinfo est en cours d'exécution.
$ minikube service podinfo
Félicitations ! NGINX Ingress Controller reçoit les demandes et les transmet à l'application et à l'API.
Dans le terminal d'origine, terminez la session BusyBox :
/ # sortie $
Dans ce défi, vous installez Locust , un outil de génération de charge open source, et l'utilisez pour simuler une augmentation du trafic qui submerge l'API et provoque le blocage de l'application.
À l’aide de l’éditeur de texte de votre choix, créez un fichier YAML appelé 3-locust.yaml avec le contenu suivant (ou copiez-le depuis GitHub ).
L'objet ConfigMap
définit un script appelé locustfile.py qui génère des requêtes à envoyer au pod, avec les en-têtes corrects. Le trafic n'est pas réparti uniformément entre l'application et l'API : les requêtes sont orientées vers l'API Podinfo , avec seulement 1 requête sur 5 dirigée vers le frontend Podinfo .
Les objets Déploiement
et Service
définissent le pod Locust.
apiVersion : v1
type : ConfigMap
métadonnées :
nom : locust-script
données :
locustfile.py : |-
de locust import HttpUser, task, between
classe QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
avec self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) comme réponse :
si response.request_meta["response_time"] > 200 :
response.failure("Échec du frontend")
else :
response.success()
@task(5)
def visit_api(self) :
avec self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) comme réponse :
si response.request_meta["response_time"] > 200 :
response.failure("Échec de l'API")
else :
response.success()
---
apiVersion : apps/v1
kind : Déploiement
métadonnées :
nom : locust
spécification :
sélecteur :
matchLabels :
application : locust
modèle :
métadonnées :
étiquettes :
application : locust
spécification :
conteneurs :
- nom : locust
image : locustio/locust
ports :
- containerPort : 8089
volumeMounts :
- mountPath : /home/locust
nom : locust-script
volumes :
- nom : locust-script
configMap :
nom : locust-script
---
apiVersion : v1
kind : Service
métadonnées :
nom : locust
spécification :
ports :
- port : 8089
targetPort : 8089
NodePort : 30015
sélecteur :
application : locust
type : Équilibreur de charge
Déployer Locust :
$ kubectl apply -f 3-locust.yaml configmap/locust-script créé déploiement.apps/locust créé service/locust créé
kubectl
apply
et l'installation est donc toujours en cours, comme indiqué par la valeur ContainerCreating
pour le pod Locust dans le champ STATUS
. Attendez que la valeur soit En cours d’exécution
avant de passer à la section suivante. (La sortie est répartie sur deux lignes pour plus de lisibilité.)$ kubectl get pods NOM PRÊT STATUT ... api-7574cf7568-c6tr6 1/1 En cours d'exécution ... frontend-6688d86fc6-78qn7 1/1 En cours d'exécution ... locust-77c699c94d-hc76t 0/1 ContainerCreating ... ... REDÉMARRE L'ÂGE... 0 33 m ... 0 33 m ... 0 4s
Ouvrez Locust dans un navigateur.
$ minikube service locust
Saisissez les valeurs suivantes dans les champs :
Cliquez sur le bouton Démarrer l'essaimage pour envoyer du trafic vers l'API Podinfo et le frontend Podinfo . Observez les schémas de trafic sur les onglets Locust Charts et Failures :
Ceci est problématique car un seul mauvais acteur utilisant l’API peut faire tomber non seulement l’API, mais toutes les applications servies par NGINX Ingress Controller !
Dans le dernier défi, vous déployez deux contrôleurs d'entrée NGINX pour éliminer les limitations du déploiement précédent, en créant un espace de noms distinct pour chacun, en installant des instances distinctes de contrôleur d'entrée NGINX pour Podinfo Frontend et Podinfo API , en reconfigurant Locust pour diriger le trafic de l'application et de l'API vers leurs contrôleurs d'entrée NGINX respectifs et en vérifiant que la limitation de débit est efficace . Commençons par voir comment résoudre le problème architectural. Dans le défi précédent, vous avez submergé le contrôleur d'entrée NGINX avec des requêtes API, ce qui a également eu un impact sur l'application. Cela s'est produit parce qu'un seul contrôleur d'entrée était responsable du routage du trafic vers l'application Web ( Podinfo Frontend ) et l'API ( Podinfo API ).
L’exécution d’un pod NGINX Ingress Controller distinct pour chacun de vos services empêche votre application d’être impactée par trop de requêtes API. Cela n’est pas nécessairement nécessaire pour chaque cas d’utilisation, mais dans notre simulation, il est facile de voir les avantages de l’exécution de plusieurs contrôleurs d’entrée NGINX.
La deuxième partie de la solution, qui empêche l’API Podinfo d’être surchargée, consiste à implémenter une limitation de débit en utilisant NGINX Ingress Controller comme passerelle API.
La limitation du débit restreint le nombre de requêtes qu'un utilisateur peut effectuer sur une période donnée. Pour atténuer une attaque DDoS, par exemple, vous pouvez utiliser la limitation de débit pour limiter le débit des requêtes entrantes à une valeur typique pour les utilisateurs réels. Lorsque la limitation de débit est implémentée avec NGINX, les clients qui soumettent trop de requêtes sont redirigés vers une page d'erreur afin de ne pas avoir d'impact négatif sur l'API. Découvrez comment cela fonctionne dans la documentation de NGINX Ingress Controller .
Une passerelle API achemine les requêtes API des clients vers les services appropriés. Une interprétation erronée de cette définition simple est qu’une passerelle API est une pièce technologique unique. Ce n'est pas le cas. Au contraire, la « passerelle API » décrit un ensemble de cas d’utilisation qui peuvent être mis en œuvre via différents types de proxys – le plus souvent un ADC ou un équilibreur de charge et un proxy inverse, et de plus en plus un contrôleur d’entrée ou un maillage de services. La limitation du débit est un cas d’utilisation courant pour le déploiement d’une passerelle API. Pour en savoir plus sur les cas d’utilisation de la passerelle API dans Kubernetes, consultez Comment choisir ? Passerelle API vs. Contrôleur d’entrée vs. Service Mesh sur notre blog.
Avant de pouvoir implémenter la nouvelle architecture et la limitation de débit, vous devez supprimer la configuration précédente du contrôleur d’entrée NGINX.
$ kubectl delete -f 2-ingress.yaml ingress.networking.k8s.io "premier" supprimé
Créez un espace de noms appelé nginx‑web pour le frontend Podinfo :
$ kubectl create namespace nginx-web namespace/nginx-web créé
Créez un espace de noms appelé nginx‑api pour l’API Podinfo :
$ kubectl create namespace nginx-api namespace/nginx-api créé
Installer le contrôleur d’entrée NGINX :
$ 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
Créez un manifeste Ingress appelé 4-ingress-web.yaml pour Podinfo Frontend (ou copiez-le depuis GitHub ).
apiVersion: k8s.nginx.org/v1 type: Métadonnées de la politique : nom : rate-limit-policy spec : rateLimit : rate : Clé 10r/s : ${binary_remote_addr} zoneSize : 10M --- apiVersion: k8s.nginx.org/v1 type: Métadonnées du serveur virtuel : nom : api-vs spec : ingressClassName : nginx-api host : api.example.com Policies : - nom : rate-limit-policy upstreams : - nom : api service : api port : 80 itinéraires : - chemin : / action : pass : api
Déployer le nouveau manifeste :
$ kubectl apply -f 4-ingress-web.yaml ingress.networking.k8s.io/frontend créé
Maintenant, reconfigurez Locust et vérifiez que :
Procédez comme suit :
Modifiez le script Locust afin que :
Étant donné que Locust ne prend en charge qu'une seule URL dans le tableau de bord, codez en dur la valeur dans le script Python à l'aide du fichier YAML 6-locust.yaml avec le contenu suivant (ou copiez-le depuis GitHub ). Prenez note des URL dans chaque tâche
.
apiVersion : v1
type : ConfigMap
métadonnées :
nom : locust-script
données :
locustfile.py : |-
de locust import HttpUser, task, between
classe QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
avec self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) comme réponse :
si response.request_meta["response_time"] > 200 :
response.failure("Échec du frontend")
else :
response.success()
@task(5)
def visit_api(self) :
avec self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) comme réponse :
si response.request_meta["response_time"] > 200 :
response.failure("Échec de l'API")
else :
response.success()
---
apiVersion : apps/v1
kind : Déploiement
métadonnées :
nom : locust
spécification :
sélecteur :
matchLabels :
application : locust
modèle :
métadonnées :
étiquettes :
application : locust
spécification :
conteneurs :
- nom : locust
image : locustio/locust
ports :
- containerPort : 8089
volumeMounts :
- mountPath : /home/locust
nom : locust-script
volumes :
- nom : locust-script
configMap :
nom : locust-script
---
apiVersion : v1
kind : Service
métadonnées :
nom : locust
spécification :
ports :
- port : 8089
targetPort : 8089
NodePort : 30015
sélecteur :
application : locust
type : Équilibreur de charge
Déployez la nouvelle configuration Locust. La sortie confirme que le script a changé mais les autres éléments restent inchangés.
$ kubectl apply -f 6-locust.yaml configmap/locust-script configuré déploiement.apps/locust inchangé service/locust inchangé
Supprimez le pod Locust pour forcer un rechargement du nouveau ConfigMap. Pour identifier le pod à supprimer, l’argument de la commande kubectl
delete
pod
est exprimé sous forme de commandes canalisées qui sélectionnent le pod Locust dans la liste de tous les pods.
$ kubectl supprimer le pod `kubectl obtenir les pods | grep locust | awk {'print $1'}`
Vérifiez que Locust a été rechargé (la valeur du pod Locust dans la colonne AGE
n'est que de quelques secondes).
$ kubectl get pods NOM PRÊT STATUT ... api-7574cf7568-jrlvd 1/1 En cours d'exécution ... frontend-6688d86fc6-vd856 1/1 En cours d'exécution ... locust-77c699c94d-6chsg 0/1 En cours d'exécution ... ... REDÉMARRE L'ÂGE... 0 9m57s ... 0 9m57s ... 0 6s
Revenez à Locust et modifiez les paramètres dans ces champs :
Cliquez sur le bouton Démarrer l'essaimage pour envoyer du trafic vers l'API Podinfo et le frontend Podinfo .
Dans la barre de titre Locust en haut à gauche, observez comment, à mesure que le nombre d'utilisateurs augmente dans la colonne STATUT , la valeur dans la colonne ÉCHECS augmente également. Cependant, les erreurs ne proviennent plus du frontend Podinfo mais plutôt de l'API Podinfo car la limite de débit définie pour l'API signifie que des demandes excessives sont rejetées. Dans la trace en bas à droite, vous pouvez voir que NGINX renvoie le message503
Service
temporairement
indisponible
, qui fait partie de la fonctionnalité de limitation de débit et peut être personnalisé. L'API est limitée et l'application Web est toujours disponible. Bien joué!
Dans le monde réel, la limitation du débit à elle seule ne suffit pas à protéger vos applications et API des mauvais acteurs. Vous devez mettre en œuvre au moins une ou deux des méthodes suivantes pour protéger les applications, les API et l’infrastructure Kubernetes :
Nous couvrons ces sujets et bien d’autres dans l’unité 3 de Microservices mars 2022 – Modèle de sécurité des microservices dans Kubernetes . Pour essayer NGINX Ingress Controller pour Kubernetes avec NGINX Plus et NGINX App Protect, démarrez votre essai gratuit de 30 jours dès aujourd'hui ou contactez-nous pour discuter de vos cas d'utilisation . Pour essayer NGINX Ingress Controller avec NGINX Open Source, vous pouvez obtenir le code source de la version ou télécharger un conteneur prédéfini depuis DockerHub .
« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."