Pour savoir où on va, il faut savoir d'où l'on vient

Vous avez
une question ?
Un projet ?

Contactez nous !
 

Contactez-nous

Vous avez une question ? un projet ? 
Vous souhaitez plus d'informations sur un produit ? sur notre offre ? 
Contactez-nous, on vous répond sous 4H.

retour

Une montée en puissance ordinaire

Une montée en puissance ordinaire

Le problème posé

Prenons un petit site web ordinaire, qui tourne sur un serveur unique. Peu importe le système d’exploitation, les outils de développement et frameworks ou le SGBD retenu, nous ne parlons ici que d’architecture.

On peut le schématiser comme suit :

image138

Un serveur HTTP, Apache sur le schéma, une application frontale web et une base de données. Le bloc « Appli Front Web » pourrait, selon les cas, se décomposer en différentes couches, en particulier un serveur d’application tel que Tomcat ou JBoss, ou encore un interpréteur de langage tel que PHP ou Perl. Mais ce découpage ne nous est pas utile ici.

Il n’y a aucune métrique pour nous dire combien de pages par seconde cette application pourra servir, cela dépend de trop de paramètres, liés tant à la manière dont elle est codée qu’à son utilisation de la base de données.

Néanmoins, le service tournait de manière satisfaisante. Le site est un succès, l’audience augmente, et petit à petit les performances se dégradent. En général, le premier signal n’est pas juste un temps de réponse qui augmente légèrement. La première alerte vient sur des heures de pointes où le temps de réponse augmente non pas un peu, mais énormément. Cela dure quelques minutes, puis le trafic retombe et tout semble rentrer dans l’ordre. La sollicitation est auto-régulée, c’est à dire que le ralentissement dégoûte une partie des internautes.

A ce stade, il est essentiel d’avoir mis en place un monitoring qui nous permettra d’identifier ces crêtes mal supportées.

Optimisation

On peut hésiter souvent entre une démarche d’optimisation et une augmentation des ressources. D’un point de vue écologique,

l’augmentation des ressources sans optimisation est une aberration, comparable à monter le chauffage au lieu d’isoler les fenêtres.

Néanmoins l’optimisation peut être coûteuse, et de plus, ses résultats ne sont pas garantis, de sorte que d’un point de vue économique, un simple changement de serveur peut être une meilleure voie.

Extension cellulaire

Une première étape donc, souvent la plus simple, consiste à basculer sur un serveur plus puissant. Il y a quantité de manière d’être plus puissant, et cet upgrade requiert une analyse : processeur multi-coeurs, disques plus rapides, mémoire augmentée, bi-processeur, … Pour que l’opération soit justifiée, il faut qu’il y ait au moins un facteur 2 à gagner, c'est-à-dire que – selon la fameuse loi de Moore – le serveur précédent ait environ 18 mois d’âge.

L’upgrade ne relèvent pas de l’architecture, néanmoins, pour mémoire, représentons le ainsi :

image140

Bien, maintenant on arrive à un bi-processeurs de dernière génération, le succès croît toujours, et ça rame de nouveau.

Extension verticale

L’étape suivante la plus simple est l’extension verticale, ou encore fonctionnelle. Nous avons identifié deux fonctions principales dans cette architecture, l’application frontale et la base de données. L’extension fonctionnelle consiste à affecter des serveurs spécialisés sur chacune de ces fonctions. Ce qui donne le schéma suivant

image142

:

L’avantage du découpage fonctionnel c’est qu’il est presque toujours transparent pour les applications.
Ainsi la base de données est déjà accédée par des protocoles réseau, qu’elle soit sur le même serveur ou bien sur un autre. Le fait de la déporter est totalement transparent pour l’application.

On a maintenant globalement deux fois plus de ressources physiques (CPU, Mémoire, Disques), au service de notre site, ce qui ne peut faire que du bien.

Avant d’aller plus loin, analysons un peu mieux cette configuration.

En premier lieu, on remarque que si n’importe lequel des deux serveurs tombe en panne, le site est arrêté. La probabilité de panne a doublé, et donc le taux d’indisponibilité a doublé. Ce n’est pas bon, mais pas forcément éliminatoire. On peut toujours envisager un secours à froid, disponible sur la plateforme.

Une autre remarque. La consommation de ressources, disons par exemple de CPU, par chacune des fonctions, par chacun des « étages » de l’architecture, est tout à fait corrélée. Une requête typique va induire une charge CF sur le fontal, et une charge CDB sur la base, et l’on peut définir un coefficient k= CF / CDB, sans présager de sa valeur.

Où veut-on en venir ? Supposons maintenant que k=0,5, c’est à dire que le trafic induit une charge deux fois plus forte sur la base de données que sur le frontal. La base de données arrivera à saturation alors que le frontal sera à la moitié de sa capacité. Mais si k=2, alors c’est le contraire, le frontal est saturé longtemps avant la base. D’une manière ou d’une autre, il y a de la ressource gaspillée. Cette architecture manque de flexibilité.

On voit parfois des plateformes allant plus loin encore dans le découpage fonctionnel, en détachant l’étage Apache :

image144

On est content alors d’annoncer une architecture vraiment multi-tiers, comme si le but était d’avoir le plus de « tiers », de couches, possible ! Pourtant, cette architecture est souvent peu intéressante, car d’une part la tolérance aux pannes doit être gérée sur chacun des trois niveaux, et d’autre part chacun des niveaux doit être dimensionné séparément, comme évoqué plus haut.

Clairement, si l’on a un dimensionnement 1-1 entre l’étage HTTP frontal et l’étage applicatif, alors on gaspille de la ressource : l’étage Apache n’a presque rien à faire. A la rigueur, on peut lui faire servir les pages et composants statiques, et donc le configurer de manière optimisée pour cette tâche. On peut aussi positionner un outil de cache frontal sur ce serveur, ou lui faire jouer un rôle de load-balancer de niveau 7.

Extension horizontale

Une étape intermédiaire possible également, est une extension horizontale d’abord, de la manière suivante :

image146

charge.

La base de données présente une contrainte particulière : elle doit le plus souvent être centralisée, afin d’assurer la cohérence des données.

En restant sur une seule « couche », on peut tout à fait partager la base de données, placée sur l’un des serveurs :

image148

 

Ce serveur S1 a davantage de travail pour servir une requête, on peut donc configurer le load-balancer pour lui attribuer un peu moins de requêtes.

Et, pour ne pas ajouter un serveur supplémentaire si l’on peut s’en passer, on utilisera S2 comme secours du SGBD, avec une réplication entre S1 et S2.

image150

En général, la réplication sollicite peu le serveur cible, de sorte qu’il conserve malgré tout une capacité supérieure.

Une alternative à cette réplication de niveau SGBD est une réplication de niveau disque, avec DRBD, déjà vu plus haut.

Dans cette configuration, on choisira de préférence de placer la base de données dans une VM distincte, dans une architecture virtualisée. Ainsi, il sera facile de la déplacer plus tard.

La capacité d’accueil est pratiquement doublée, en temps normal, mais en cas de panne d’un serveur elle revient à ce qu’elle était : on n’a pas simultanément tolérance aux pannes et capacité doublée. Malgré tout, cette configuration horizontale est supérieure à la configuration verticale précédente, qui au contraire dégradait la disponibilité.

Extension en 2D

L’étape suivante, maintenant, consiste à avoir plus de frontaux, tout en détachant l’étage SGBD.

image152

On pourra, ici aussi, répliquer la base de données sur l’un des serveurs disponibles, de manière à disposer d’un secours, ou au minimum d’une sauvegarde en continu.

image154

Ici, on est encore à un stade « modeste », ou « low-cost », où l’on essaye de calibrer le nombre de serveur au plus juste.

Et bien sûr, une fois à 2 frontaux, on peut passer à 3, 4, … N frontaux pour une même base de données, selon le même principe de répartition :

image156

Nous avons là un grand classique des architectures web ordinaires.

On en voit bien les limites : la base de données unique et centrale finira tôt ou tard par être le goulot d’étranglement. A quel stade ? Combien de frontaux pour une base ? Il n’y a aucune règle en la matière. La sollicitation de la base de données, et donc le ratio k cité plus haut, dépend entièrement de la typologie de l’application, de sa bonne utilisation de la base, des couches d’abstraction type Hibernate, d’un cache sur les données, et bien sûr du bon tuning des requêtes. On peut rencontrer des architectures mettant en œuvre une dizaine de frontaux partageant une base de données.

Comme évoqué plus haut, la base est ici aussi point de fragilité, « SPOF » comme on dit. Sur une architecture d’envergure, on n’utilisera pas un frontal comme secours, ce qui est un peu mesquin, on préfèrera privilégier l’homogénéité des configurations et des fonctions.

On met donc en place, a minima, un secours par réplication, comme suit :

image158

Le passage en secours, dans une architecture de ce type, peut être rendu pratiquement transparent. Il faut quelques secondes pour que la base de secours prenne la fonction et les applications doivent se reconnecter sur cette base. Les connexions SGBD en cours sont perdues, mais certains pools de connexions savent gérer une reconnexion automatique, dans un mode « test on borrow ». Lorsque l’application redemandera une connexion, elle obtiendra une connexion valide, sur la nouvelle base.

Spécialisation en écriture / lecture

Mais le succès ne se dément pas, et bien que nous ayons ajouté de nombreux frontaux, ce qui devait arriver arrive : nous avons maintenant 8 frontaux et le serveur de base de données ne tient plus la charge. On a gonflé la configuration au max, on est passé sur un bi-pro, puis un quadri-pro, rajouté des disques, de la mémoire, mais ça ne suffit plus…

Pourra-t-on aller plus loin ?

Nous avons vu que beaucoup de plateformes web ont un taux d’écritures base bien plus faible que de lectures. Dans ce cas, il est intéressant, de répartir les lectures sur différents serveurs, tout en concentrant les écritures sur un serveur unique.

image160

Ici, les frontaux répartissent les lectures sur les différentes bases, mais les écritures ne sont adressées que sur la base de gauche. Les transactions sont répliquées depuis celle-ci, vers toutes les autres bases.

Il y a quelques inconvénients à cette configuration :

  • Elle n’est pas transparente pour les applications, qui doivent distinguer explicitement leurs écritures et leurs lectures.
  • Il y a un petit de propagation avant qu’une écriture ne soit visible sur les autres bases, ce qui peut engendrer des incohérences transitoires.

Bases multiples en réplication croisée

image162

Dans l’architecture précédente, nous avons deux bases de données, qui se partagent la charge, certainement par l’intermédiaire d’un double pool de connexions. Ici les deux bases sont actives, il n’y a pas une base principale et une base de secours.

C’est l’architecture que nous avons mise en place pour un des plus grands sites français autour de 2004, et qui a parfaitement répondu à des charges de plus de 150 000 visites par jour.

Néanmoins, elle présente quelques inconvénients encore :

  • Elle a des impacts fonctionnels, liés au petit décalage entre les serveurs ;
  • Elle a des impacts également dans le modèle des données, l’utilisation des séquences (valeur auto-incrémentée) par exemple.
  • Elle peut faire apparaître des incohérences, dans le cas où deux mises à jour de la même entité sont demandées de part et d’autre.
  • Elle n’est pas transparente pour les applications, qui doivent se préoccuper du bon choix de serveur et du passage en secours ;
  • La tolérance aux pannes de l’étage base de données est imparfaite. On a intégré deux serveurs parce qu’il en fallait deux, mais si l’un tombe en panne, le serveur restant est insuffisant pour satisfaire le besoin. Il faut donc prévoir également un secours tiède de ces serveurs.
  • Et finalement, elle n’est pas extensible à l’infini. Même s’il n’est pas impossible de passer à trois serveurs, les mécanismes et flux de réplication croissent selon le carré du nombre de serveurs.

Serveur dédié à la contribution

Il est assez courant que les mises à jour soient réservées à des internautes identifiés. Dans ce cas, la règle d’affectation est assez simple : les internautes identifiés sont gérés sur la base maître.

Un cas particulier usuel est celui d’un site utilisant un outil de gestion de contenus. On distingue alors des contributeurs et de simples lecteurs. Les contributeurs agissent alors sur la base maître, tandis que les internautes lecteurs accèdent à l’une des bases répliquées.

Dans certains cas, on trouvera même plus simple de dédier un couple frontal / base de données pour les contributions :

image164

Bien sûr, cela pose à nouveau le problème de la tolérance aux pannes, tout particulièrement pour la contribution. Mais on peut admettre parfois que la disponibilité de la contribution est moins critique que celle de la lecture.

A vrai dire, dans le schéma précédent, la flexibilité de l’aiguillage entre frontaux et bases, pour la partie lecture n’est pas fondamentale. Pour le même nombre de serveurs, on pourra envisager de réunir à nouveau étages frontal et base de données.

image166

En rassemblant les étages, on obtient une répartition de ressources transparente entre frontal et SGBD.

L’inconvénient majeur de ce type d’architecture, c’est la prise en compte des « User Generated Content » (UGC) de toutes formes : commentaires, notation, participation à des forums, etc. Les UGC sont une tendance majeure du web moderne, et il n’est plus possible de distinguer aussi clairement contribution et publication.

Partitionnement des données

Enfin, le nec plus ultra de l’extensibilité sera atteint avec le partitionnement des données, que l’on a évoqué déjà. Comme on l’a dit, le partitionnement n’est pas compatible avec toutes les typologies d’application. On l’évoquera davantage dans notre cas d’école suivant, traitant d’une plateforme de blog.