+ 33 6 68 40 27 75 contact@odeven.fr

Les Secrets de la Scalabilité : 4. La Fiabilité et la résilience

par | Avr 26, 2024 | DevOps

Préambule

Dans l’univers du développement logiciel, la scalabilité d’une application est une pierre angulaire déterminante de son succès à long terme. Alors que les entreprises cherchent constamment à optimiser leurs systèmes pour gérer des charges d’utilisateurs croissantes et des volumes de données en augmentation, l’architecture de ces systèmes joue un rôle crucial.

Nous verrons ensemble comment concevoir votre application de manière à ce qu’elle soit scalable, et les solutions possibles qui vous permettront de dormir sur vos deux oreilles !

Cet article est le quatrième d’une liste de 5 chapitres, concernant les secrets de la scalabilité :

  1. Les Pratiques de Développement
  2. L’Infrastructure
  3. Les Performances et l’Optimisation
  4. La Fiabilité et la résilience
  5. Le Monitoring

Scalabilité : Fiabiliser l’application

La scalabilité ne se limite pas à la capacité d’un service d’augmenter ses ressources pour fournir un service plus performant. La fiabilité de l’application est un aspect indispensable à prendre en compte afin d’éviter à avoir à mettre en production un nombre d’instance élevé.

C’est ce qu’on appelle la résilience, c’est-à-dire la capacité d’une application à continuer à fonctionner en cas de panne de serveur, de piratage, d’augmentation de la charge de travail, d’incident, etc…

Conception pour la panne

Concevoir une application pour gérer la panne est un aspect à prendre en compte dès le début de la phase d’analyse. S’il s’agit d’une architecture monolithique, il y a alors généralement peu de latitude pour mettre en place des mécanismes intéressants, mais cela ne signifie pas que rien ne peut être fait. Dans le cas d’une architecture orientée service ou microservices, il y a alors plus de possibilités.

1. Identifier les composants, intervenants et ressources

La première chose à faire, c’est d’identifier les différents composants/intervenants de votre application :

  • Les bases de données
  • Les différents services de l’application
  • Les services tiers (APIs externes)
  • Etc…

A cela, vous pourrez ajouter les ressources matérielles pouvant poser problème :

  • Stockage saturé
  • Mémoire saturée
  • Connexion limitée ou indisponible
  • Etc…

2. Identifier les risques

Ensuite, vous pourrez dresser une liste de questions correspondant à chaque aspect de l’application, dans le cas où ils feraient défaut. Exemples :

  • Que se passe-t-il si la base de données est inaccessible ?
  • Que se passe-t-il s’il n’y a plus d’espace disque disponible ?
  • Que se passe-t-il si le système de paiement en ligne est hors service ?
  • Que se passe-t-il si le service de statistiques est inaccessible ?
  • Etc…

Ces questions vous aideront dans votre réflexion afin d’identifier les conséquences d’une panne sur le reste de l’application.

3. Définir le comportement souhaité

S la réponse à l’une de ces questions ne convient pas à ce que vous souhaiteriez, il conviendra de trouver une solution visant à limiter les dégâts.

Il n’existe malheureusement pas de solution clé-en-main pour résoudre ces problématiques. Il sera nécessaire d’analyser principalement la partie métier de l’application afin de définir le comportement à adopter, afin de limiter au maximum l’impact de la panne.

Par exemple :

  • Si l’espace de stockage disponible peut être un problème : Vous pouvez cloisonner les différents types de données, en séparant les bases de données, et les fichiers uploadés par les utilisateurs. De cette manière, si les utilisateurs saturent l’espace dédié à l’hébergement des fichiers, la base de données restera opérationnelle, et seul l’upload de fichier pourrait être inaccessible. Alors que si vous stockez toutes les données sur le même disque, sans limite, alors l’intégralité du système pourrait se retrouver en panne en cas de saturation.
  • Si la base de données est inaccessible : pour une application Web, il peut être intéressant de pouvoir délivrer au minimum une page d’accueil ou de maintenance « user-friendly ». Ces pages ne nécessiteraient aucun accès à la base de données, afin d’avoir un contenu présentable à l’utilisateur.
  • Si le service de « statistiques » est inaccessible : puisqu’il s’agit généralement d’une fonctionnalité non essentielle, il faudrait concevoir les différentes pages et fonctionnalités d’une application pour qu’elles puissent fonctionner, même si l’accès aux statistiques est indisponible. Si un traitement nécessite l’écriture de données dans le module de « statistiques », vous pouvez utiliser une file d’attente de message (ex: AMQP, Kafka, etc…) au lieu d’un accès direct à l’API du service, afin que le message puisse être traité lorsque le service « statistiques » sera de nouveau opérationnel, sans empêcher votre traitement de continuer.

4. Faire du Chaos Engineering

Le Chaos Engineering est une approche visant à provoquer volontairement des pannes et instabilités dans le système, afin de valider le comportement de l’application ou des services, dans les situations mises en avant grâce aux questions précédentes.

Une manière simple de tester ce type de situation consiste à arrêter un service manuellement, et de vérifier le comportement des autres services.

Bien sûr, du Chaos Engineering manuel n’est pas viable pour des projets de grosse envergure, dans lesquels ces aspects ont une importante capitale. Il conviendra dans ce cas d’intégrer le Chaos Engineering dans le cycle de vie de l’application, en automatisant ces tests.

Santé des containers

Docker intègre nativement une fonctionnalité capable de vérifier régulièrement la santé d’un container dans l’optique de le redémarrer si quelque chose ne va pas. Directement dans le fichier Dockerfile avec la commande « HEALTHCHECK » ou dans un fichier docker-compose.yml avec l’option « healthcheck », vous pouvez définir le test à exécuter visant à vérifier l’état du container.

Ces commandes peuvent être de simples appels CURL sur un endpoint HTTP, un ping, un script personnalisé, etc…

Avec Kubernetes, qui utilise son propre système, vous pouvez utiliser les notions de « liveness probe » et de « readiness probe » afin de configurer un pod de manière à connaître son état.

Grâce à ce type de configuration, vous pouvez vous assurer que votre service ne restera pas indéfiniment dans un état bloquant. L’idéal est bien sûr de résoudre les problèmes à l’origine de ces situations, mais l’utilisation de ces outils constituent un garde fou intéressant pour limiter les dégâts.

Tests de charge et de résistance

Afin d’éviter de mauvaises surprises, il peut être utile d’effectuer ce que l’on appel des tests de charge (ou « load tests ») et des tests de résistance (ou « stress tests »). Ces tests ont pour but d’identifier les goulots d’étranglement, les limites de l’application, et de vérifier que son comportement est acceptable dans chaque situation.

Certains outils peuvent vous aider à tester ces situations, tels que JMeter qui est open-source.

Tests de charge

Les tests de charge consistent à tester une fonctionnalité dans le cas où la charge atteint le maximum attendu. Pour une application Web par exemple, on peut estimer le nombre maximum d’utilisateur connectés en parallèle. Les tests auront ainsi pour but de soumettre l’application à un volume équivalent au pic estimé, afin de s’assurer que le système soit capable de délivrer le contenu avec des performances acceptables.

Ces tests peuvent mettre en lumière certaines fonctionnalités isolées pouvant impacter l’intégralité de l’application et l’expérience utilisateur, et ainsi apporter des pistes d’amélioration pertinentes.

Tests de résistance

Les tests de résistance consistent à soumettre l’application à une charge extrêmement lourde, au-delà des seuils attendus, pouvant engendrer une défaillance du système. Ces tests permettent d’identifier le seuil de sollicitation du système à partir duquel l’application bascule en mode « dégradé », et de s’assurer du bon rétablissement du système après un retour à une charge normale.

Associés à la « conception pour la panne » vue plus haut, ces tests permettent de limiter l’impact en cas de situation anormale.

Conclusion

La résilience d’une application et sa fiabilité sont des aspects importants à prendre en compte dans une stratégie de scalabilité optimale. Bien qu’exceptionnelles, ces situations anormales peuvent devenir très problématiques si elles n’ont pas été gérées. Les tests de charge et de résistance permettent de s’assurer du comportement de l’application, tandis que la conception pour la panne permet de proposer un mode « dégradé » préférable à une indisponibilité totale de l’application.

Nous verrons dans un prochain article les aspects de la scalabilité qui concernent le monitoring, alors j’espère vous revoir bientôt 🙂