La haute disponiblité et la répartition de charge avec HeartBeat/IPVS
( 30 Jun 2009 )Intérêt et problématique
Commençons par la problématique : vous avez besoin pour une application de haute disponibilité et/ou de répartition de charge. Si votre application supporte le fait au les clients arrivent sur tel ou tel serveur (puis restent connectes au même serveur) alors vous pouvez utiliser un système automatiques de répartition des utilisateurs. Là, le choix est vaste.
Déjà, si l'application possède un tel répartiteur en frontal, il faut l'utiliser. Sinon on peux utiliser des switchs dédiés si vous avez un budget important et surtout des besoins de débits très important (de l'ordre de plusieurs dizaines de Mo/s). En cas de débits moindres, il est possible d'utiliser des solutions logicielles open sources que nous allons étudier ici.
Fonctionnement
Des solutions logicielles existent, on va en voir : IPVS, le module du noyau Linux dédié a cette tache. La qualité de la pile réseau de Linux n'est plus à démonter, et ce module l'illustre parfaitement. Associé au programme HeartBeat, il permet de bâtir une solution de haute disponibilité et répartition de charge fiable. Ce premier post présente le fonctionnement global. D'autres posts suivront pour présenter la mise en place effective.
Deux frontaux se partagent (en fail over) une VIP grâce à l'application HeartBeat. Le client va s'adresser à ces frontaux (un seul à la fois, ils sont en fail over). Le frontal maitre va alors décider suivant une table de sessions basées sur les IP sources et du nombre de connexions actives vers quel serveur réels il va adresser la requête. Le second frontal reçoit régulièrement les nouvelles sessions de l'actif et met à jour sa propre table de session. De cette manière, s'il doit passer actif, les connexions ne seront pas perdues.
Les règles de répartitions sont les grandes classiques (round robin, serveur le moins chargé en terme de connexions, etc). Le frontal « transfère » la requête, et là deux solutions sont possibles :
- la requête est envoyée vers le serveur, puis revient au frontal pour repartir au client. Du Nat en gros. Donc le frontal se prend toute la charge réseau. C'est moyen. On verra un peu plus loin que suivant l'application, ceci peux être loin d'être optimal.
- la requête est envoyée vers le serveur qui a une interface réseau ayant comme ip la VIP. Le serveur étant poli, il répond en tant que la VIP, directement au client, sans passer par la case frontal. C'est le mode direct. Pour ceux qui ont suivis, OUI, il y aura un problème ARP car plusieurs machines auront la VIP sur le réseau. Mais ce problème à une (plusieurs en fait…) solution (acceptable) que ce soit sur un serveur Windows ou Linux. On les verra par la suite.
Fonctionnement NAT
Voici un schéma simplifié du mode NAT :
Ici les frontaux vont se comporter comme n'importe quel routeur qui fait du NAT. Le principal intérêt est qu'on redirige vers une IP. Les serveurs réels peuvent donc se situer n'importe ou. Une seule restriction cependant : pour que le client voie la réponse provenir le la même IP que celle a laquelle il a parle, il faut que le retour passe par le frontal. Il faut donc que le serveur réel ait comme gateway par défaut le frontal.
Fonctionnement direct
Voici un schéma simplifié du mode direct:
Ça a l'air trivial comme solution, mais il y a un petit soucis : le client doit voir revenir une réponse provenant de la même IP. Vu que la réponse provient du serveur réel, il doit avoir l'IP du service. Mais alors tous les serveurs réels doivent avoir l'IP, cela peux poser des problèmes. En gros, le protocole ARP va poser des petits soucis. Si plusieurs machines ont la même IP sur un réseau, le protocole ARP ne va pas être content : lorsqu'un client va demander à qui est l'IP grâce à une requête ARP, plusieurs résultats vont arriver et le client va être complètement perdu! Il faut qu'une seule machine réponde, celle qui fait l'équilibrage de charge, le frontal.
La solution est simple : pour envoyer la requête au serveur réel, le frontal va changer l'adresse MAC destination du paquet par l'adresse MAC du serveur reel. Oui oui, on joue au niveau 2, niveau MAC. Il faut donc que les frontaux aient une patte sur le même sous-réseau que les serveurs réels.
Voyons donc une version complète du fonctionnement:
On voit ici que le point principal dans le fonctionnement en mode direct se situe en fait sur les serveurs appli, les vrais serveurs. Il faut monter un alias d'interface avec une IP égale à la VIP qui ne répond pas aux requêtes ARP (point 2 bis du schéma). Ainsi seul le frontal répond aux requêtes ARP. Ceci a été testé avec succès sur des Linux (règle arptables ou configuration du noyau) et sur un Windows 2k (alias sur interface de loopback qui n'a pas le droit de faire de l'ARP :) ).
Le travail au niveau du frontal est on ne peut plus simple et élégant :
- vu qu'il est le seul à répondre à l'ARP sur la VIP, il reçoit le paquet du client
- il regarde dedans (niveau IP/TCP) pour voir s'il n'a pas déjà dans sa table des sessions une session correspondante à l'ip du client (pas de dissection du paquet au niveau 7 à ma connaissance dans IPVS, pour un cookie par exemple)
- il modifie l'adresse de destination MAC (niveau Ethernet donc) pour mettre l'adresse du vrai serveur qui va devoir répondre, et le rebalance sur le réseau. On voit ici qu'il faut que les frontaux et les serveurs réels soient sur le même sous-réseau. Peut importe le nombre de switchs entre, le temps qu'ils sont sur le même sous-réseau, c'est gagné. Il ne modifie pas le paquet IP (donc TTL inchangé, pas de modification couteuse des checksums).
- Le paquet arrive donc au serveur, qui l'attrape (normal, c'est son adresse MAC), en tant que VIP (normal, le client parlait à la VIP dans le paquet IP), le serveur est bien élevé, il traite puis répond en tant que VIP à l'IP du client. Il faut bien sur que l'application écoute sur l'interface de la VIP, mais ce n'est généralement pas un problème.
- Pour le client, il a parlé à la VIP avec une adresse MAC A, la VIP lui répond avec une adresse MAC B, mais il s'en fiche, la couche IP est bonne (numéro de séquence OK), TCP/UDP pareil (numéro de séquence ok). C'est comme si la réponse provenait d'un autre chemin que celui par lequel il est parti, mais ce n'est pas un problème. Les seules modifications se font donc au niveau Ethernet. Merci la séparation des couches.
En résumé
Les plus
- Ceci permet d'avoir de la haute dispo + loadbalancing
- le frontal ne se prends que les paquets envoyés vers les serveurs. Dans certain cas, ceci permet de diviser la charge réseau. Si on prend le Web par exemple : le client envoie un GET ou un POST de taille raisonnable, et se prend comme réponse les pages wWeb et les images. Le flux qui passe par les frontaux sont donc les requêtes du client, les gros retour (pages et images) passent donc directement du serveur au client, sans repasser par la case frontal. D'expérience, dans ce cas, le frontal ne se prend environ que 10% du flux total applicatif. Ceci permet donc d'augmenter sensiblement la charge que peux supporter cette solution
- Les serveurs réels n'ont besoin que d'une modification mineure (leur IP « normale » est toujours accessible bien sûr)
- la persistance de session se base sur les IP sources, les couches supérieures ne sont pas affectées
- C'est supporté par RedHat (fourni dans RH5, mais utilisable dans la 4)
- Ça fonctionne très bien sur des VM pour les frontaux, et VM ou réels pour les serveurs d'applis
Les moins
- Il faut que les serveurs réels et les frontaux soient sur le même réseau, si on imagine un firewall entre eux, il le faut en mode bridge, mais a priori si firewall il y a, il devrait être placé avant le frontal
- Même mineure, la modification des serveurs réels existe
Les performances
Cette solution est en place sur notre serveur de production (un gros ERP pour plus de 300 utilisateurs). Et ce avec une VM sur une seule carte réseau, et un système non taillé pour faire office de « routeur » (taille des stacks réseaux et cie). Les tests de performances n'ont pas réussi à faire frémir les load balanceurs : les applications sont tombées avant!
NOTE 2021: vu qu'il ya prescription je peux l'avouer: j'ai déjà fait tombé un serveur de qualif (pas la prod faut pas être fou) lors d'un test de perf. La petit VM avec
LVS a tenu sans problème, alors que le gros serveur apache derrière s'est éffondré ^^
La réaction face à la perte d'un noeud
On a vu qu'il y a deux frontaux. Ils sont en actif-passif. Régulièrement, le frontal maître synchronise sa table de session avec le second noeud. En cas de non réponse du premier nœud, l'esclave prends la main sur la VIP et peux loadbalancer sans perdre de sessions. A ce niveau aucun problème donc.
Les serveurs applicatifs sont ce qu'ils sont : peu fiables. Il faut donc que le dispositif puisse détecter les pertes de service. IPVS ne permet pas cela nativement. Il ne s'occupe que de la répartition. Pour cela on utilise le logiciel ''ldirector''. Il va surveiller les services qu'on loadbalance, si un service tombe, il va le retirer et faire pointer les clients du service tombe sur les autres serveurs encore en vie. La méthode de vérification de la disponibilité du service est libre, mais généralement on regardera si le port est ouvert tout simplement.
Dans un prochain post, nous verrons la mise en place effective de cette solution avec la méthode directe qui est la plus recommandée.