Loadbalancer hautement disponible avec HAProxy et Keepalived

Introduction

Cet article est une version actualisée de ce que j’avais publié dans GNU/Linux France Magazine numéro 163.

Voici ce que l’on souhaite obtenir :

  • Un service utilisateur hautement disponible avec des serveurs de type Postfix, Apache, Zimbra, Dovecot, etc…
  • Les serveurs de ces backends devront être en mode actif/actif afin de pouvoir fournir un système apte à monter en charge
  • La haute disponibilité sera gérée par des load balancer qui ne devront pas être eux mêmes un SPOF, ils seront donc également load balancés
  • Et pour être en phase avec la législation, les backends devront avoir en visibilité les adresses IP des clients et ce contrairement à un certain nombre d’architectures où le load balancer effectue du NAT et donc masque l’IP source.

Ces fonctionnalités sont disponibles via KeepAlived pour la HA du load balancer et HAProxy pour la HA des backends. Le mode transparent est lui accessible depuis le récent module kernel TPROXY dsponible sous Ubuntu depuis la release LTS 16.04.

Architecture

Un impératif dans cette architecture c’est que les backends doivent être dans le même sous réseau que les load balancer. En effet, les serveurs load balancés auront comme passerelle la VIP des load balancer et non pas le firewall. ha-ka-haproxy-tproxy

 

En pratique je vais avoir :

  • www1 : Apache / Ubuntu 14.04, 192.169.69.106
  • www2 : Apache / Ubuntu 14.04, 192.169.69.107
  • lb1 : Apache / Ubuntu 16.04, 192.169.69.111
  • lb2 : Apache / Ubuntu 16.04, 192.169.69.112
  • La VIP 192.168.69.110

iMPORTANT, si vous devez tester depuis une machine du même sous-réseau, il y a une astuce! En effet, les paquets vont arriver aux serveurs load balancés via la VIP mais l’IP source étant sur le même subnet, la réponse dans ce cas se fera sans ressortir par le load balancer. Dans mon cas, ma machine a comme IP 192.168.69.104. Du coup, sur chaque serveur load balancés, il est nécessaire d’ajouter une règle comme suit :

ip route add 192.168.69.104/32 via 192.168.69.110

 

Haute disponibilité du load balancer

On commence sur les deux load balancer à installer KeepAlived qui fournira les fonctionnalités de cluster VRRP :

apt-get install keepalived haproxy hatop
systemctl enable keepalived.service
systemctl enable haproxy.service
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf

Il nous faut configurer notre cluster VRRP dans le fichier /etc/keepalived/keepalived.conf. Attention au nom de l’interface réseau dans ce fichier.

Sur le master :

vrrp_script reload_haproxy {
        script "killall -0 haproxy"
        interval 1
}

vrrp_instance VI_1 {
   virtual_router_id 100
   state MASTER
   priority 100
   # Check inter-load balancer toutes les 1 secondes
   advert_int 1
   # Synchro de l'état des connexions entre les LB sur l'interface enp0s3
   lvs_sync_daemon_interface enp0s3
   interface enp0s3
   # Authentification mutuelle entre les LB, identique sur les deux membres
   authentication {
        auth_type PASS
        auth_pass secret
   }
   # Interface réseau commune aux deux LB
   virtual_ipaddress {
        192.168.69.110/32 brd 192.168.69.255 scope global
   }

   track_script { 
       reload_haproxy 
   }

}

Sur le Slave, c’est à peu près le même fichier :

vrrp_script reload_haproxy { 
        script "killall -0 haproxy"
        interval 1
} 

vrrp_instance VI_1 { 
   virtual_router_id 100 
   state BACKUP 
   priority 100 
   # Check inter-load balancer toutes les 1 secondes 
   advert_int 1 
   # Synchro de l'état des connexions entre les LB sur l'interface enp0s3 
   lvs_sync_daemon_interface enp0s3 
   interface enp0s3 
   # Authentification mutuelle entre les LB, identique sur les deux membres 
   authentication { 
        auth_type PASS 
        auth_pass secret 
   } 
   # Interface réseau commune aux deux LB 
   virtual_ipaddress { 
        192.168.69.110/32 brd 192.168.69.255 scope global 
   }

   track_script { 
       reload_haproxy 
   } 

}

Le premier des deux load balancer va récupérer l’adresse de la VIP :

root@lb1:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:68:44:b0 brd ff:ff:ff:ff:ff:ff
    inet 192.168.69.111/24 brd 192.168.69.255 scope global enp0s3
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe68:44b0/64 scope link 
       valid_lft forever preferred_lft forever

Le redémarrage du service keepalived ou le reboot du serveur permettant de tester que la VIP bascule bien sur lb2 :

Sep 18 21:53:00 lb2 Keepalived_vrrp[18740]: VRRP_Instance(VI_1) Transition to MASTER STATE
Sep 18 21:53:01 lb2 Keepalived_vrrp[18740]: VRRP_Instance(VI_1) Entering MASTER STATE

Les serveurs load balancés

Seule configuration, définir la VIP comme passerelle par défaut!

HAProxy

Contrairement à mon article dans GLMF 163, ici c’est le célèbre HAProxy qui est utilisé pour réaliser le load balancing. HAProxy dispose de deux modes, http ce qui lui permet de traiter et de manipuler finement ce protocole et gère également l’offload SSL. Le second mode est le mode TCP qui permet ainsi de load balancer n’importe quel protocole de niveau supérieur basé sur TCP qui a ma préférence.

Premièrement, il faut faire un peu d’iptables avec que haproxy puisse identifier les paquets rattachés à une socket non locale ce qui est parfaitement documenté dans les sources du kernel.

Sur chaque load balancer on fait donc ceci (qui peut être placé dans le fichier /etc/rc.local ou un script d’init dédié) :

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT

ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

Enfin, on paramètre haproxy avec le même fichier /etc/haproxy/haproxy.cfg sur les deux load balancer. Seul défaut du mode transparent, c’est que haproxy doit tourner en root :

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        user root
        group root
        daemon


defaults
        log     global
        mode    tcp
        timeout connect 5000
        timeout client  50000
        timeout server  50000

frontend ft_http
        bind :80 transparent
        mode tcp
        default_backend bk_http

backend bk_http
        mode tcp
        balance leastconn
        stick store-request src
        stick-table type ip size 200k expire 30m
        source 0.0.0.0 usesrc clientip
        server s1 192.168.69.106:80
        server s1 192.168.69.107:80

frontend ft_https
        bind :443 transparent
        mode tcp
        default_backend bk_https

backend bk_https
        mode tcp
        balance leastconn
        stick store-request src
        stick-table type ip size 200k expire 30m
        source 0.0.0.0 usesrc clientip
        server s1 192.168.69.106:443
        server s1 192.168.69.107:443

Il ne reste plus qu’à redémarrer le service haproxy pour prise en compte, et voila!

 

Vus : 1489
Publié par Morot : 34