Ziirish's Home :: Blog

Ziirish's Pub

 
 

Ouais, je sais, comme d'habitude j'ai des titres vraiment pourris. Mais je suis pas un marketeux, alors on s'en contentera :)

Au programme d'aujourd'hui :

  • du cluster
  • de la ha
  • du load balancing

Bref, du bon et du lourd (comme le welsh-frites).

De quoi avons-nous besoin pour réaliser cette recette ?

Alors bien sûr, pour faire du HA et compagnie, c'est mieux d'avoir plusieurs serveurs. Sauf que 1) j'ai pas les moyens d'avoir 20 serveurs à la maison et 2) j'ai pas la place. Du coup, j'ai utilisé des VM Xen sur mon petit zor0 (on est bien d'accord que là c'était juste pour le PoC, dans la vraie vie c'est totalement débile de foutre ses LB sur des machines virtuelles hébergées sur un même dom0 !)

Avant de se lancer dans la technique, faisons un petit point théorique. Nous avons besoin d'une IP par load-balancer + une IP virtuelle qui basculera d'un lb à l'autre. Il nous faut aussi un "backend" qui sera atteint à travers notre solution de load-balancing.

Or donc il advint que j'utilisais mon dom0 en mode nat. Pour les non initiés, Xen propose 3 "backends" réseau pour les domU : le mode nat qui impose d'avoir des règles iptables de nat ainsi que des routes statiques, le mode route que je n'ai jamais utilisé donc je ne le définirai pas ici, et enfin le mode bridge qui permet de bridger les interfaces des domU à une interface du dom0. Problème, en mode "nat", il m'est impossible d'utiliser une ip virtuelle "flottante" puisque c'est iptables sur le dom0 qui s'occupe d'envoyer les paquets au bon endroit... et donc on perd tout l'intérêt de la bascule automatique.

La solution, c'est d'utiliser le mode bridge qui va lui nous permettre de basculer l'ip d'un domU à l'autre de manière automatique. Mais forcément, ce n'est pas si simple. Mon dom0 est en "production", et changer de backend réseau implique de tout casser et tout refaire... flemme ! J'ai donc salement modifié les scripts que Xen utilise pour créer les domU afin d'avoir un mode "hybride" me permettant d'attacher une interface utilisant le backend nat et une seconde interface en mode bridge.

J'ai donc modifié mon fichier /etc/xen/scripts/vif-nat pour y ajouter ceci en début de script :


dir=$(dirname "$0")
if grep -e "/1$" <<<$XENBUS_PATH 2>&1 >/dev/null
then
    $dir/vif-bridge $@
    exit $?
fi

. "$dir/vif-common.sh"

Ensuite on édite notre fichier de configuration de domU pour avoir quelque chose comme :


kernel = '/kernel/vmlinuz-2.6.32-5-amd64'
ramdisk= '/kernel/initrd.img-2.6.32-5-amd64'
vcpus = '1'
memory = '128'
name = 'lb1'
vif = [ 'ip=10.0.0.6','ip=172.17.0.6,bridge=br0' ]
disk = [
'phy:/dev/vg01/lb1,xvda1,w',
'phy:/dev/vg01/swap_lb1,xvda2,w'
]
root = '/dev/xvda1 ro'
console = 'hvc0'

On crée notre bridge sur dom0 en ajoutant ceci à notre /etc/network/interfaces :


# Create the backend Xen bridge
auto br0
iface br0 inet static
    # bridge_ports tells ifupdown that this is a bridge
    bridge_ports none
    # assign address for dom0 to use on the virtual net
    address 172.17.0.254
    netmask 255.255.255.0

Puis on monte notre bridge :


% ifconfig br0 up

Enfin on configure correctement notre domU (cf. cet article) :


% cat /etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 10.0.0.6
gateway 10.0.0.254
netmask 255.255.255.0

auto eth1
iface eth1 inet static
address 172.17.0.6
netmask 255.255.255.0

Et voilà comment monter un domU avec deux NICs sur deux sous-réseaux différents afin de se mettre dans une situation presque réaliste. On a donc besoin de deux machines de la sorte, et on rattachera notre ip virtuelle à l'interface eth1 de ces machines.

Voici donc la configuration de keepalived sur le MASTER :


vrrp_script chk_haproxy {           # Requires keepalived-1.1.13
        script "killall -0 haproxy"     # cheaper than pidof
        interval 2                      # check every 2 seconds
        weight 2                        # add 2 points of prio if OK
}

vrrp_instance VI_1 {
        interface eth1
        state MASTER
        virtual_router_id 51
        priority 101                    # 101 on master, 100 on backup
        virtual_ipaddress {
                172.17.0.5              # virtual IP
        }
        track_script {
                chk_haproxy
        }
}

Le BACKUP :


vrrp_script chk_haproxy {           # Requires keepalived-1.1.13
        script "killall -0 haproxy"     # cheaper than pidof
        interval 2                      # check every 2 seconds
        weight 2                        # add 2 points of prio if OK
}

vrrp_instance VI_1 {
        interface eth1
        state BACKUP
        virtual_router_id 51
        priority 100                    # 101 on master, 100 on backup
        virtual_ipaddress {
                172.17.0.5              # virtual IP
        }
        track_script {
                chk_haproxy
        }
}

Il est nécessaire d'effectuer certains changements des paramètres du noyau pour que keepalived fonctionne correctement. Pour ce faire, il faut ajouter (ou modifier) la ligne suivante dans le fichier /etc/sysctl.conf


net.ipv4.ip_nonlocal_bind=1

Puis exécuter la ligne suivante pour que la modification soit effectuée à chaud :


% sysctl -p

Il est également nécessaire de charger le module ip_vs puisque cette version de keepalived utilise des options deprecated sur debian squeeze :


% modprobe ip_vs

Pour que la modification soit prise en compte à chaque redémarrage, il faut ajouter la ligne suivante dans le fichier /etc/modules


ip_vs

Nous passons maintenant à la configuration de haproxy :


defaults
        log global
        option dontlognull
        balance leastconn
        clitimeout 60000
        srvtimeout 60000
        contimeout 5000
        retries 3
        option redispatch
 
listen stats 10.0.0.6:80 # ou bien 10.0.0.7 pour le second serveur
        mode http
        stats enable
        stats uri /stats
        stats realm HAProxy Statistics
        stats auth utilisateur:motdepasse
 
listen http 172.17.0.5:80
        mode tcp
        balance source
        maxconn 10000
        server web01 10.0.0.6:8080 maxconn 5000 check
        server web02 10.0.0.7:8080 maxconn 5000 check
 
 listen https 172.17.0.5:443
        mode tcp
        balance roundrobin
        maxconn 10000 
        server web01 10.0.0.6:4443 maxconn 5000 check
        server web02 10.0.0.7:4443 maxconn 5000 check

Et enfin, on configure notre nginx :


% cat /etc/nginx/sites-enabled/blog
server {
        listen 10.0.0.6:8080;
        server_name lb.domain.tld;

        location / {
                index index.php;

                proxy_pass http://blog.ziirish.info;
        }
}

# HTTPS server
#
server {
    listen      10.0.0.6:4443;
    server_name  lb.domain.tld;

    ssl  on;
    ssl_certificate  /keys/cert.crt;
    ssl_certificate_key  /keys/cert.key;

    ssl_session_timeout  5m;

    location / {
        index index.php;

        proxy_pass http://blog.ziirish.info;
    }
}

On remplacera 10.0.0.6 par 10.0.0.7 sur le second serveur. lb.domain.tld étant un enregistrement DNS pointant sur l'ip virtuelle.

Note : pour haproxy, on pensera à l'activé en éditant le fichier /etc/default/haproxy :


% cat /etc/default/haproxy 
# Set ENABLED to 1 if you want the init script to start haproxy.
ENABLED=1
# Add extra flags here.
#EXTRAOPTS="-de -m 16"

On démarre ensuite tous nos services, et c'est parti pour les tests !

Voici un petit exemple, pour le reste je vous invite à jouer vous-même :)


lb1% ifdown eth1

lb2% tail -3 /var/log/messages
Nov  9 20:08:18 lvs2 Keepalived_vrrp: VRRP_Instance(VI_1) Entering BACKUP STATE
Nov  9 20:12:32 lvs2 Keepalived_vrrp: VRRP_Instance(VI_1) Transition to MASTER STATE
Nov  9 20:12:33 lvs2 Keepalived_vrrp: VRRP_Instance(VI_1) Entering MASTER STATE

lb1% tail -3 /var/log/messages
Nov  9 20:12:30 lvs1 Keepalived_vrrp: Kernel is reporting: interface eth1 DOWN
Nov  9 20:12:30 lvs1 Keepalived_vrrp: VRRP_Instance(VI_1) Entering FAULT STATE
Nov  9 20:12:30 lvs1 Keepalived_vrrp: VRRP_Instance(VI_1) Now in FAULT state