Laboratoire infra Linux HA #5 – Load balancing avec HAProxy et Keepalived
lab07-front-02, lab07-lb-01 et lab07-lb-02
Cet article est le cinquième opus de la suite d’articles sur la mise en œuvre d’une infrastructure Linux visant à servir le blog « lab07.lab » en haute dispo.
Dans le précédent article, nous avons enfin mis en œuvre de la haute-disponibilité pour le stockage du code de notre blog.
Dans cet article on va d’abord ajouter un nouveau serveur FRONT et comme ça devrait aller vite, on ajoutera deux rĂ©partiteurs de charge (load balancers ou encore LB) Ă l’aide de HAProxy
qui partageront une VIP gérée par Keepalived
.
HAProxy
va nous permettre de faire de jolies choses tu vas voir : il va bien entendu rĂ©partir les requĂŞtes HTTP entre les serveurs ce qui sera dĂ©jĂ pratique mais, il va Ă©galement nous permettre de rediriger le trafic sur un seul nĹ“ud si l’autre est en maintenance afin d’Ă©viter que de nombreux visiteurs (krrkrrkrr) de « lab07.lab » ne reçoivent un timeout.
On s’y met ?
Ajouter un nouveau serveur FRONT lab07-front-02
DĂ©sormais tu devrais savoir comment prĂ©parer la machine sinon tu relis ce qu’on a fait dans les articles prĂ©cĂ©dents ou bien tu vĂ©rifie avec history
les commandes sur les autres machines, quand c’est bon tu reviens sur l’article.
On va installer les outils nĂ©cessaires Ă l’usage du dĂ©pĂ´t Sury puis installer Apache, PHP8.3 et son module de connexion MySQL.
root@lab07-front-02:~# apt install curl lsb-release ca-certificates gnupg2
root@lab07-front-02:~# sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
root@lab07-front-02:~# curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
root@lab07-front-02:~# dpkg -i /tmp/debsuryorg-archive-keyring.deb
root@lab07-front-02:~# apt install apache2 php8.3 php8.3-mysql
root@lab07-front-02:~# a2enmod php8.3
root@lab07-front-02:~# systemctl restart apache2
# Bon PHP8.3 était déjà autorisé mais ça mange pas de pain
On va installer nfs-common
, paramétrer la connexion à la VIP de nos serveurs NFS en recopiant la dernière ligne du fstab
de lab07-front-01
(parce que je sais que c’est ma dernière ligne, fais pas n’imp non plus) et lancer la connexion .
root@lab07-front-02:~# apt install nfs-common
root@lab07-front-02:~# ssh lab07-front-01 'tail -n1 /etc/fstab' >> /etc/fstab
root@lab07-front-01's password:
root@lab07-front-02:~# mount /var/www
lab07-front-02
a dĂ©sormais un Apache d’installĂ©, capable de servir du PHP et accès au code. Il ne reste plus qu’Ă paramĂ©trer et activer son vhost. Il va ĂŞtre identique Ă celui de lab07-front-01
alors pourquoi se casser la tĂŞte ? On va le dupliquer depuis le premier FRONT.
root@lab07-front-02:~# scp lab07-front-01:/etc/apache2/sites-available/lab07.lab.conf /etc/apache2/sites-available/
root@lab07-front-01's password:
lab07.lab.conf 100% 294 148.7KB/s 00:00
root@lab07-front-02:~# a2ensite lab07.lab
Enabling site lab07.lab.
To activate the new configuration, you need to run:
systemctl reload apache2
root@lab07-front-02:~# systemctl reload apache2
On va modifier le fichier /etc/hosts
de notre machine locale et dĂ©placer “lab07.lab” sur lab07-front-02
puis on tentera de surfer sur http://lab07.lab. On s’assurera de bien surfer sur le bon serveur – ce serait vraiment balo que tu sois en train de surfer sur lab07-front-01
nan ?
Encore une bonne excuse bidon pour prĂ©senter l’usage d’un outil basique. Tu vas faire un tail -f /var/log/apache2/access_lab07.lab_log
, appuyer sur Entrée
quelques fois pour bien différencier ce qui est déjà présent dans le journal et ce qui va venir. Ensuite tu ouvres http://lab07.lab
.
On a bien deux serveurs FRONT capable de servir notre site mais que ce soit modifier notre /etc/hosts
, une zone DNS ou encore, une ouverture de port, ça ne sera pas super amusant et encore moins automatique. Pour cette raison notamment, on va mettre des load balancers en amont de nos serveurs FRONT.
Les reverse proxy
Tu commences à connaître le job, on va préparer directement les deux load balancers lab07-lb-01
et lab07-lb-0
2. Une fois prĂŞtes, on va pouvoir installer HAProxy sur les deux et le configurer.
Sur le principe pour notre usage HAProxy est simple, on lui donne une VIP
en frontend
et nos serveurs FRONT en backend
.
root@lab07-lb-01:~# apt install haproxy
Le fichier de configuration vient en partie remplie. Mais surtout, le manuel de HAProxy, man haproxy
nous informe qu’une documentation de configuration a Ă©tĂ© dĂ©posĂ©e dans /usr/share/doc/haproxy/configuration.txt.gz
.
Options par défaut
On va déjà modifier un peu les contraintes temporelles timeout
pour commencer.
root@lab07-lb-01:~# vi /etc/haproxy/haproxy.cfg
#[...]
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 50s
timeout server 100s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
Nos serveurs LB ne vont gérer que du HTTP, on aurai pu paramétrer http
directement en face de log
.
Les timeout
sont très hauts, il faudra les abaisser petit Ă petit. Perso je viens de perdre plus d’1h30 parce que j’avais paramĂ©trĂ© 1s/2s/2s et j’ai cherchĂ© dans tous les sens (le vhost d’Apache, les droits sur les fichiers de WordPress sur le NFS, ajout du no_root_squash) avant de capter que c’Ă©tait trop bas.
L’interface d’administration
A la suite, dans le mĂŞme fichier de configuration, on va configurer l’interface web de HAProxy stats pour pouvoir gĂ©rer nos serveur FRONT en ajoutant ce paragraphe.
#[...]
listen stats
bind 0.0.0.0:12345 # on Ă©coute sur toutes les IP de l'hote sur le port 12345
mode http # en HTTP
maxconn 10 # tolérer 10 clients ce sera déjà pas mal
stats enable # on autorise
stats refresh 20s # avec un rafraîchissement de 20s
stats show-legends # pour rendre la page de stats plus bavarde
stats show-node # afficher les nodes
stats hide-version # pour rendre la page de stats moins bavarde
stats uri / # sur quelle adresse sera servie notre page de stats
acl is_auth http_auth(stats) # et puis un peu de sécurité et des contraintes temporelles...
acl is_admin http_auth_group(stats) stats-admin
stats http-request auth unless is_auth
stats admin if is_admin
timeout client 100s
timeout server 100s
timeout connect 100s
timeout queue 100s
L’administrateur
T’as vu on a parlĂ© d’admin, d’is_admin
sans que je ne dĂ©taille. Tu te demandes comment on va accĂ©der ou comment dĂ©finir qu’on est admin ? On va ajouter un nouveau paragraphe au fichier de configuration pour le dĂ©finir.
#[...]
userlist stats
user haproxy insecure-password haproxy groups stats-admin # la 2nde occurence de haproxy sur cette ligne représente le mot de passe, déconne pas ne laisse pas comme ça
group stats-admin
De mĂŞme, la documentation en ligne de HAProxy est très claire pour l’usage d’un hash plutĂ´t qu’une chaĂ®ne. Bon tu vas pas recommencer Ă me casser les pieds non ? Puisque je te dis que c’est un lab, on peut laisser une chaĂ®ne Ă la con!
Mais c’est pas vrai, faut toujours que t’insiste…
root@lab07-lb-01:~# apt install pwgen whois
root@lab07-lb-01:~# pwgen -sBc 14 1
XbgdvdecR4FtTc
root@lab07-lb-01:~# mkpasswd -m sha-256 XbgdvdecR4FtTc
$5$J1SG0OvQzflKD.H/$UxRuSEbQiaS3vcg6ivqGk4ZVe7R0VoPDO0uv4CaIPV.
root@lab07-lb-01:~# mkpasswd -m sha-256 haproxy
$5$n4v2tmh4SG2foaBg$4GiE28c4h0zwdnn1YTxE5U7eu2wdib/E6CBqLeXthkD
Depuis le temps que tu lis, j’espère que tu as compris d’utiliser tes mots de passe perso ou, de lancer toi-mĂŞme la commande
pwgen
(avec la paramètres qui te conviennent d’ailleurs) et que tu rĂ©utilise pas les mots de passe que je gĂ©nère dans mon lab pour ta prod ?!
On remplace alors le paragraphe précédent par le suivant.
#[...]
userlist stats
user haproxy password $5$n4v2tmh4SG2foaBg$4GiE28c4h0zwdnn1YTxE5U7eu2wdib/E6CBqLeXthkD groups stats-admin
group stats-admin
Ça va t’es heureux lĂ ? T’façon c’est pas mon problème, je vais garder l’embrouillamini de haproxy moi.
La VIP et les FRONT
Je t’avoue qu’on vient de finir la partie la moins passionnante de la configuration de HAProxy
. On peut enfin s’attaquer Ă ce qui nous intĂ©resse le plus : la rĂ©partition de charge et la tolĂ©rance de panne de nos serveurs FRONT Ă l’aide de l’adresse IP virtuelle.
#[...]
frontend lab07
bind 192.168.50.30:80 # Donner l'IP virtuelle du keepalived sinon "Cannot assign requested address"
mode http # On paramètre le proxy en http (couche 7 du modèle OSI, Application) pour inspecter les messages HTTP (traduction éhontée de la doc officielle, tu vas faire quoi ?)
default_backend frontservers # Vers qui on redirige ?
option httplog # Journalisation des requĂŞtes HTTP, des sessions et timers (c'est quoi un timer ?)
backend frontservers
balance roundrobin
option httpchk # Les backends devront retourner un code HTTP 2xx ou 3xx
http-check connect # Ouvre une nouvelle connexion pour vérifier l'état de santé HTTP
mode http
server lab07-front-01 192.168.50.23:80 check
server lab07-front-02 192.168.50.24:80 check
Tu peux dupliquer le fichier haproxy.cfg de lab07-lb-0
1 vers lab07-lb-02
via scp
, rsync
ou comme moi, Ă la pogne (clic molette for the win).
On va quand mĂŞme demander Ă HAProxy
ce qu’il pense de tout ça.
root@lab07-lb-01:~# haproxy -c -f /etc/haproxy/haproxy.cfg
Configuration file is valid
La gestion de l’IP virtuelle par Keepalived
Dans notre configuration d’HAProxy
on a renseignĂ© la VIP sur laquelle il va rĂ©pondre mais elle n’est paramĂ©trĂ©e nulle part. Ce n’est pas le taf d’HAProxy
d’attribuer la VIP Ă l’un ou l’autre des serveurs LB ; c’est le travail de Keepalived
.
Par dĂ©faut, l’OS ne laissera pas Keepalived
attribuer la VIP parce qu’elle n’existe pas dans la configuration des interfaces. On n’a pas le droit ? On prend le gauche ! Pour la session en cours et de manière persistante.
root@lab07-lb-01:~# sysctl --write net.ipv4.ip_nonlocal_bind=1
root@lab07-lb-01:~# vi /etc/sysctl.conf
#[...]
net.ipv4.ip_nonlocal_bind=1
Sur le premier serveur, on a utiliser l’approche de modifier le fonctionnement du système et de reporter la configuration dans le fichier de configuration par dĂ©faut. On peut tout Ă fait fonctionner de manière inverse, amender le fichier de configuration puis recharger le fichier de configuration.
root@lab07-lb-02:~# vi /etc/sysctl.conf
#[...]
net.ipv4.ip_nonlocal_bind=1
root@lab07-lb-02:~# sysctl -p
net.ipv4.ip_nonlocal_bind = 1
J’ai pas spĂ©cifiĂ© le fichier de configuration Ă utiliser parce que je suis fainĂ©ant mais sur un système en production j’aurai peut-ĂŞtre pas fait le malin et j’aurai ajoutĂ© le chemin sysctl -p /etc/sysctl.conf
.
Quelle que soit ta méthode préférée, fais le sur les deux serveurs LB et on est prêt pour installer Keepalived
.
root@lab07-lb-01:~# apt install keepalived
Toi tu sais pas parce que tu vois que le travail (Ă peu près) abouti mais, moi je relis les notes que j’avais prises lorsque j’ai fait le laboratoire la première fois et je comprenais pas pourquoi j’avais conservĂ© aucune note de la configuration de Keepalived
.
OuĂ© je sais je perds un peu de ma superbe quand je t’Ă©cris que je lis mes notes, mais je suis pas un barbu ; je suis probablement Ă peine au dessus de la poule qui a trouvĂ© un couteau.
Une fois le paquet installĂ©, lĂ j’ai compris pourquoi j’avais pas de notes. Parce que Keepalived
est livrĂ© avec un magnifique fichier d’exemple : /etc/keepalived/keepalived.conf.sample
!
root@lab07-lb-01:~# cp /etc/keepalived/keepalived.conf.sample /etc/keepalived/keepalived.conf
root@lab07-lb-01:~# vi /etc/keepalived/keepalived.conf
Bon je te confie que je vais pas juste Ă©purer et adapter le fichier de configuration mais je vais le commenter en paraphrasant la documentation ; comme d’hab quoi.
! Configuration File for keepalived
global_defs {
vrrp_no_swap # Le processus VRRP enfant reste en RAM, pas de swap
vrrp_garp_master_delay 10 # Délais en secondes de grace ARP après une transition en MASTER
vrrp_garp_master_refresh 60 # DĂ©lais minimum de rafraichissement de la grace ARP tant que le noeud est MASTER
vrrp_garp_interval 0.001 # Délais entre les messages ARP gratuits envoyés a une interface
}
vrrp_instance lab07-lb-vip { # Nom de l'instance VRRP
state MASTER # Etat par défaut
interface enp0s3 # Interface sur laquelle repose la VIP
virtual_router_id 1 # ID du routeur pour s'entendre avec le second noeud
priority 100 # Priorité du présent noeud (le plus haut gagne - max 255)
advert_int 1 # DĂ©lais de l'envoi de trames
authentication {
auth_type PASS
auth_pass 8_Caract # eres_max
}
virtual_ipaddress {
192.168.50.30 # Notre VIP
}
unicast_src_ip 192.168.50.21 # IP du présent noeud
unicast_peer {
192.168.50.22 # IP du second noeud
}
}
Et je garde la mĂŞme configuration sur lab07-lb-02
, je vais juste inverser les adresses source et pair.
! Configuration File for keepalived
global_defs {
vrrp_no_swap # Le processus VRRP enfant reste en RAM, pas de swap
vrrp_garp_master_delay 10 # Délais en secondes de grace ARP après une transition en MASTER
vrrp_garp_master_refresh 60 # DĂ©lais minimum de rafraichissement de la grace ARP tant que le noeud est MASTER
vrrp_garp_interval 0.001 # Délais entre les messages ARP gratuits envoyés a une interface
}
vrrp_instance lab07-lb-vip { # Nom de l'instance VRRP
state BACKUP # Etat par défaut
interface enp0s3 # Interface sur laquelle repose la VIP
virtual_router_id 1 # ID du routeur pour s'entendre avec le second noeud
priority 100 # Priorité du présent noeud (le plus haut gagne - max 255)
advert_int 1 # DĂ©lais de l'envoi de trames
authentication {
auth_type PASS
auth_pass 8_Caract # eres_max
}
virtual_ipaddress {
192.168.50.30 # Notre VIP
}
unicast_src_ip 192.168.50.22 # IP du présent noeud
unicast_peer {
192.168.50.21 # IP du second noeud
}
}
Un petit restart des Keepalived sur chaque serveur LB. Tu adaptes le hosts de ta machine pour que lab07.lab soit sur la même IP que la lab07-lb.vip et ça devrait surfer.
Qui c’est qui sert le blog ?
Génial, tout fonctionne mais on peux avoir besoin de savoir vers quel backend la requête HTTP à été redirigée, non ? On peut par exemple déposer un cookie en reconfigurant HAProxy
.
root@lab07-lb-01:~# vi /etc/haproxy/haproxy.cfg
#[...]
backend frontservers
cookie SERVERID insert # Je déclare mon cookie au niveau du backend
balance roundrobin
option httpchk
http-check connect
mode http
server lab07-front-01 192.168.50.23:80 check cookie lab07-front-01 # Je l'ajoute au niveau de chaque serveur
server lab07-front-02 192.168.50.24:80 check cookie lab07-front-02 # Je l'ajoute au niveau de chaque serveur
On a changĂ© la conf donc on doit redĂ©marrer l’outil.
root@lab07-lb-01:~# systemctl restart haproxy
Maintenant on peut s’assurer depuis un poste client que le cookie est bien dĂ©posĂ©. Pour ça on va faire 10 curls successifs et regarder la tronche du cookie.
antoine@dressing:~$ for i in {1..10}; do curl -c - http://lab07.lab 2>/dev/null| tail -1; done
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-01
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-01
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-01
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-01
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-01
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
Non seulement notre cookie est bien déposé mais en même temps on constate que le roundrobbin fait bien son boulot.
Bon le roundrobbin c’est bien mais il faudra quand mĂŞme penser Ă partager les sessions entre les serveurs.
Je ne suis pas encore assez calĂ© sur le sujet mais on pourrait imaginer qu’un de nos serveurs hĂ©berge un Redis
ou un étage supplémentaire avec un cluster Redis
.
La page de stats de HAProxy
On en a parlĂ© très sommairement plus haut mais la page de statistiques est vraiment importante. On y accède par l’IP des LB ou directement par la VIP.
Elle va avoir un Ă©norme intĂ©rĂŞt pour pouvoir passer un de nos backends “frontservers” en maintenance lorsque nĂ©cessaire et HAProxy
ne redirigera plus que sur le FRONT qui est fonctionnel. Ça se passe dans le menu dĂ©roulant tout en bas de la page et s’applique au(x) backend(s) dont la case est(sont) cochĂ©e(s). Ok… je te lâche la main.
Dans la vraie vie, tu préféreras passer ton serveur en DRAIN avant de le passer en MAINT pour ne pas fermer la porte au nez de tes clients.
antoine@dressing:~$ for i in {1..10}; do curl -c - http://lab07.lab 2>/dev/null| tail -1; done
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
lab07.lab FALSE / FALSE 0 SERVERID lab07-front-02
T’as vu ? HAProxy
a tout redirigé sur lab07-front-02
.
On a un peu modifié notre lab depuis le dernier schéma : on a ajouté un FRONT et les deux LB. Voici ce à quoi il ressemble.
La fritte sur l’macdo
On peut mĂŞme regarder notre Keepalived
taper la causette.
root@lab07-lb-01:~# tcpdump -i enp0s3 -nn 'host 192.168.50.21 and vrrp'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:03:28.385511 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20
15:03:29.385837 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20
15:03:30.386244 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement,
vrid 1, prio 100, authtype simple, intvl 1s, length 20
[...]
root@lab07-lb-01:~# tcpdump -i enp0s3 -nn 'host 192.168.50.22 and vrrp'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:03:46.396094 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20
15:03:47.396626 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20
15:03:48.400254 IP 192.168.50.21 > 192.168.50.22: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20
Fin de la bande