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 ?
- de nginx
- de haproxy
- et de keepalived
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