Serveur dédié OVH : deux IP fail-over sur la même VM KVM

Il y a maintenant quelques mois, j'ai travaillé à la mise en place d'une petite infrastructure haute-disponibilité à deux noeuds pour l'hébergement d'une application web. Tout fonctionnait parfaitement, jusqu'à ce que l'IP principale, d'un seul coup, ne réponde plus... Soupçonnant un problème de routage, nous avons contacté OVH pour savoir s'il se passait quelque chose de particulier de leur côté. Ils ont reconnu un problème ponctuel lors d'une mise à jour d'un routeur, mais ce problème aurait été corrigé et OVH a donc suggéré que le problème venait de la configuration de la VM. La vérité est entre les deux, mais le problème a bien pu être corrigé après quelques modifications de configurtion sur la VM.

Rappel de la configuration en place

  • Deux VMs en haute disponibilité, ayant chacune leur IP (une IP fail-over avec mac virtuelle) configurée sur eth0
  • Les VMs sont de type KVM (hôte Proxmox 2), OS Debian Squeeze (kernel 2.6.32)
  • Une IP-failover supplémentaire, avec sa mac virtuelle, est utilisée pour l'accès à l'application. En cas de défaillance du serveur 1, cette IP est basculée sur le serveur 2, pour la continuité de service. Cette IP principale est configurée sur eth1 sur les deux VM concernées (heartbeat gère lui-même l'activation de l'interface au besoin).

Explication du problème

La raison pour laquelle l'IP fail-over a cessé de fonctionné est lié aux règles de routages. Jusque là, nous n'avons pas mis en place de règle particulièrement exotique : les seules règles de routage mises en place correspondaient à ce qui est préconisé par OVH pour la mise en place d'une IP fail-over avec mac virtuelle, donc simplement une route par défaut vers l'IP du routeur lié au serveur (IP.DU.SERVEUR.254). Cette route par défaut pointait vers eth0 :

ip route

xx.xx.xx.254 dev eth0  scope link
default via xx.xx.xx.254 dev eth0

(xx reprend l'IP principale du serveur, par les IP-failover)

Cette configuration a fonctionné sans problème jusque là pour nous. Sur les forums OVH, certains utilisateurs semblent utiliser la même stratégie avec succès, tandis que d'autres avaient déjà le problème que nous avons rencontré ces derniers jours.

Il semblerait que selon les routeurs (ou selon leur configuration), les règles de routage 'simples' ne suffisent pas. Le problème est assez simple à comprendre : lorsqu'une requête arrive sur eth1 (par exemple un ping), la réponse à cette requête est transmise selon les règles de routage définies sur le système ; en l'absence de règles spécifiques, la réponse sera donc envoyée selon la route par défaut, qui passe par eth0. Pour protéger son réseau, OVH met en place des règles très strictes concernant la cohérence des informations présentes dans le traffic réseau émanant d'une machine. Si une réponse ayant pour IP source une IP différente de celle associée à l'adresse mac de l'interface par laquelle le traffic réseau est émis, le routeur va s'en rendre compte et ainsi bloquer le traffic avec un certain temps (quelques minutes en général). C'est en tout cas notre analyse après les histoires de ces derniers jours.

La solution : règles de routage avancées

La solution au problème est heureusement assez simple. Mais comme elle n'est documentée clairement nulle-part dans la documentation OVH, il nous a fallu un peu de temps !

Principe de la solution : avoir deux routes par défaut. Une route par défaut générale, et une route par défaut pour le traffic dont l'IP source est l'IP-failover, pour forcer ce traffic sur l'interface eth1, sur laquelle l'IP est configurée. Les outils fournis par iproute2 permettent de faire tout cela en quelques commandes très simples, mais ça ne marche pas toujours (voir plus bas). Attention par contre à ne pas faire d'erreur ! Quand on joue avec le routage à distance, on a vite fait de perdre la connectivé. Vous êtes prévenu, alors prévoyez bien une solution de secours (accès console).

1) Ajouter une table de routage personnalisée

echo "2 failover" > /etc/iproute2/rt_tables

2) Ajouter une route par défaut sur cette table de routage

ip route add default via IP.DU.SERVEUR.254 dev eth1 table failover

Attention à bien utiliser la bonne IP de base comme adresse du routeur. Il s'agit de celle du serveur et non de l'IP fail-over. Si vous ne spécifiez pas cette route, votre machine va envoyer sur cette interface des requêtes ARP inutiles (c'est au routeur de s'en occuper), et votre IP va être bloquée au bout de 48h.

Pour moi, la commande ci-dessus n'a pas fonctionné du premier coup. J'obtenais l'erreur suivante :

RTNETLINK answers: No such process

Il m'a fallu encore quelques heures et une exploration des forums Dedibox (pour changer...) avant de me souvenir que la commande 'ip' ne remplaçait pas encore en totalité les anciennes commandes (notamment la commande 'route'). Donc au moins sous Debian stable, la commande 'ip route add ...' spécifiée au-dessus ne fonctionnera sans doute pas dans le cas d'OVH (où les IP sont sur des subnets différents, et où il faut utiliser exactement la même route par défaut pour toutes les IP fail-over).

La solution est là encore très simple. Il faut utiliser la commande 'route' pour ajouter la route par défaut à la table de routage principale du kernel, qui ensuite la reconnaîtra correctement et nous laissera ajouter la route par défaut sur notre table de routage personnalisée.

route add -host IP.DU.SERVEUR.254 dev eth1

route add default gw IP.DU.SERVEUR.254 dev eth1

Maintenant si vous lancez la commande 'route' toute seule pour vérifier, vous devriez avoir quelque chose comme ça :

Table de routage IP du noyau
Destination Passerelle Genmask Indic Metric Ref Use Iface
vss-gw-6k.fr.eu * 255.255.255.255 UH 0 0 0 eth1
vss-gw-6k.fr.eu * 255.255.255.255 UH 0 0 0 eth0
default vss-gw-6k.fr.eu 0.0.0.0 UG 0 0 0 eth1
default vss-gw-6k.fr.eu 0.0.0.0 UG 0 0 0 eth0

 

Vous pouvez maintenant tenter à nouveau l'ajout de la route par défaut sur la table de routage personnalisée :

ip route add default via IP.DU.SERVEUR.254 dev eth1 table failover

 

3) Ajouter une règle pour utiliser la table de routage selon l'adresse source

ip rule add from IP.FAIL.OVER lookup failover prio 1000

Cela indique au kernel d'utiliser la table de routage failover pour les paquets réseaux émanant de l'IP fail-over, et donc d'utiliser la route par défaut définie dans cette table.

 

Un petit 'ip route flush table cache' permet ensuite de vider le cache de route et d'appliquer les modifications immédiatement.

Quelques liens qui m'ont été particulièrement utiles :

- http://guides.ovh.com/printPage/ConfigurerIpSupplementaire

- http://www.rjsystems.nl/en/2100-adv-routing.php

- http://blog.guiguiabloc.fr/index.php/2008/06/14/routage-avec-deux-cartes-reseaux/

- http://forum.ovh.com/archive/index.php/t-74430.html

- http://forum.online.net/index.php?/topic/1052-configurer-2-ip-sur-une-vm/?hl=rt_tables#entry6575