ProxySQL est un proxy SQL intelligent et performant qui prend en charge MySQL, MariaDB et ClickHouse. Récemment, ProxySQL 2.0 est devenu GA et il est livré avec de nouvelles fonctionnalités intéressantes telles que les lectures cohérentes GTID, le SSL frontal, la prise en charge native de Galera et de la réplication de groupe MySQL.
Il est relativement facile d'exécuter ProxySQL en tant que conteneur Docker. Nous avons déjà expliqué comment exécuter ProxySQL sur Kubernetes en tant que conteneur d'assistance ou en tant que service Kubernetes, basé sur ProxySQL 1.x. Dans cet article de blog, nous allons utiliser la nouvelle version ProxySQL 2.x qui utilise une approche différente pour la configuration de Galera Cluster.
Image Docker ProxySQL 2.x
Nous avons publié un nouveau conteneur d'images ProxySQL 2.0 Docker et il est disponible dans Docker Hub. Le README fournit un certain nombre d'exemples de configuration, en particulier pour Galera et MySQL Replication, pré et post v2.x. Les lignes de configuration peuvent être définies dans un fichier texte et mappées dans le chemin du conteneur à /etc/proxysql.cnf pour être chargées dans le service ProxySQL.
La balise "dernière" de l'image pointe toujours vers 1.x jusqu'à ce que ProxySQL 2.0 devienne officiellement GA (nous n'avons pas encore vu de blog/article de publication officiel de l'équipe ProxySQL). Ce qui signifie que chaque fois que vous installez l'image ProxySQL à l'aide de la dernière balise de Manynines, vous obtiendrez toujours la version 1.x avec celle-ci. Notez que les nouvelles configurations d'exemple activent également les statistiques Web ProxySQL (introduites dans la version 1.4.4 mais toujours en version bêta) - un tableau de bord simple qui résume la configuration globale et l'état de ProxySQL lui-même.
Prise en charge de ProxySQL 2.x pour le cluster Galera
Parlons plus en détail du support natif de Galera Cluster. La nouvelle table mysql_galera_hostgroups se compose des champs suivants :
- writer_hostgroup : ID du groupe d'hôtes qui contiendra tous les membres rédacteurs (read_only=0).
- backup_writer_hostgroup : Si le cluster s'exécute en mode multi-écrivain (c'est-à-dire qu'il existe plusieurs nœuds avec read_only=0) et que max_writers est défini sur un nombre inférieur au nombre total de nœuds, les nœuds supplémentaires sont déplacés vers ce groupe d'hôtes d'écrivain de sauvegarde.
- reader_hostgroup : ID du groupe d'hôtes qui contiendra tous les membres qui sont des lecteurs (c'est-à-dire les nœuds qui ont read_only=1)
- hors ligne_hostgroup : Lorsque la surveillance ProxySQL détermine qu'un hôte est HORS LIGNE, l'hôte sera déplacé vers offline_hostgroup.
- actif : une valeur booléenne (0 ou 1) pour activer un groupe d'hôtes
- max_writers : Contrôle le nombre maximal de nœuds autorisés dans le groupe d'hôtes de l'écrivain, comme mentionné précédemment, des nœuds supplémentaires seront déplacés vers le backup_writer_hostgroup.
- écrivain_est_aussi_lecteur : Lorsque 1, un nœud dans le writer_hostgroup sera également placé dans le reader_hostgroup afin qu'il soit utilisé pour les lectures. Lorsqu'il est défini sur 2, les nœuds de backup_writer_hostgroup seront placés dans le reader_hostgroup, au lieu du ou des nœuds dans le writer_hostgroup.
- max_transactions_behind : détermine le nombre maximal d'ensembles d'écritures qu'un nœud du cluster peut avoir mis en file d'attente avant que le nœud ne soit ÉVITÉ pour empêcher les lectures obsolètes (ceci est déterminé en interrogeant la variable Galera wsrep_local_recv_queue).
- commentaire : Champ de texte pouvant être utilisé à toutes fins définies par l'utilisateur
Voici un exemple de configuration pour mysql_galera_hostgroups sous forme de tableau :
Admin> select * from mysql_galera_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:
ProxySQL effectue des vérifications de l'état de Galera en surveillant l'état/la variable MySQL suivants :
- lecture_seule - Si activé, alors ProxySQL regroupera l'hôte défini dans reader_hostgroup sauf si writer_is_also_reader vaut 1.
- wsrep_desync - Si activé, ProxySQL marquera le nœud comme indisponible, le déplaçant vers offline_hostgroup.
- wsrep_reject_queries - Si cette variable est activée, ProxySQL marquera le nœud comme indisponible, le déplaçant vers offline_hostgroup (utile dans certaines situations de maintenance).
- wsrep_sst_donor_rejects_queries - Si cette variable est activée, ProxySQL marquera le nœud comme indisponible pendant que le nœud Galera sert de donneur SST, le déplaçant vers offline_hostgroup.
- wsrep_local_state - Si ce statut renvoie autre que 4 (4 signifie Synced), ProxySQL marquera le nœud comme indisponible et le déplacera dans offline_hostgroup.
- wsrep_local_recv_queue - Si ce statut est supérieur à max_transactions_behind, le nœud sera évité.
- wsrep_cluster_status - Si ce statut renvoie autre que Primaire, ProxySQL marquera le nœud comme indisponible et le déplacera dans offline_hostgroup.
Cela dit, en combinant ces nouveaux paramètres dans mysql_galera_hostgroups avec mysql_query_rules, ProxySQL 2.x a la flexibilité de s'adapter à beaucoup plus de cas d'utilisation de Galera. Par exemple, on peut avoir des groupes d'hôtes à un seul écrivain, plusieurs écrivains et plusieurs lecteurs définis comme groupe d'hôtes de destination d'une règle de requête, avec la possibilité de limiter le nombre d'écrivains et un contrôle plus fin du comportement des lectures obsolètes.
Comparez cela à ProxySQL 1.x, où l'utilisateur devait définir explicitement un planificateur pour appeler un script externe pour effectuer les vérifications de l'état du backend et mettre à jour l'état des serveurs de base de données. Cela nécessite une certaine personnalisation du script (l'utilisateur doit mettre à jour l'utilisateur/le mot de passe/le port de l'administrateur ProxySQL) et dépend d'un outil supplémentaire (client MySQL) pour se connecter à l'interface d'administration ProxySQL.
Voici un exemple de configuration du programmateur de scripts de vérification de l'état Galera au format tableau pour ProxySQL 1.x :
Admin> select * from scheduler\G
*************************** 1. row ***************************
id: 1
active: 1
interval_ms: 2000
filename: /usr/share/proxysql/tools/proxysql_galera_checker.sh
arg1: 10
arg2: 20
arg3: 1
arg4: 1
arg5: /var/lib/proxysql/proxysql_galera_checker.log
comment:
En outre, étant donné que le thread du planificateur ProxySQL exécute n'importe quel script indépendamment, de nombreuses versions de scripts de vérification de l'état sont disponibles. Toutes les instances ProxySQL déployées par ClusterControl utilisent le script par défaut fourni par le package d'installation de ProxySQL.
Dans ProxySQL 2.x, les variables max_writers et writer_is_also_reader peuvent déterminer comment ProxySQL regroupe dynamiquement les serveurs MySQL principaux et affectera directement la distribution des connexions et le routage des requêtes. Par exemple, considérez les serveurs backend MySQL suivants :
Admin> select hostgroup_id, hostname, status, weight from mysql_servers;
+--------------+--------------+--------+--------+
| hostgroup_id | hostname | status | weight |
+--------------+--------------+--------+--------+
| 10 | DB1 | ONLINE | 1 |
| 10 | DB2 | ONLINE | 1 |
| 10 | DB3 | ONLINE | 1 |
+--------------+--------------+--------+--------+
Avec la définition suivante des groupes d'hôtes Galera :
Admin> select * from mysql_galera_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:
Considérant que tous les hôtes sont opérationnels, ProxySQL regroupera très probablement les hôtes comme ci-dessous :
Regardons-les un par un :
Configuration | Description |
---|---|
writer_is_also_reader=0 |
|
writer_is_also_reader=1 |
|
writer_is_also_reader=2 |
|
Avec cette configuration, on peut avoir différents choix pour la destination du groupe d'hôtes afin de répondre à des charges de travail spécifiques. Les écritures "Hotspot" peuvent être configurées pour aller sur un seul serveur afin de réduire les conflits multi-maîtres, les écritures non conflictuelles peuvent être réparties de manière égale sur les autres maîtres, la plupart des lectures peuvent être réparties uniformément sur tous les serveurs MySQL ou les non-écrivains, les lectures critiques peuvent être transmises aux serveurs les plus récents et les lectures analytiques peuvent être transmises à une réplique esclave.
Déploiement de ProxySQL pour le cluster Galera
Dans cet exemple, supposons que nous ayons déjà un cluster Galera à trois nœuds déployé par ClusterControl, comme illustré dans le schéma suivant :
Nos applications Wordpress fonctionnent sur Docker tandis que la base de données Wordpress est hébergée sur notre cluster Galera fonctionnant sur des serveurs bare metal. Nous avons décidé d'exécuter un conteneur ProxySQL aux côtés de nos conteneurs Wordpress pour avoir un meilleur contrôle sur le routage des requêtes de base de données Wordpress et utiliser pleinement notre infrastructure de cluster de base de données. Étant donné que le ratio lecture-écriture est d'environ 80 % à 20 %, nous souhaitons configurer ProxySQL pour :
- Transférer toutes les écritures vers un nœud Galera (moins de conflits, se concentrer sur l'écriture)
- Équilibrer toutes les lectures vers les deux autres nœuds Galera (meilleure répartition pour la majorité de la charge de travail)
Tout d'abord, créez un fichier de configuration ProxySQL dans l'hôte Docker afin que nous puissions le mapper dans notre conteneur :
$ mkdir /root/proxysql-docker
$ vim /root/proxysql-docker/proxysql.cnf
Ensuite, copiez les lignes suivantes (nous expliquerons les lignes de configuration plus bas) :
datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="admin:admin"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
web_enabled=true
web_port=6080
stats_credentials="stats:admin"
}
mysql_variables=
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
default_schema="information_schema"
stacksize=1048576
server_version="5.1.30"
connect_timeout_server=10000
monitor_history=60000
monitor_connect_interval=200000
monitor_ping_interval=200000
ping_interval_server_msec=10000
ping_timeout_server=200
commands_stats=true
sessions_sort=true
monitor_username="proxysql"
monitor_password="proxysqlpassword"
monitor_galera_healthcheck_interval=2000
monitor_galera_healthcheck_timeout=800
}
mysql_galera_hostgroups =
(
{
writer_hostgroup=10
backup_writer_hostgroup=20
reader_hostgroup=30
offline_hostgroup=9999
max_writers=1
writer_is_also_reader=1
max_transactions_behind=30
active=1
}
)
mysql_servers =
(
{ address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)
mysql_query_rules =
(
{
rule_id=100
active=1
match_pattern="^SELECT .* FOR UPDATE"
destination_hostgroup=10
apply=1
},
{
rule_id=200
active=1
match_pattern="^SELECT .*"
destination_hostgroup=30
apply=1
},
{
rule_id=300
active=1
match_pattern=".*"
destination_hostgroup=10
apply=1
}
)
mysql_users =
(
{ username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
{ username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)
Passons maintenant à quelques-unes des sections les plus configurées. Tout d'abord, nous définissons la configuration des groupes d'hôtes Galera comme suit :
mysql_galera_hostgroups =
(
{
writer_hostgroup=10
backup_writer_hostgroup=20
reader_hostgroup=30
offline_hostgroup=9999
max_writers=1
writer_is_also_reader=1
max_transactions_behind=30
active=1
}
)
Le groupe d'hôtes 10 sera le writer_hostgroup, le hostgroup 20 pour le backup_writer et le hostgroup 30 pour le lecteur. Nous définissons max_writers sur 1 afin que nous puissions avoir un groupe d'hôtes à écrivain unique pour le groupe d'hôtes 10 où toutes les écritures doivent être envoyées. Ensuite, nous définissons writer_is_also_reader sur 1, ce qui fera également de tous les nœuds Galera des lecteurs, adaptés aux requêtes pouvant être distribuées de manière égale à tous les nœuds. Le groupe d'hôtes 9999 est réservé pour offline_hostgroup si ProxySQL détecte des nœuds Galera non opérationnels.
Ensuite, nous configurons nos serveurs MySQL avec par défaut le groupe d'hôtes 10 :
mysql_servers =
(
{ address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)
Avec les configurations ci-dessus, ProxySQL "verra" nos groupes d'hôtes comme ci-dessous :
Ensuite, nous définissons le routage des requêtes via des règles de requête. Selon nos exigences, toutes les lectures doivent être envoyées à tous les nœuds Galera à l'exception de l'écrivain (groupe d'hôtes 20) et tout le reste est transmis au groupe d'hôtes 10 pour un seul écrivain :
mysql_query_rules =
(
{
rule_id=100
active=1
match_pattern="^SELECT .* FOR UPDATE"
destination_hostgroup=10
apply=1
},
{
rule_id=200
active=1
match_pattern="^SELECT .*"
destination_hostgroup=20
apply=1
},
{
rule_id=300
active=1
match_pattern=".*"
destination_hostgroup=10
apply=1
}
)
Enfin, nous définissons les utilisateurs MySQL qui seront passés par ProxySQL :
mysql_users =
(
{ username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
{ username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)
Nous définissons transaction_persistent sur 0 afin que toutes les connexions provenant de ces utilisateurs respectent les règles de requête pour le routage des lectures et des écritures. Sinon, les connexions finiraient par toucher un groupe d'hôtes, ce qui irait à l'encontre de l'objectif de l'équilibrage de charge. N'oubliez pas de créer d'abord ces utilisateurs sur tous les serveurs MySQL. Pour l'utilisateur ClusterControl, vous pouvez utiliser la fonctionnalité Gérer -> Schémas et utilisateurs pour créer ces utilisateurs.
Nous sommes maintenant prêts à démarrer notre conteneur. Nous allons mapper le fichier de configuration ProxySQL en tant que montage lié lors du démarrage du conteneur ProxySQL. Ainsi, la commande d'exécution sera :
$ docker run -d \
--name proxysql2 \
--hostname proxysql2 \
--publish 6033:6033 \
--publish 6032:6032 \
--publish 6080:6080 \
--restart=unless-stopped \
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf \
severalnines/proxysql:2.0
Enfin, modifiez la base de données Wordpress pointant vers le port de conteneur ProxySQL 6033, par exemple :
$ docker run -d \
--name wordpress \
--publish 80:80 \
--restart=unless-stopped \
-e WORDPRESS_DB_HOST=proxysql2:6033 \
-e WORDPRESS_DB_USER=wordpress \
-e WORDPRESS_DB_HOST=passw0rd \
wordpress
À ce stade, notre architecture ressemble à ceci :
Si vous souhaitez que le conteneur ProxySQL soit persistant, mappez /var/lib/proxysql/ sur un volume Docker ou un montage lié, par exemple :
$ docker run -d \
--name proxysql2 \
--hostname proxysql2 \
--publish 6033:6033 \
--publish 6032:6032 \
--publish 6080:6080 \
--restart=unless-stopped \
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf \
-v proxysql-volume:/var/lib/proxysql \
severalnines/proxysql:2.0
Gardez à l'esprit que l'exécution avec un stockage persistant comme ci-dessus rendra notre /root/proxysql/proxysql.cnf obsolète au deuxième redémarrage. Cela est dû à la configuration multicouche de ProxySQL dans laquelle si /var/lib/proxysql/proxysql.db existe, ProxySQL ignorera les options de chargement du fichier de configuration et chargera à la place tout ce qui se trouve dans la base de données SQLite (sauf si vous démarrez le service proxysql avec --initial drapeau). Cela dit, la prochaine gestion de la configuration ProxySQL doit être effectuée via la console d'administration ProxySQL sur le port 6032, au lieu d'utiliser le fichier de configuration.
Surveillance
Le journal de processus ProxySQL se connecte par défaut à syslog et vous pouvez les afficher à l'aide de la commande docker standard :
$ docker ps
$ docker logs proxysql2
Pour vérifier le groupe d'hôtes actuel, interrogez la table runtime_mysql_servers :
$ docker exec -it proxysql2 mysql -uadmin -padmin -h127.0.0.1 -P6032 --prompt='Admin> '
Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname | status |
+--------------+--------------+--------+
| 10 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.22 | ONLINE |
| 30 | 192.168.0.23 | ONLINE |
| 20 | 192.168.0.22 | ONLINE |
| 20 | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+
Si l'enregistreur sélectionné tombe en panne, il sera transféré vers offline_hostgroup (HID 9999) :
Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname | status |
+--------------+--------------+--------+
| 10 | 192.168.0.22 | ONLINE |
| 9999 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.22 | ONLINE |
| 30 | 192.168.0.23 | ONLINE |
| 20 | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+
Les changements de topologie ci-dessus peuvent être illustrés dans le schéma suivant :
Nous avons également activé l'interface utilisateur des statistiques Web avec admin-web_enabled=true. Pour accéder à l'interface utilisateur Web, accédez simplement à l'hôte Docker sur le port 6080, par exemple :http://192.168.0.200:8060 et vous serez invité avec le nom d'utilisateur/mot de passe pop-up. Entrez les informations d'identification telles que définies sous admin-stats_credentials et vous devriez voir la page suivante :
En surveillant la table de pool de connexions MySQL, nous pouvons obtenir un aperçu de la distribution des connexions pour tous les groupes d'hôtes :
Admin> select hostgroup, srv_host, status, ConnUsed, MaxConnUsed, Queries from stats.stats_mysql_connection_pool order by srv_host;
+-----------+--------------+--------+----------+-------------+---------+
| hostgroup | srv_host | status | ConnUsed | MaxConnUsed | Queries |
+-----------+--------------+--------+----------+-------------+---------+
| 20 | 192.168.0.23 | ONLINE | 5 | 24 | 11458 |
| 30 | 192.168.0.23 | ONLINE | 0 | 0 | 0 |
| 20 | 192.168.0.22 | ONLINE | 2 | 24 | 11485 |
| 30 | 192.168.0.22 | ONLINE | 0 | 0 | 0 |
| 10 | 192.168.0.21 | ONLINE | 32 | 32 | 9746 |
| 30 | 192.168.0.21 | ONLINE | 0 | 0 | 0 |
+-----------+--------------+--------+----------+-------------+---------+
La sortie ci-dessus montre que le groupe d'hôtes 30 ne traite rien car nos règles de requête n'ont pas ce groupe d'hôtes configuré comme groupe d'hôtes de destination.
Les statistiques liées aux nœuds Galera peuvent être consultées dans la table mysql_server_galera_log :
Admin> select * from mysql_server_galera_log order by time_start_us desc limit 3\G
*************************** 1. row ***************************
hostname: 192.168.0.23
port: 3306
time_start_us: 1552992553332489
success_time_us: 2045
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
*************************** 2. row ***************************
hostname: 192.168.0.22
port: 3306
time_start_us: 1552992553329653
success_time_us: 2799
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
*************************** 3. row ***************************
hostname: 192.168.0.21
port: 3306
time_start_us: 1552992553329013
success_time_us: 2715
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
Le jeu de résultats renvoie l'état de la variable/statut MySQL associé pour chaque nœud Galera pour un horodatage particulier. Dans cette configuration, nous avons configuré le bilan de santé Galera pour qu'il s'exécute toutes les 2 secondes (monitor_galera_healthcheck_interval=2000). Par conséquent, le temps de basculement maximal serait d'environ 2 secondes si un changement de topologie se produisait sur le cluster.
Références
- Prise en charge de ProxySQL Native Galera
- Solution de haute disponibilité et de clustering :ProxySQL en tant que routeur intelligent pour Galera et la réplication de groupe
- Image ProxySQL Docker par Plusieursnines
- Comment surveiller ProxySQL avec Prometheus et ClusterControl