Controllare MySQL con xinetd

Con la replica MySQL e' facile mantenere su server diversi una serie di database allineati tra loro. I database vengono utilizzati dalle applicazioni migliorando cosi' le prestazioni, la scalabilita' e fornendo la massima affidabilita'.
Nelle configurazioni piu' complete la scelta del server cui collegarsi e' effettuata da un load balancer (software o hardware) e non dal driver o dall'applicazione.
In questa pagina presentiamo una semplice tecnica basata su xinetd per effettuare il routing corretto con un load balancer.

Nel seguito sono riportate alcune informazioni di interesse organizzate in paragrafi specifici: Introduzione, Installazione e configurazione, Esempi, Varie ed eventuali, ...

Il contenuto di questo documento e' tecnico. E' opportuna una conoscenza dell'architettura MySQL e della Replication.

Negli esempi che seguono vengono utilizzate le sintassi valide sugli ambienti piu' recenti ovvero: CentOS 7.3, MySQL 5.7 ed HAProxy 1.7 ma valgono per altri sistemi, programmi e versioni naturalmente... mutatis mudandis!

Introduzione

La funzionalita' di replica dati di MySQL e' molto utilizzata perche' e' semplice da configurare, aggiunge un carico ridotto ed e' molto flessibile.

Con la replica MySQL le architetture che si possono realizzare sono molteplici a seconda delle disponibilita' di risorse computazionali e degli obiettivi ricercati. Una tipica configurazione e' quella che utilizza molteplici Slave per fornire un'elevata scalabilita' sui carichi di lettura: MySQL Replication with a load balancer

Nella configurazione presentata nella figura precedente le connessioni in lettura vengono bilanciate su decine (o centinaia) di DB server differenti permettendo di distribuire il carico ed ottenere prestazioni sempre ottimali. Naturalmente questo richiede che le applicazioni utilizzino connessioni differenti a seconda del tipo di richiesta: molte applicazioni MySQL hanno la possibilita' di configurare stringhe di connessione differenti appunto per questa ragione.

MySQL Replication with a load balancer Molto piu' recente [NdE disponibile dal 2016-12 in MySQL 5.7.17] e' la Group Replication che consente di gruppi di replica in alta affidabilita' presentata nella figura a lato.

In questo caso e' possibile bilanciare anche le connessioni in scrittura perche' la Group Replication effettua automaticamente la failure detection e consente anche la configurazione Multi Primary.
Con la Group Replication le scelte possibili sono diverse... a mio avviso la configurazione migliore e' quella di utilizzare la configurazione Single-Primary indirizzando le scritture sul Master e bilanciando le letture su tutti i nodi disponibili. In questo modo si ha l'alta affidabilita', l'automatismo per la gestione dei fault e si bilancia comunque il carico.

Nella configurazione dei load balancer una semplice configurazione consiste nel verificare se la porta 3306 dei server e' in LISTEN. Tale configurazione del load balancer con MySQL e' adatta solo alle architetture piu' semplici perche' non copre tutte le possibili casistiche:

E' quindi necessario implementare una logica che tenga conto dello stato della base dati per istruire correttamente il load balancer.

In questa pagina illustriamo una semplice tecnica, basata sul demone xinetd, per produrre una pagina web sul DB server che indica se il database e' disponibile o meno. In questo modo la verifica da effettuare sul load balancer e' molto semplice: se la pagina restituisce OK la base dati puo' essere contattata!

Installazione e configurazione

Su CentOS 7 l'istallazione e l'avvio del servizio xinetd e' banale:

yum install xinetd systemctl start xinetd systemctl enable xinetd

Ora possiamo effettuare la configurazione del nuovo servizio. Nel file /etc/xinetd.d/mysql_check impostiamo la configurazione:

# default: on
# description: check to see if the node is a viable routing candidate
service mysql_check
{
	disable = no
	id		= mysql-stream
	type		= unlisted
	wait		= no
	socket_type	= stream
	flags = REUSE
	socket_type = stream
	port = 6446
	wait = no
	user = mysql
	server = /var/lib/mysql-files/mysql_check.sh
	group		= mysql
	server_args	= NONE
	log_on_failure += USERID
}

Non e' obbligatorio ma e' comunque educato dichiarare le porte utilizzate nel file /etc/services:

mysql_check     6446/tcp                        # MySQL Availability Check

Il cuore del controllo e' contenuto in questo script shell da riportare nel file /var/lib/mysql-files/mysql_check.sh:

#!/bin/bash
M_USER=check_db
M_PASS="Check.X00"
M_HOST=localhost
M_PORT=3306

# Simple check (can connect to MySQL)
M_CHECK=`mysql -nsLNE --connect-timeout=5 --host=$M_HOST --port=$M_PORT --user="$M_USER" --password="$M_PASS" -e 'SELECT "OK" ' 2>/dev/null | grep -v '*'`

if [ "$M_CHECK" == "OK" ]
then
    # Member is a candidate ==> return HTTP 200
    echo -en "HTTP/1.1 200 OK\r\n"
    echo -en "Content-Type: text/plain\r\n"
    echo -en "Connection: close\r\n"
    echo -en "Content-Length: 57\r\n"
    echo -en "\r\n"
    echo -en "Database Server is OK.\r\n"
    exit 0
else
    # Member is NOT a candidate ==> return HTTP 503
    echo -en "HTTP/1.1 503 Service Unavailable\r\n"
    echo -en "Content-Type: text/plain\r\n"
    echo -en "Connection: close\r\n"
    echo -en "Content-Length: 61\r\n"
    echo -en "\r\n"
    echo -en "Database Server is not in a valid state.\r\n"
    exit 1
fi

Naturalmente va creato uno specifico utente sul DB oppure va utilizzata un'utenza attiva.

Con il riavvio del demone la configurazione e' attiva:

systemctl restart xinetd

Dovrebbe essere chiaro che la verifica si effettua con un banale telnet sulla porta 6446 oppure con un browser!

Esempi

Nella configurazione del controllo e' stata utilizzata una verifica molto semplice, basta riuscire ad eseguire su MySQL la query: SELECT "OK".
E' una verifica appena piu' sofisticata rispetto alla semplice apertura della porta 3306... ma basta cambiare la query per implementare il controllo desiderato.

Il controllo per verificare se la base dati e' in sola lettura (quando si vuole effettuare il load balancing tra i soli Slave):

SELECT "OK" from dual where @@global.read_only=1

E' analogo il controllo per verificare se la base dati e' in scrittura (quando si vuole effettuare una transazione sul Master):

SELECT "OK" from dual where @@global.read_only=0

Se invece l'esigenza e' quella di indirizzare le scrittura nella configurazione single-primary della Group Replication: SELECT "OK" FROM performance_schema.global_status WHERE variable_name= 'group_replication_primary_member' AND variable_value=@@server_uuid

La configurazione del load balancer dipende dal... load balancer!

Ecco un esempio per HAProxy (file /etc/haproxy/haproxy.cfg):

frontend mysql-gr-front
bind *:3306
mode tcp
default_backend mysql-gr-back
 
backend mysql-gr-back
mode tcp
balance leastconn
option httpchk
server mysql1 192.168.1.92:3306 check port 6446 inter 1000 rise 1 fall 2
server mysql2 192.168.1.93:3306 check port 6446 inter 1000 rise 1 fall 2
server mysql3 192.168.1.94:3306 check port 6446 inter 1000 rise 1 fall 2

Le configurazioni ed i controlli possibili sono innumerevoli e dipendono dall'architettura presente e dalle funzionalita' desiderate.
Ad esempio si puo' voler dare precedenza a DB server locali, escludere i nodi che effettuano backup o sono in manutenzione, ... e' semplice modificare la query di controllo eseguita da xinetd o le politiche di bilanciamento configurate sul load balancer per ottenere il risultato voluto.

Varie ed eventuali

L'idea di utilizzare xinetd non e' mia (cfr. articolo Matt Lord) e non e' neanche recente (articolo Unai Rodriguez)... Pero' e' una buona idea lo stesso e non ho trovato una pagina analoga in italiano!

La scelta del load balancer presenta molteplici possibilita' in termini di funzionalita', prestazioni, costi, ...
Tra i moltissimi fornitori di load balancer Hardware: F5 BIG-IP Local Traffic Manager (LTM), Citrix NetScaler, Cisco, Radware, Kemp, Barracuda, ...
Tra i moltissimi fornitori di load balancer Software generici: HAProxy (un altro esempio), NGINX, ...
Ed infine i load balancer specifici per MySQL: ProxySQL, Max Scale, ...
Per completare il quadro si deve ricordare che praticamente tutti i fornitori HW hanno anche una versione SW, che tutti i balancer SW possono essere installati su appliance e che per fare bilanciamento non e' necessario un load balancer (lo fanno anche il DNS ed i DB driver ma non si possono controllare).

Un'alternativa all'utilizzo dei load balancer e' MySQL Router che tuttavia nella versione attuale [NdE 2.0] non ha funzionalita' per la gestione dell'alta affidabilita' ne si integra con la group replication.

Breaking news!
Disponibile oggi in CA [NdA 12 aprile 2017] la nuova versione di MySQL Router 2.1 con il supporto della Group Replication e della HA.


Titolo: Controllo disponibilita' di MySQL con xinetd
Livello: Esperto (4/5)
Data: 1 Gennaio 2017
Versione: 1.0.1 - 12 Aprile 2017
Autore: mail [AT] meo.bogliolo.name