Haute disponibilité et répartition de charge sous FreeBSD part 1. Auteur: aflab@free.fr Site: http://aflab.free.fr Quels que soient les événements il est impensable pour un administrateur de couper ses services. Il n'est pas possible par exemple de couper un service de paiement en ligne si un serveur tombe en panne ou si une opération de maintenance doit être effectuée. Il nous faut aussi être capable d'accueillir un maximum d'utilisateurs et ne pas se laisser surprendre par la moindre montée en charge. La qualité et la disponibilité du service sont d'ailleurs deux points souvent mis en avant dans le cahier des charges pour la réalisation d'une application. Je vais donc vous montrer dans cet article comment effectuer cette tâche avec notre OS adoré: FreeBSD. Il est à noter que j'utilise une version 5.2 tout simplement parce que pf (le firewall d'OpenBSD) n'est supporté que dans la branche 5 de FreeBSD. - Répartition de charge et Haute disponibilité à moindre coût. Afin de pouvoir accueillir un maximum d'utilisateurs sur notre site internet nous allons mettre 4 serveurs web, il va donc falloir que les connexions soient réparties sur chacun des serveurs et tout ça de façon égale. Nous allons donc utiliser une option fort sympathique que IPF et PF mettent à notre disposition, cette option c'est: (roulement de tambours) 'round-robin'. Cette option permet de répartir chaque nouvelle connexion sur un serveur différent ce qui donne par exemple que le premier client ira sur le premier serveur web, le deuxième sur le deuxième serveur web etc... Tout cela dans un roulement régulier ce qui fait que par exemple si nous avons 20 utilisateurs alors chacun de nos 4 serveurs web servira 5 clients. Voyons maintenant comment utiliser cette option: pour IPF à mettre dans votre fichier de configuration de ipnat # vi /etc/ipnat.conf ------------------------------------- SNiP ------------------------------------ rdr 0.0.0.0/0 port 80 -> 192.168.0.1,192.168.0.2 port 80 tcp round-robin rdr 0.0.0.0/0 port 80 -> 192.168.0.3,192.168.0.4 port 80 tcp round-robin ------------------------------------- SNiP ------------------------------------ ou alors ------------------------------------- SNiP ------------------------------------ rdr 0.0.0.0/0 port 80 -> 192.168.0.1 port 80 tcp round-robin rdr 0.0.0.0/0 port 80 -> 192.168.0.2 port 80 tcp round-robin rdr 0.0.0.0/0 port 80 -> 192.168.0.3 port 80 tcp round-robin rdr 0.0.0.0/0 port 80 -> 192.168.0.4 port 80 tcp round-robin ------------------------------------- SNiP ------------------------------------ Il est vivement conseillé d'utiliser la seconde méthode parce que si nous décidons de retirer un serveur de la liste il sera plus évident de le faire avec la seconde syntaxe. Car dans le premier cas nous serons obligés d'enlever 2 serveurs. Donc maintenant pour que la modification soit prise en compte on relance ipnat # ipnat -CF -f /etc/ipnat.conf pour PF. la syntaxe est similaire nous allons juste déclarer deux tables en plus la table web_up et la table web_down pour gérer les serveurs web qui ne répondent plus. ------------------------------------- SNiP ------------------------------------ table persist { 192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4 } table persist { } rdr pass on $ext_if proto tcp from any to $ext_if port 80 -> round-robin ------------------------------------- SNiP ------------------------------------ Nous utilisons l'option persist qui permet de garder la table en mémoire ce qui va nous permettre de la modifier à n'importe quel moment. Pour que les modifications soient prises en compte: # pfctl -f /etc/pf.rules -F nat Nous avons maintenant une répartion opérationnelle mais cela reste très simple en effet si un serveur web tombe nous allons continuer quand même à lui transmettre des clients alors qu'il n'y aura plus de serveur pour répondre ce qui provoquera une perturbation du service. Nous allons donc à l'aide d'un script shell gérer les cas où un ou plusieurs serveurs web ne répondent plus. Ici nous ne faisons qu'un test ping pour vérifier si le serveur web est encore en vie mais on pourrait imaginer un test plus élaboré ou nous irions tester le serveur web et interroger une page pour voir que tout fonctionne correctement. Voilà un exemple de script shell pour IPF, le seul hic c'est que vous allez bien détecter quand le serveur ne répondra plus mais vous ne pourrez pas gérer le retour d'un serveur. ------------------------------------- SNiP ------------------------------------ #!/bin/sh IPNATCFG="/etc/ipnat.conf" IPLIST="192.168.0.1 192.168.0.2 192.168.0.3 192.168.0.4" for IP in `grep round-robin $IPNATCFG |awk '{print $6}'` ;do echo -n "$IP : " ping -c 2 -t 2 $IP > /dev/null 2>&1 if [ $? != 0 ] ; then echo "is down" echo "rdr 0.0.0.0/0 port 80 -> $IP port 80 tcp round-robin" | ipnat -rf - else echo "is up" fi done ------------------------------------- SNiP ------------------------------------ Et un autre exemple pour PF et là c'est magnifique on gère un serveur qui tombe mais aussi un serveur qui revient. (PF c'est plus fort que toi) ------------------------------------- SNiP ------------------------------------ #!/bin/sh PING = "/sbin/ping -c 2 -t 2" if ! WEB_UP=`pfctl -t web_up -Ts`; then echo "la table web_up n'existe pas" exit 1 fi if ! WEB_DOWN=`pfctl -t web_down -Ts`; then echo "la table web_down n'existe pas" exit 1 fi for IP in $WEB_UP; do if $PING $IP >/dev/null 2>&1; then echo "$IP est up" else echo "$IP est down" pfctl -t web_up -Td $IP >/dev/null 2>&1 pfctl -t web_down -Ta $IP >/dev/null >2>&1 fi done for IP in $WEB_DOWN; do if $PING $IP >/dev/null 2>&1; then echo "$IP est maintenant up" pfctl -t web_down -Td $IP >/dev/null >2>&1 pfctl -t web_up -Ta $IP >/dev/null 2>&1 else echo "$IP toujours down" fi done ------------------------------------- SNiP ------------------------------------ Vous n'en croyez pas vos petits yeux incrédules et bien si nous venons de mettre en place une solution de répartition de charge et de haute disponibilité à pas cher capable de concurrencer des produits à très très cher ! Trêve de plaisanterie certains me diront "mais comment on fait pour gérer les sessions d'un utilisateur puisqu'il ne tombe jamais sur le même serveur" et là! Oui j'ai encore une solution pour vous. Il existe une option encore plus merveilleuse mais disponible seulement pour PF (oui bon on ne peut pas tout faire), cette option s'appelle (roulements de tambours encore une fois) 'source-hash'. Cette option permet de renvoyer un utilisateur toujours sur le même serveur ce qui fait que vous n'aurez plus de problème de session puisque l'utilisateur jusqu'à sa déconnexion totale tombera toujours sur le même serveur. La suite au prochain épisode...