Load Balancing avec HAProxy

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.