Nous allons voir dans cet article comment mettre en place un Load Balancer frontal HAProxy pour répartir la charge entre deux serveurs fournissant un service identique.
Dans cet exemple, et dans un soucis de simplicité, HAProxy est utilisé pour répartir la charge entre deux bases de données PostgreSQL en lecture seule.
HAProxy n’est pas capable de répartir les lectures sur un cluster en streaming replication car il travaille sur la couche 4 (tcp), sans aucune considération pour les protocoles de couche supérieure.
1. Prérequis
L’environnement est composé de quatre serveurs installés en CentOS 7.7 :
- Deux serveurs postgreSQL qui présentent la même base de données en lecture seule
- Deux serveurs HAProxy
Un seul serveur HAProxy suffit pour effectuer la répartition de charge. Cependant, dans une optique de haute disponibilité, nous allons mettre en place deux Load Balancers en actif/passif via une adresse IP virtuelle partagée. La gestion de cette VIP se fera à l’aide de Keepalived.
2. HAProxy
Installer HAProxy sur les deux serveurs :
[root@ha1 haproxy]# yum install haproxy
Suite à l’installation, sauvegarder le fichier de configuration par défaut et saisir la configuration suivante sur les deux Load Balancers :
[root@ha1 haproxy]# mv /etc/haproxy/haproxy.cfg /etc/haproxy/origin.haproxy.cfg [root@ha1 haproxy]# vi /etc/haproxy/haproxy.cfg --------------------------------------------------------------- global log 127.0.0.1 local5 debug defaults mode tcp timeout connect 5000ms timeout client 3600s timeout server 3600s frontend front bind *:2525 default_backend pgpools backend pgpools balance roundrobin server pgpool1 pg1:9999 check observe layer4 weight 100 server pgpool2 pg2:9999 check observe layer4 weight 100 ---------------------------------------------------------------
- Dans la section defaults, on spécifie que l’on souhaite que HAProxy fonctionne en mode tcp (couche 4) et on spécifie des timeouts pour l’établissement de la connexion, et pour les connexions établies coté client et serveur.
- Dans la section frontend, on configure l’adresse et le port d’écoute. * permet d’écouter sur toutes les adresses. On indique également un backend par défaut.
- Dans la section backend, on liste les serveurs dont la charge doit être répartie avec un alias, le hostname et le port d’accès. Le paramètre weight permet de jouer sur l’équité de la répartition.
Activer le service haproxy et le démarrer :
[root@ha1 haproxy]# systemctl enable haproxy [root@ha1 haproxy]# systemctl start haproxy
Une fois HAProxy démarré, il devrait être possible d’accéder à son processus d’écoute pour accéder au service balancé. Dans cet exemple, on teste depuis un client la connexion postgreSQL en attaquant chaque serveur haproxy sur le port 2525.
[root@pgclient ~]# psql -U postgres -h ha1 -p 2525 Mot de passe pour l'utilisateur postgres : psql (12.1) Saisissez « help » pour l'aide. postgres=# \q [root@pgclient ~]# psql -U postgres -h ha2 -p 2525 Mot de passe pour l'utilisateur postgres : psql (12.1) Saisissez « help » pour l'aide.
Dans les deux cas, la connexion est établie, ce qui confirme la bonne configuration des backends.
3. Keepalived
A ce stade, les HAProxy sont prêts à effectuer la répartition de charge. Cependant, un problème reste à régler car nous avons deux HAProxy qui rendent le même service mais les applications doivent accéder au service via un couple hostname:port unique. Pour ce faire, on pourrait par exemple mettre en place un round-robin via le serveur DNS, pour qu’un nom soit traduit à tour de rôle pour l’une ou l’autre des adresses IPs des HAProxy. Cependant, en cas de panne d’un serveur, la moitié des requêtes n’aboutiraient plus. Pour faire fonctionner cela de manière hautement disponible, il faut mettre en place une adresse IP virtuelle partagée entre les deux serveurs via Keepalived.
Installation de keepalived sur les deux Load Balancers :
yum install keepalived
Sauvegarder le fichier de configuration par défaut et saisir la configuration suivante :
[root@ha1 haproxy]# mv /etc/keepalived/keepalived.conf /etc/keepalived/origin.keepalived.conf [root@ha1 haproxy]# vi /etc/keepalived/keepalived.conf -------------------------------------------------------- global_defs { lvs_sync_daemon enp0s3 INSTANCE_1 router_id ha1 } vrrp_instance INSTANCE_1 { virtual_router_id 100 interface enp0s3 state MASTER priority 100 advert_int 1 authentication { auth_type PASS auth_pass secret } virtual_ipaddress { 10.1.0.50/32 brd 10.1.0.255 scope global } } --------------------------------------------------------
- Le paramètre routeur_id peut être changé sur le second noeud HAProxy, même si cela n’est pas obligatoire.
- Il faut vérifier que l’interface réseau enp0s3 soit la bonne, sinon la modifier.
- Veiller à ce que le virtual_router_id soit bien le même sur les deux noeuds.
Une fois cette configuration en place, on peut activer et démarrer le service keepalived sur les deux noeuds, et tester le bon fonctionnement de la VIP :
[root@ha1 haproxy]# systemctl enable keepalived [root@ha1 haproxy]# systemctl start keepalived
On doit voir la VIP 10.1.0.50 disponible sur l’un des serveurs. Ici, elle est active sur le second noeud ha2 :
[root@ha1 keepalived]# ip addr | grep 'inet ' inet 127.0.0.1/8 scope host lo inet 10.1.0.20/24 brd 10.1.0.255 scope global noprefixroute enp0s3 [root@ha2 keepalived]# ip addr | grep 'inet ' inet 127.0.0.1/8 scope host lo inet 10.1.0.21/24 brd 10.1.0.255 scope global noprefixroute enp0s3 inet 10.1.0.50/32 brd 10.1.0.255 scope global enp0s3
En cas d’arrêt du serveur ha2, on constate bien que la VIP est failed over sur l’autre serveur :
[root@ha1 keepalived]# ip addr | grep 'inet ' inet 127.0.0.1/8 scope host lo inet 10.1.0.20/24 brd 10.1.0.255 scope global noprefixroute enp0s3 inet 10.1.0.50/32 brd 10.1.0.255 scope global enp0s3
Il convient alors d’utiliser cette adresse 10.1.0.50 au niveau des applicatifs ou autres clients. Ce faisant, le service tolère la perte d’un des serveurs HAProxy et de l’un des serveurs PostgreSQL.
Pour utiliser la répartition de charge HAProxy dans un environnement PostgreSQL plus typique avec une base maître et des bases secondaires en streaming replication, il faut cependant le combiner avec pgpool, qui est capable de répartir seulement les lectures sur des noeuds en lecture seule, de part sa compréhension du protocole de communication PostgreSQL.