Bilanciare il traffico verso Galera Cluster con HAProxy

Abbiamo già visto qui le caratteristiche di Galera Cluster, che permette di realizzare una topologia multi-master active-active di database con replica sincrona dei dati, abilitando le operazioni di lettura e scrittura su tutti i nodi del cluster.

Per garantire un’alta disponibilità ed una maggiore scalabilità dell’architettura è utile inserire un elemento di bilanciamento del carico tra la parte applicativa (Application Servers) ed il cluster database. HAproxy offre le funzionalità necessarie per avere un traffico dati bilanciato tra applicazioni e nodi database, assicurando inoltre che il nodo database sia sempre disponibile.

In figura è illustrata una possibile architettura con HAProxy come Load Balancer tra applicazioni e database.

haproxy-galera

In questo caso abbiamo ipotizzato che HAProxy sia installato su un elemento separato, e tutti gli App Server comunicano con il database tramite il singolo HAProxy. Altra possibilità è quella di installarlo localmente agli App Server, in questo caso ciascuna applicazione invia richieste al HAProxy locale che poi bilancia sui nodi database. Ulteriore possibilità è installare HAProxy sui nodi database, in questo caso un App Server potrebbe inviare richieste verso HAProxy di un singolo nodo, il quale a sua volta può bilanciare verso gli altri nodi database.

Configurazione di HAProxy

HAProxy è un pacchetto di solito incluso nelle distribuzioni Linux, pertanto si procedere con un semplice yum install haproxy, a meno di funzionalità particolari per le quali magari può essere richiesta una versione particolare disponibile sul sito.

In uno scenario come questo lo scopo di HAProxy è quello di rappresentare un punto di accesso unico al cluster Galera, bilanciando il traffico verso i diversi nodi ed assicurando che le richieste del client siano indirizzate correttamente verso uno dei nodi disponibili. Pertanto, in fase di configurazione si dovrà tenere in considerazione sia la modalità di balancing che si vuole utilizzare, sia i meccanismi di controllo della disponibilità dei nodi.

HAProxy prevede diverse modalità di bilanciamento del traffico, tra le quali la modalità round-robin e la modalità backup, con uno o più nodi utilizzati esclusivamente come backup. In questo articolo vedremo brevemente questa parte, mentre andremo nel dettaglio riguardo i meccanismi di controllo dei nodi database. Possiamo considerare tre diverse logiche di monitoring della disponibilità dei nodi database.

  • TCP health check usato per abilitare un controllo di base, HAProxy invierà un pacchetto TCP al server di backend per verificare se è in ascolto sulla porta specificata e se risponde correttamente
  • MySQL health check usato per abilitare un controllo a livello di connessione sul database server, HAProxy tenterà una connessione al database per verificare che il server sia attivo
  • HTTP health check permette di abilitare un controllo più avanzato sulla disponibilità del server, HAProxy invia una richiesta HTTP al server che può essere utilizzata per effettuare controlli puntuali sulle funzionalità del server

Supponiamo che l’IP del nostro HAProxy sia 192.168.1.5 e che i nostri DBMS server siano 192.168.1.10, 192.168.1.11 e 192.168.1.12 e andiamo a vedere come configurare i diversi metodi di health check.

TCP health check

In questo caso HAProxy verifica che sia possibile stabilire una connessione TCP con i server.

	#---------------------------------------------------------------------
	# galera cluster
	#---------------------------------------------------------------------
	listen galeracluster
     bind 192.168.1.5:3306
     log global
     balance source
     mode tcp
     option tcpka
     server node1 192.168.1.10:3306 inter 2s downinter 2s rise 3 fall 2
     server node2 192.168.1.11:3306 inter 2s downinter 2s rise 3 fall 2 backup
     server node3 192.168.1.12:3306 inter 2s downinter 2s rise 3 fall 2 backup

Con questa configurazione HAProxy prova a stabilire una connessione TCP sulla porta di ascolto dei server. L’opzione option tcpka utilizzata per abilitare la funzionalità di keep-alive TCP permette di mantenere la connessione TCP aperta con il server di backend anche dopo aver completato il controllo della sua disponibilità.

Questa la descrizione degli altri parametri presenti sull’opzione server:

  • inter specifica l’intervallo tra 2 health check consecutivi
  • downinter è l’intervallo di tempo minimo che deve trascorrere dopo che un server di backend è stato contrassegnato come DOWN prima che inizi a essere soggetto a ulteriori health check
  • rise è il numero di health check con successo consecutivi richiesti affinché un server precedentemente in stato DOWN venga nuovamente considerato UP
  • fall è il numero di health check falliti consecutivi affinché un server venga considerato in stato DOWN
  • backup contrassegna un server come server di backup, cioè verrà usato solo se il server (o i server) principali sono in stato DOWN

MySQL health check

In questo caso HAProxy verifica che sia possibile stabilire una connessione MySQL con il DBMS attivo sui server.

	#---------------------------------------------------------------------
	# galera cluster
	#---------------------------------------------------------------------
	listen galeracluster
     bind 192.168.1.5:3306
     log global
     balance source
     mode tcp
     option mysql-check
     server node1 192.168.1.10:3306 inter 2s downinter 2s rise 3 fall 2
     server node2 192.168.1.11:3306 inter 2s downinter 2s rise 3 fall 2 backup
     server node3 192.168.1.12:3306 inter 2s downinter 2s rise 3 fall 2 backup

Con questa configurazione HAProxy prova a stabilire una connessione con il server MySQL. Con l’opzione option mysql-check HAProxy invia una richiesta di connessione al server MySQL e controlla il pacchetto di risposta se è un “Mysql Handshake Initialization” packet oppure se è un “Error” packet. Si può anche utilizzare l’opzione specificando un utente, option mysql-check user in questo caso HAProxy proverà a connettersi al server MySQL usando l’utente specificato, inviando un “Client Authentication” packet. Sul DBMS è necessario che l’utente specificato sia configurato per accettare connessioni senza password.

HTTP health check

Utilizzando i 2 metodi di check appena descritti, HAProxy è in grado di controllare che il server MySQL sia attivo e cioè risponda a richieste di connessione, ma questo non garantisce che il DBMS sia operativo a tutti gli effetti. In un cluster Galera, per esempio, in condizioni di split brain, il server accetta connessioni ma il database non è operativo e quindi risponderà con errore a qualsiasi richiesta di query successiva alla login.
Per controllare l’effettiva disponibilità del DBMS possiamo sfruttare una modalità di check più intelligente di HAProxy abbinata alle funzionalità application socket di Linux. Per fare questo abbiamo bisogno di:

  1. Configurare HAProxy per utilizzare l’opzione httpchk che effettua l’health check inviando una richiesta HTTP e controllando la risposta
  2. Attivare un application socket sul server per restare in ascolto su una determinata porta TCP
  3. Predisporre sul server uno script eseguito alla ricezione di una connessione sul socket che va ad effettuare le opportune verifiche sul DBMS

La configurazione di HAProxy per il check di tipo HTTP è la seguente.

	#---------------------------------------------------------------------
	# galera cluster
	#---------------------------------------------------------------------
	listen galeracluster
     bind 192.168.1.5:3306
     log global
     balance source
     mode tcp
     option httpchk OPTIONS * HTTP/1.1
     server node1 192.168.1.10:3334 inter 2s downinter 2s rise 3 fall 2
     server node2 192.168.1.11:3334 inter 2s downinter 2s rise 3 fall 2 backup
     server node3 192.168.1.12:3334 inter 2s downinter 2s rise 3 fall 2 backup

In questo modo HAProxy invia una OPTIONS HTTP sulla porta 3334 dei server e considererà il server UP se la risposta è un 2xx o un 3xx, altrimenti considererà il server nello stato DOWN.

Sul server attiviamo questi 2 servizi.

	cd /etc/systemd/system
	cat mysqlcheck.socket
	
	[Unit]
	Description=mysqlcheck Socket

	[Socket]
	ListenStream=192.168.1.10:3334
	Accept=yes

	[Install]
	WantedBy=sockets.target


	cat mysqlcheck@.service
	
	[Unit]
	Description=mysqlcheck Per-Connection Server

	[Service]
	ExecStart=-/opt/mysql/mysqlcheck.sh     # the binary to start
	User=root
	Group=root
	StandardInput=socket

In questo modo sul server è attivo un socket sulla porta 3334 TCP che alla ricezione di una connessione esegue il servizio /opt/mysql/mysqlcheck.sh. Lo script in questione è il seguente.

	#!/bin/bash

	# MySQL Health Monitoring Script

	MYSQL_HOST="localhost"
	MYSQL_PORT="3306"
	MYSQL_USER="hauser"
	MYSQL_PASS="hapassword"

	SUCCESS_LOG="/opt/mysql/mysql-scs.log"
	ERR_LOG="/opt/mysql/mysql-err.log"

	# MySQL query

	/usr/bin/mysql --host=$MYSQL_HOST --port=$MYSQL_PORT --user=$MYSQL_USER --password=$MYSQL_PASS -e "select * from haproxy.status;" > $SUCCESS_LOG 2> $ERR_LOG


	# Checking parameters -- if MySQL answers the query, it writes success to SUCCESS_LOG else file will be empty then it'll jump to error(else) part of the script

	if [ "$(/bin/cat $SUCCESS_LOG)" != "" ]
	then
		# mysql is fine, return http 200
		/bin/echo -e "HTTP/1.1 200 OK\r\n"
	else
		# Problem with database, return http 503
		/bin/echo -e "HTTP/1.1 503 Service Unavailable\r\n"
	fi

Quindi provando a riassumere, HAProxy invia una HTTP OPTIONS sulla porta 3334 del server; sul server è attivo un socket sulla porta 3334 che alla ricezione della connessione esegue uno script; lo script effettua la connessione al database con un utente opportunamente configurato ed esegue una query; se la query è eseguita con successo il server risponde con HTTP 200 OK, se la query ritorna errore il server risponde con un errore HTTP 500; HAProxy riceve la risposta ed interpreta il codice di risposta, settando lo stato del server come UP oppure DOWN.