Si vous êtes coutumier de l’utilisation des applications en ligne de la DGI (Direction générale des impôts) au Cameroun, vous avez surement remarquez que ceux-ci sont presque toujours victimes de lenteurs et autres dysfonctionnements (défaut d’affichage, affichage incomplet des interfaces, etc.) à l’approche des dates butoirs des déclarations fiscales mensuelles ou annuelles. Curieusement quand vous êtes hors de ces périodes de fortes sollicitations, les applications fonctionnent relativement normalement. Ceci pousse naturellement à la conclusion que ces soucis sont probablement dû à la dégradation des performances à la suite de montées de charge.
Je me suis alors posé la question suivante :
Si les grandes plateformes internet étaient capable de construire des infras pour leur application capable de tenir le trafic de centaines de millions voire des milliards de d’utilisateurs en simultanée, qu’est ce qui empêchait techniquement la DGI de mettre en place une infra pour tenir aux alentours de 300 000 utilisateurs en simultanés ? 300 000 étant le nombre d’approximatif d’entreprises au Cameroun. En partant de l’hypothèse d’une connexion simultanée (qui reste peu probable) de ces 300 000 entreprises pour faire leur déclarations, l’objectif des 300 000 utilisateurs simultanées est une bonne estimation et base de départ pour mener des réflexions sur l’architecture de déploiement des applications de la DGI. Je vais donc m’essayer à une analyse pour dégager une esquisse de solution à ce problème. Et de mon expérience, je dirai que la solution à ce problème se joue à plusieurs niveaux ou levier.
Premièrement, le levier matériel.
Ici il s’agit de doper les serveurs physiques ou virtuels avec le max de RAM et de CPU. Le souci c’est que vous ne pouvez pas jouer sur ce levier de façon infinie à cause de la limite naturelle des capacités matérielles de vos serveurs.
En deuxième lieu, le levier du système d’exploitation.
Certains systèmes d’exploitation sont reconnus comme plus optimisés que d’autres niveaux des performances de leur pile réseau quant à la prise en charge des situations de fort trafic. C’est notamment le cas des systèmes d’exploitation basés sur Linux ou UNIX. Ainsi pour son infrastructure de production, une entité qui est préoccupé par les questions de performance et gestion de fortes charges pour ses applications doit s’orienter de préférence vers les systèmes Linux ou Unix.
En troisième lieu, le levier de l’application à déployer en elle-même.
Souvent, les problèmes de performances peuvent être liés à l’application à déployer elle-même. Améliorer les performances à ce niveau va impliquer l’usage de langages, frameworks et architectures logicielles orientées vers la performance applicative en situation de forte charge. D'un point de vue architecture logicielle, l'adoption d'une approche asynchrone pour le traitement de certaines opérations peut s'avérer judicieux. Je pense notamment aux opérations longues durées induis par des calculs complexes, des appels réseaux intensifs ou encore des traitements intensifs de fichiers. Dans de nombreux cas, ceci peut nécessiter une réécriture partielle ou totale de l’application (usage de framework web asynchrone, usage de système distribué de gestion de file d'attente de tâche ou d'un de système de pubsub comme celery, etc.). Ceci est loin d’être évident si vous n’avez pas un accès au code source de l’application (à défaut de pouvoir exiger et obtenir de l’éditeur qu’il optimise en profondeur les performances de son application).
En quatrième lieu, le levier des logiciels serveurs utilisés (serveur de base de données, serveurs HTTP, etc.) et leurs configurations.
Au niveau base de données par exemple, il s’agit notamment d’augmenter autant que le permettent les configurations de votre SGBD, le nombre de connexions simultanées qu’il est capable de supporter de façon optimale. Il peut être aussi nécessaire de mettre en place de la répartition de charge (entre plusieurs instances de votre SGBD) et l'auto-scaling
Au niveau du serveur HTTP, c’est le même objectif que précédemment de gestion d'un grand nombre de connexions simultanées. Il faudra pour cela opter pour un serveur web ou un reverse proxy qui résout le problème C10k. Ce problème désigne un ensemble de limitations matérielles et logicielles qui empêche les serveurs web (ou TCP de façon générale) basés sur une architecture bloquante à base de multi processus et multi-thread de dépasser la barre des 10000 (dix milles) connexions ou requêtes entrantes simultanées. Malheureusement certains serveurs web ne résolvent pas ce problème car ils n’ont été conçus pour répondre à des exigences élevées en termes de concurrence (en effet tous les cas d'usage de déploiement d’application web n'exigent pas de devoir supporter un nombre élevé de connexions utilisateurs). Je pense notamment au vénérable apache (conçu à une époque où les applications web n’avaient pas d’exigences élevées en termes de concurrence) qui, malgré les modes ajoutés pour améliorer la gestion simultanée d’un grand nombre de connexions entrantes (mode worker ou mode event qui est une variante du mode worker), reste relativement peu performant dans des contextes de fort trafic. Ces serveurs web sont donc à utiliser pour des contextes sollicitations modérées de vos applications web. Maintenant, pour ce qui des serveurs HTTP résolvant le problème du C10K, ils fonctionnent généralement suivant une architecture événementielle, asynchrone et non bloquante. Je pense notamment à Nginx (et ses dérivés), litespeed, Tornado, etc. Ainsi, pour la DGI, si le serveur web actuelle n’est pas taillé pour les connexions simultanées élevés, elle devrait soit la remplacer par un serveur nginx (ou autre solutions similaires), soit conserver le serveur actuel (si le remplacer n’est pas envisageable parce que l’application est dépendante des configurations particulières de ce serveur) mais en le couplant à un reverse proxy qui lui résout le problème du C10K (HAproxy, Varnish, Nginx en mode reverse proxy, etc.).
Enfin, le levier de l'auto scaling.
Quel que soit les optimisations que vous pourrez faire au niveau des points précédemment évoqués, il est clair qu’une seule instance d’une application (même écrite avec les langages et architectures logicielles les plus performants) ne peut pas gérer un nombre indéfini d'utilisateurs. Il viendra un moment où vous allez devoir démarrer plusieurs instances de votre application pour satisfaire aux besoins de montée en charge et de performance associées. Vous pourrez le faire manuellement en usant d’alerte provenant de votre système de monitoring. Toutefois, votre niveau de réactivité sera limité et c’est là qu’intervient l’auto-scaling.
L'auto scaling désigne la capacité de votre application (et son infra sous-jacente) à s’adapter automatiquement (en hausse ou en baisse) à la charge de trafic reçu de sorte à garder en permanence un niveau de performance correct tout en optimisant l'utilisation des ressources. Ainsi, si en condition normale de trafic une instance de votre application suffit pour servir correctement les utilisateurs, votre infra devra être capable de démarrer ou éteindre automatiquement des instances de votre application suivant la fluctuation au niveau de sa sollicitation. Il faut noter que la mise en place de l'auto scaling est d’autant plus aisée que votre infrastructure est virtualisée (vos applications tournent dans des VPS) et que votre gestionnaire de machine virtuelle offre des apis ainsi que des capacités de templating pour automatiser la création ou la destruction de machine virtuel à la demande. Chaque VPS devra être configuré de manière à pouvoir faire tourner une instance de l’application dans ses limites naturelles en termes de satisfaction des exigences de performances.
Plusieurs options pour la mise en œuvre de l'auto scaling existent, parmi lesquelles :
L’utilisation des solutions d'auto scaling des grands fournisseurs cloud (pour ceux qui en disposent comme AWS, GCP, Azure, etc.) si votre application y est déjà hébergée ou si l'éventualité d'un hébergement des applications sur des serveurs cloud ne vous pose pas de problème de contrôle et souveraineté des données (Je doute fort que la DGI au Cameroun soit favorable à cette idée). A côté des questions de confidentialité et contrôle de données, il y’a bien entendu la question du coût qui est loin d’être négligeable.
L’utilisation des orchestrateurs de containers qui fournissent pour certains une brique native d'auto scaling des containers. Je pense notamment à Kubernetes ou Nomad de Hashicorp. Cette approche nécessite donc que vos applications soient conteneurisées (ce qui n’est pas toujours le cas ou même envisageable). Il ne faut pas oublier la charge travail induite par la gestion d'un orchestrateur de container.
La solution faite maison. Il s’agit de la dernière option d'auto scaling si vous ne souhaitez pas déployez vos applications dans le cloud, ni utiliser des orchestrateurs de containers.
Focus sur la mise en place d’une solution d'auto scaling fait maison.
A ce niveau, la première chose à faire est de mener un test de performance (via des outils de test de charge comme Jmeter, Gatlin ou Loadrunner) et des tests d’interface utilisateurs (via des outils comme selenium, puppeteer, etc) sur les applications pour déterminer les niveaux de de sollicitations sur les applications à partir desquels on commence à observer des dégradations significatives de performances. Ainsi, pour moi, si une application métier comme celles de le DGI met plus de 10 secondes à afficher les éléments de son interface graphique ou à effectuer des traitements métiers, alors il y ‘a clairement un problème.
Une fois cette étape passée, il y a 03 approches possibles pour envisager une solution d'auto scaling fait maison. La première est orientée par les métriques systèmes (de l'OS et des logiciels serveurs), la seconde est orientée par la disponibilité des serveurs et la dernière est orientée par l’expérience ou ressenti utilisateur.
L'approche orientée métriques systèmes consiste à utiliser des solutions de monitorings qui vont faire des checks réguliers de l’état de charge des paramètres de vos serveurs et déclencher un workflow permettant de démarrer ou supprimer automatiquement les VM hébergeant les instances de l’application (c’est l’approche mis en place dans ce tuto par exemple : https://deepakjd2004.medium.com/build-your-own-byo-autoscaling-solution-using-open-source-tools-on-linode-a05f70678c26).
L’approche orientée disponibilité des serveurs consiste à s’appuyer uniquement sur l’état de disponibilité des machines pour déclencher la mise en route du workflow de création ou de suppression des VM hébergeant les instances de l’application (c’est l’approche mis en place dans ce tuto par exemple : https://blog.stefan-koch.name/2021/05/02/load-balancing-auto-scaling-open-source-haprox).
Enfin, l'approche orientée par l’expérience/ressenti de l’utilisateur consiste à simuler l’usage de l’application avec les outils comme selenium, puppeteer ou cucumber; lesquels vont être associés à des scripts personnalisés. Par la suite on va comparer le comportement réel de l’application avec le comportement attendu pour déterminer si on est en situation de dégradation de performance ou pas. En fonction de cela, on décidera s’il faut détruire certaines instances (si les résultats de test sont bons) ou créer de nouvelles instances (si les résultats de test ne sont pas bons).
Une fois l'auto scaling mis en place, on pourrait tester que tout fonctionne correctement en écrivant un script python automatisant (via une boucle l’envoi en masse de requête http légitime à l’application mais qui adapte (à la hausse ou à la baisse) le rythme de l’envoi à mesure que le temps moyen de réponse aux requêtes est à la hausse ou à la baisse.
Autres problèmes des applications de la DGI :
La réinitialisation des mots de passe par email ou SMS ne fonctionne pas
Certaines applications n’ont pas leur trafic sécurisé par HTTPS. Au-delà de la sécurisation des flux par HTTPS, je serai curieux de savoir ce qui a été mis au niveau de la sécurisation des serveurs web en eux même.
Quelques lectures supplémentaires sur le sujet :
https://fr.wikipedia.org/wiki/C10k_problem, ,
https://forums.docker.com/t/autoscaling-in-docker-swarm/44353/7, ,
https://blog.stefan-koch.name/2021/05/02/load-balancing-auto-scaling-open-source-haproxy, ,
https://www.rapidseedbox.com/blog/haproxy-vs-nginx, ,
https://stackoverflow.com/questions/32372882/measuring-requests-per-second, ,
https://serverfault.com/questions/401040/maximizing-tcp-connections-on-haproxy-load-balancer, ,
https://stackoverflow.com/questions/41668621/how-to-configure-autoscaling-on-docker-swarm, ,https://www.reddit.com/r/hetzner/comments/1292jm3/has_anyone_set_up_autoscaling_on_hetzner/
https://help.cloud66.com/docs/auto-scaling/auto-scaling