Tests de charge distribués avec Kubernetes

Les tests de charge sont essentiels au développement de toute infrastructure de backend, car ils montrent comment le système fonctionne face à des demandes réelles. Un aspect important des tests de charge est la simulation appropriée du comportement de l'utilisateur et de l'appareil afin d'identifier et de comprendre les éventuels goulots d'étranglement du système, bien avant le déploiement des applications en production.

Toutefois, une infrastructure de test dédiée peut s'avérer coûteuse et difficile à gérer car elle n'est pas utile sur le long terme. De plus, une infrastructure de test dédiée représente souvent un investissement ponctuel avec une capacité fixe, ce qui complique l'adaptation des tests de charge au-delà de l'investissement initial et peut limiter les tests. Cela peut ralentir la productivité des équipes de développement et aboutir à des applications qui ne sont pas correctement testées avant les déploiements en production.

Présentation de la solution

Les tests de charge distribués utilisant le cloud computing représentent une option intéressante pour plusieurs scénarios de test. Les plates-formes cloud offrent un degré élevé d'élasticité pour l'infrastructure, facilitant ainsi les tests d'applications et de services avec un grand nombre de clients simulés, chacun générant du trafic calqué sur les utilisateurs ou les appareils. De plus, le modèle de tarification de cloud computing est parfaitement adapté à la nature très élastique des tests de charge.

Les conteneurs, qui offrent une alternative légère à l'exécution d'instances de machine virtuelle complètes pour des applications, sont parfaitement adaptés au scaling rapide de clients simulés. Les conteneurs représentent une excellente abstraction pour l'exécution de clients de test, car ils sont légers, simples à déployer, immédiatement disponibles et adaptés à des tâches uniques.

Google Cloud Platform constitue un excellent environnement pour les tests de charge distribués utilisant des conteneurs. La plate-forme accepte les conteneurs en tant qu'objets de première classe via Google Kubernetes Engine, qui est alimenté par le gestionnaire de cluster de conteneurs Open Source, Kubernetes. Kubernetes Engine permet de provisionner rapidement une infrastructure de conteneur, ainsi que des outils permettant de gérer les applications et les ressources déployées.

Cette solution montre comment utiliser Kubernetes Engine pour déployer un framework de tests de charge distribué. Le framework a recours à plusieurs conteneurs pour créer un trafic de tests de charge pour une API simple basée sur REST. Bien que cette solution teste une application Web simple, le même modèle peut être utilisé pour créer des scénarios de test de charge plus complexes, tels que des applications de jeu ou de l'Internet des objets (IoT) Cette solution présente l'architecture générale d'un framework de test de charge basé sur des conteneurs. Pour obtenir des instructions pas à pas sur la configuration d'un exemple de framework, reportez-vous à la section Tutoriel à la fin de ce document.

Cette solution est axée sur la création d'un trafic de test de charge à l'aide de Kubernetes Engine. Le système soumis aux tests est une application Web simple utilisant une API REST. La solution exploite un framework de tests de charge existant pour modéliser les interactions des API, décrites plus en détails dans les prochaines sections. Après avoir déployé le système soumis aux tests, la solution utilise Kubernetes Engine pour déployer les tâches de tests de charge distribuées.

Système soumis aux tests

Dans la terminologie relative aux tests logiciels, le système soumis aux tests correspond au système pour l'évaluation duquel vos tests sont conçus. Dans cette solution, le système soumis aux tests est une petite application Web déployée sur Google App Engine. L'application expose les points de terminaison de base de type REST pour enregistrer les requêtes HTTP POST entrantes (les données entrantes ne sont pas conservées). Dans un scénario réel, les applications Web peuvent être complexes et inclure une multitude de composants et de services supplémentaires, tels que la mise en cache, la messagerie et la persistance. Ces éléments complexes n'entrent pas dans le champ d'application de cette solution. Pour en savoir plus sur la création d'applications Web évolutives sur Google Cloud Platform, reportez-vous à la solution Créer des applications Web évolutives et résilientes.

Le code source de l'exemple d'application est disponible dans le tutoriel, à la fin de ce document.

Exemples de charges de travail

L'exemple d'application est modélisé d'après le composant de service de backend situé dans de nombreux déploiements de l'Internet des objets (IoT). Les appareils s'enregistrent d'abord auprès du service, puis commencent à créer des rapports de métriques ou de relevés de capteur, tout en s'enregistrant périodiquement auprès du service.

Le schéma ci-dessous illustre une interaction commune du composant de service de backend.

Schéma illustrant une interaction commune du composant de service de backend

Pour modéliser cette interaction, vous pouvez utiliser Locust, un outil de tests de charge distribué basé sur Python, capable de distribuer des requêtes sur plusieurs chemins d'accès cibles. Par exemple, Locust peut distribuer des requêtes sur les chemins d'accès cibles /login et /metrics. Il existe de nombreux packages logiciels de génération de charges disponibles, parmi lesquels JMeter, Gatling et Tsung, plus ou moins adaptés aux besoins de votre projet.

La charge de travail est basée sur l'interaction décrite ci-dessus et est modélisée en tant qu'ensemble de tâches dans Locust. Pour se rapprocher des clients du monde réel, chaque tâche Locust est pondérée. Par exemple, l'enregistrement se produit tous les milliers de requêtes client.

Solutions de calcul basées sur des conteneurs

D'un point de vue architectural, le déploiement de cette solution de tests de charge distribuée comprend deux composants principaux : l'image de conteneur Locust et le mécanisme de gestion et d'orchestration du conteneur.

L'image du conteneur Locust est une image Docker contenant le logiciel Locust. Le Dockerfile se trouve dans le dépôt GitHub associé (reportez-vous au tutoriel ci-dessous). Le Dockerfile utilise une image Python de base et inclut des scripts pour démarrer le service Locust et exécuter les tâches.

Cette solution utilise Google Kubernetes Engine comme mécanisme de gestion et d'orchestration du conteneur. Basé sur le framework Open Source Kubernetes, Kubernetes Engine est le fruit de nombreuses années d'expérience dans l'exécution, l'orchestration et la gestion de déploiements de conteneurs dans Google. Les solutions de calcul basées sur des conteneurs permettent aux développeurs de se concentrer sur leurs applications plutôt que sur les déploiements et les intégrations dans des environnements d'hébergement. Les conteneurs facilitent également la portabilité des tests de charge afin que les applications en conteneurs puissent s'exécuter sur plusieurs environnements cloud. Kubernetes Engine et Kubernetes introduisent plusieurs concepts spécifiques à l'orchestration et à la gestion de conteneurs.

Clusters de conteneurs

Un cluster de conteneurs est un groupe d'instances Compute Engine qui constitue la base de toute votre application. La documentation Kubernetes Engine et Kubernetes fait référence à ces instances en tant que nœuds. Un cluster comprend un seul nœud maître et un ou plusieurs nœuds de calcul. Le nœud maître et les nœuds de calcul s'exécutent tous sur Kubernetes. C'est pourquoi les clusters de conteneurs sont parfois appelés clusters Kubernetes. Pour plus d'informations sur les clusters, consultez la documentation de Kubernetes Engine.

Pods

Un pod est un groupe de conteneurs étroitement liés qui doivent être déployés ensemble. Certains pods ne contiennent qu'un seul conteneur. Par exemple, dans cette solution, chaque conteneur Locust s'exécute dans son propre pod. Toutefois, les pods contiennent souvent plusieurs conteneurs qui fonctionnent ensemble d'une manière ou d'une autre. Par exemple, dans cette solution, Kubernetes utilise un pod avec trois conteneurs pour fournir des services DNS. Dans un conteneur, SkyDNS fournit la fonctionnalité de serveur DNS. SkyDNS s'appuie sur un magasin de paires valeur/clé, nommé etcd, qui réside dans un autre conteneur. Dans le troisième conteneur du pod, kube2sky fait le lien entre Kubernetes et SkyDNS.

Contrôleurs de réplication

Un contrôleur de réplication garantit qu'un certain nombre de pods dupliqués s'exécutent en même temps. S'il y en a trop, le contrôleur de réplication supprime certains pods. S'il n'y en a pas assez, il en crée davantage. Cette solution comporte trois contrôleurs de réplication : le premier assure l'existence d'un seul pod de serveur DNS, le deuxième gère un seul pod maître de serveur DNS et le troisième gère exactement 10 pods de nœuds de calcul Locust en cours d'exécution.

Services

Un pod spécifique peut disparaître pour plusieurs raisons : par exemple, en cas de défaillance des nœuds ou d'une interruption volontaire des nœuds pour effectuer des opérations de mises à jour ou de maintenance. Cela signifie que l'adresse IP d'un pod ne fournit pas une interface suffisamment fiable. Une approche plus fiable serait d'utiliser une représentation abstraite de cette interface qui ne change jamais, même si le pod sous-jacent disparaît et est remplacé par un nouveau pod avec une adresse IP différente. Un service Kubernetes Engine fournit ce type d'interface abstraite en définissant un ensemble logique de pods et une règle permettant d'y accéder. Dans cette solution, plusieurs services représentent des pods ou des ensembles de pods. Par exemple, il existe un service pour le pod du serveur DNS, un autre service pour le pod maître Locust et un service représentant les 10 pods de nœuds de calcul Locust.

Le schéma ci-dessous illustre le contenu des nœuds maîtres et des nœuds de calcul.

Schéma illustrant le contenu des nœuds maîtres et des nœuds de calcul.

Déployer un système soumis aux tests

La solution exécute le système soumis aux tests à l'aide de Google App Engine. Pour déployer le système soumis aux tests, vous devez disposer d'un compte Google Cloud Platform actif pour pouvoir installer et exécuter le SDK Google Cloud Platform. Avec le SDK en place, vous pouvez déployer l'exemple d'application Web à l'aide d'une seule commande. Le code source pour l'application Web est disponible dans le tutoriel, à la fin de ce document.

Déployer des tâches de tests de charge

Avant de déployer les tâches de tests de charge, vous devez d'abord déployer un nœud maître de tests de charge, puis un groupe de 10 nœuds de calcul de tests de charge. Avec une telle quantité de nœuds de calcul de tests de charge, vous pouvez créer une quantité de trafic importante à des fins de test. Toutefois, n'oubliez pas que la génération d'un volume excessif de trafic vers des systèmes externes peut s'apparenter à une attaque par déni de service. Assurez-vous de passer en revue les Conditions d'utilisation de Google Cloud Platform et les Règles d'utilisation autorisée Google Cloud Platform.

Nœud maître de tests de charge

Le premier composant du déploiement est le nœud maître Locust, qui constitue le point d'entrée pour l'exécution des tâches de tests de charge décrites ci-dessus. Le nœud maître Locust est déployé en tant que contrôleur de réplication avec une seule réplication, car un seul nœud maître est nécessaire. Un contrôleur de réplication est utile même lors du déploiement d'un seul pod, car il garantit une haute disponibilité.

La configuration du contrôleur de réplication spécifie plusieurs éléments, y compris le nom du contrôleur (locust-master), les libellés de l'organisation (name: locust, role: master) et les ports qui ont besoin d'être exposés par le conteneur (8089 pour l'interface Web 5557 et 5558 pour la communication avec les nœuds de calcul). Ces informations servent ensuite à la configuration du contrôleur de nœuds de calcul Locust. L'extrait de code suivant contient la configuration des ports :

...
ports:
  - name: locust-master-web
    containerPort: 8089
    protocol: TCP
  - name: locust-master-port-1
    containerPort: 5557
    protocol: TCP
  - name: locust-master-port-2
    containerPort: 5558
    protocol: TCP

Déployez ensuite un service pour vous assurer que les ports exposés sont accessibles aux autres pods via hostname:port dans le cluster et peuvent être référencés via un nom de port descriptif. L'utilisation d'un service permet aux nœuds de calcul Locust de découvrir facilement et de communiquer de manière fiable avec le nœud maître, même si celui-ci tombe en panne et que le contrôleur de réplication le remplace par un nouveau pod. Le service de nœud maître Locust comprend également une instruction permettant de créer une règle de transfert externe au niveau du cluster, afin que le trafic externe puisse accéder aux ressources du cluster. Veuillez prendre en compte que vous devez toujours créer des règles de pare-feu pour fournir un accès complet aux instances cibles.

Après avoir déployé le nœud maître Locust, vous pouvez accéder à l'interface Web à l'aide de l'adresse IP publique de la règle de transfert externe. Après avoir déployé les nœuds de calcul Locust, vous pouvez démarrer la simulation et consulter des statistiques globales via l'interface Web Locust.

Nœuds de calcul de tests de charge

Le composant suivant du déploiement comprend les nœuds de calcul Locust, qui exécutent les tâches de tests de charge décrites ci-dessus. Les nœuds de calcul Locust sont déployés par un seul contrôleur de réplication, qui crée 10 pods. Les pods sont répartis dans le cluster Kubernetes. Chaque pod utilise des variables d'environnement afin de contrôler des informations de configuration importantes, telles que le nom d'hôte du système soumis aux tests et le nom d'hôte du nœud maître Locust. La configuration du contrôleur de réplication du nœud de calcul est décrite dans le tutoriel ci-dessous. La configuration comprend le nom du contrôleur, locust-worker, les libellés d'organisation, name: locust, role: worker, et les variables d'environnement décrites précédemment. L'extrait de code suivant contient la configuration pour le nom, les libellés et le nombre de réplications :

kind: ReplicationController
  apiVersion: v1
  metadata:
    name: locust-worker
    labels:
      name: locust
      role: worker
  spec:
    replicas: 10
    selector:
      name: locust
      role: worker
...

Pour les nœuds de calcul Locust, aucun service supplémentaire n'a besoin d'être déployé, car les pods de nœuds de calcul ne doivent pas nécessairement accepter les communications entrantes : ils se connectent directement au pod de nœud de calcul Locust.

Le schéma suivant illustre la relation entre le nœud maître Locust et les nœuds de calcul Locust.

Schéma illustrant les pods Kubernetes avec les nœuds maîtres et les nœuds de calcul Locust.

Une fois que le contrôleur de réplication a déployé les nœuds de calcul Locust, vous pouvez revenir à l'interface Web du nœud maître Locust et constater que le nombre d'esclaves correspond au nombre de nœuds de calcul déployés.

Exécuter des tâches de tests de charge

Démarrer les tests de charge

L'interface Web du nœud maître Locust vous permet d'exécuter les tâches de tests de charge sur le système soumis aux tests, comme illustré dans l'image suivante :

Capture d'écran de l'interface Web initiale Locust.

Pour commencer, indiquez le nombre total d'utilisateurs à simuler et le taux auquel chaque utilisateur doit être généré. Cliquez ensuite sur Start swarming (Démarrer le travail en essaim) pour commencer la simulation. À mesure que le temps passe et que les utilisateurs apparaissent, les statistiques commencent à regrouper les métriques de simulation, telles que le nombre de requêtes et le nombre de requêtes par seconde, comme illustré dans l'image suivante :

Capture d'écran de l'interface Web Locust lorsqu'un travail en essaim est en cours.

Pour arrêter la simulation, cliquez sur Stop (Arrêter). Les résultats complets peuvent être téléchargés sous forme d'une feuille de calcul.

Faire évoluer des clients

Pour faire évoluer le nombre d'utilisateurs simulés, une augmentation du nombre de pods de nœuds de calcul Locust est nécessaire. Comme spécifié dans le contrôleur de nœuds de calcul Locust, le contrôleur de réplication déploie 10 pods de nœuds de calcul Locust. Pour augmenter le nombre de pods déployés par le contrôleur de réplication, Kubernetes offre la possibilité de redimensionner les contrôleurs sans avoir à les redéployer. Par exemple, vous pouvez modifier le nombre de pods de nœuds de calcul à l'aide de l'outil de ligne de commande kubectl. La commande suivante permet de faire évoluer le pool de pods de nœuds de calcul Locust au nombre de 20 :

$ kubectl scale --replicas=20 replicationcontrollers locust-worker

Après avoir émis la commande "scale", attendez quelques minutes pour le déploiement et le démarrage de tous les pods. Une fois que tous les pods ont démarré, revenez à l'interface Web du nœud maître Locust et relancez les tests de charge.

Ressources et coûts

Cette solution utilise quatre nœuds Kubernetes Engine, chacun étant sauvegardé sur une instance de VM standard Compute Engine de type n1-standard-1. Vous pouvez également utiliser le simulateur de coût Google Cloud Platform pour obtenir une estimation du coût mensuel de l'exécution de ce cluster de conteneurs. Comme indiqué précédemment, vous pouvez adapter la taille du cluster de conteneurs en fonction de vos besoins. Le simulateur de coût vous permet de personnaliser les caractéristiques du cluster afin d'obtenir une estimation du coût d'un scaling à la hausse ou à la baisse.

Étapes suivantes

Vous savez désormais créer un framework de tests de charge pour une application Web simple à l'aide de Kubernetes Engine. Kubernetes Engine vous permet de spécifier le nombre de nœuds de conteneur qui constituent la base de votre framework de tests de charge. Kubernetes Engine vous permet également d'organiser vos nœuds de calcul de tests de charge en pods et d'indiquer le nombre de pods que vous souhaitez continuer à exécuter dans Kubernetes Engine.

Vous pouvez exploiter ce même modèle pour créer des frameworks de tests de charge adaptés à différents scénarios et applications. Par exemple, vous pouvez utiliser ce modèle pour créer des frameworks de tests de charge pour des systèmes de messagerie, des systèmes de gestion de flux de données et des systèmes de base de données. Vous pouvez créer des tâches Locust ou même basculer vers un framework de tests de charge différent.

Une autre méthode pour étendre le framework présenté dans cette solution consiste à personnaliser les métriques collectées. Par exemple, vous pouvez mesurer le nombre de requêtes par seconde ou surveiller le temps de latence des réponses lorsque la charge augmente, ou encore vérifier les taux d'échec des réponses et les types d'erreur. Plusieurs options de surveillance sont disponibles, y compris Google Cloud Monitoring.

Tutoriel

Vous trouverez le contenu complet du tutoriel, y compris les instructions et le code source, sur GitHub à l'adresse https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes.

Cette page vous a-t-elle été utile ? Évaluez-la :

Envoyer des commentaires concernant…