MariaDB en Master/Slave via ProxySQL
On y est. J'ai enfin réussi à faire tourner ProxySQL comme je l'entendais pour que mes conteneurs tapent dans des BDD loin de Kubernetes. Sortir ses bases de données du cluster est un choix. Si vous êtes courageux, vous pouvez les laisser dedans. Ceci-dit, faut être un tantinet croyant pour se lancer dans la seconde solution. Dans ces histoires de hautes dispo et de résistance aux pannes, la bonne solution semble être de gérer son cluster de BDD en dehors de son cluster k8s.
Installation des MariaDB
Là, je devrais vous passer une pile de commandes pour installer des serveurs MariaDB en master/slave, mais bon. Tout est très clair dans la documentation officielle.
Sachez simplement que le master porte l'IP 192.168.0.17 et que le slave, lui, là 192.168.0.77.
Installation de ProxySQL
Helm ? Non, pas ici. On va donc pondre un YAML avec le Deployment et le Service qui va bien :
dada@master:~/proxysql$ cat proxysql.yaml
apiVersion: v1
kind: Deployment
metadata:
name: proxysql
labels:
app: proxysql
spec:
replicas: 2
selector:
matchLabels:
app: proxysql
tier: frontend
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: proxysql
tier: frontend
spec:
restartPolicy: Always
containers:
- image: severalnines/proxysql:1.4.12
name: proxysql
volumeMounts:
- name: proxysql-config
mountPath: /etc/proxysql.cnf
subPath: proxysql.cnf
ports:
- containerPort: 6033
name: proxysql-mysql
- containerPort: 6032
name: proxysql-admin
volumes:
- name: proxysql-config
configMap:
name: proxysql-configmap
---
apiVersion: v1
kind: Service
metadata:
name: proxysql
labels:
app: proxysql
tier: frontend
spec:
type: NodePort
ports:
- nodePort: 30033
port: 6033
name: proxysql-mysql
- nodePort: 30032
port: 6032
name: proxysql-admin
selector:
app: proxysql
tier: frontend
A remarquer
Le Deployment contient la configuration de ce qu'on appelle un ReplicatSet. Ça permet à k8s de toujours maintenir le nombre de pods déclaré en fonction. Quand un pod se casse la figure, il revient à lui. Dans un RS, quand un pod de casse la figure, il va aussi revenir à lui. Super. Sauf que, par exemple, dans le cas d'une mise à jour, k8s ne va pas terminer les pods d'un coup, mais les gérer un par un.
Le YAML contient aussi un Service de type NodePort qui va nous permettre de mapper les ports 30032 et 30033 internes aux pods sur les ports 6032 et 6033 accessibles depuis l'extérieur. Voyez la suite du billet pour découvrir qu'au lieu de taper sur le port 3306 de vos serveurs MariaDB, vous allez taper sur le port 6033.
On fait gober tout ça au cluster :
dada@master:~/proxysql$ kubectl apply -f proxysql.yaml
Et voilà le travail :
dada@master:~/proxysql$ kubectl get pods --all-namespaces | grep proxysql
default proxysql-5c47fb85fb-fdh4g 1/1 Running 1 39h
default proxysql-5c47fb85fb-kvdfv 1/1 Running 1 39h
Configuration de ProxySQL
Tout va se jouer dans le fichier proxysql.cnf. Tout, ou presque, mais j'en parlerai après. Voici déjà de quoi faire :
datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="proxysql-admin:adminpwd"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
}
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="proxysqlpwd"
}
mysql_replication_hostgroups =
(
{ writer_hostgroup=10, reader_hostgroup=20, comment="MariaDB Replication" }
)
mysql_servers =
(
{ address="192.168.0.17", port=3306, hostgroup=10, max_connections=100, max_replication_lag = 5 },
{ address="192.168.0.77", port=3306, hostgroup=20, max_connections=100, max_replication_lag = 5}
)
mysql_users =
(
{ username = "nextcloud" , password = "nextcloudpwd" , default_hostgroup = 10 , active = 1 }
)
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
}
)
Il est long alors on va le découper ensemble.
Global
{
admin_credentials="proxysql-admin:adminpwd"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
}
Ici, on retrouve surtout les accès administrateur de ProxySQL et son port qui écoute sans limite d'IP.
Mysql_variables
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="proxysqlpwd"
}
Là encore, je ne vais pas m'étendre sur toute la configuration (par défaut) mais sur deux points seulement : le port 6033 sera celui que vous allez rentrer dans les fichiers de configuration de vos services ayant besoin de MariaDB et l'utilisateur "monitor" est là pour vérifier l'état de vos serveurs MariaDB.
Mysql_replication_hostgroups
mysql_replication_hostgroups =
(
{ writer_hostgroup=10, reader_hostgroup=20, comment="MariaDB Replication" }
)
C'est à partir de là qu'on commence à s'amuser ! Ici, on déclare deux groupes de serveurs avec deux ID différents. Les writer, ceux qui ont le droit d'écrire et les reader, ceux qui n'auront que le droit de lire. Ça tombe bien, c'est plus ou moins ce qu'on veut.
Mysql_servers
mysql_servers =
(
{ address="192.168.0.17", port=3306, hostgroup=10, max_connections=100, max_replication_lag = 5 },
{ address="192.168.0.77", port=3306, hostgroup=20, max_connections=100, max_replication_lag = 5}
)
Si vous aviez suivi le blabla précédent, vous pouvez déchiffrer ces deux lignes en :
- Le serveur avec l'IP 192.168.0.17 fait partie du groupe des writers On lui demande de traiter 100 connexions actives maximum et d'avoir un lag avec son master de moins de 5 secondes.
- Le serveur avec l'IP 192.168.0.77 fait partie du groupe des reader.
On lui demande de traiter 100 connexions actives maximum et d'avoir un
lag avec son master de moins de 5 secondes.
Mysql_users
mysql_users =
(
{ username = "nextcloud" , password = "nextcloudpwd" , default_hostgroup = 10 , active = 1 }
)
Les utilisateurs MariaDB doivent être renseignés ici !
Dans mon cas, profitez de la configuration sécurisée de mon utilisateur nextcloud. Il sera assigné à un groupe d'utilisateur par défaut : le groupe des writer, et sera activé. Il est possible qu'un besoin particulier soit à l'origine de la création de la variable "active" mais j'ai du mal à comprendre pourquoi un compte MariaDB resterait renseigné avec active = 0 plutôt que dégagé. M'enfin.
Mysql_query_rules
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
}
Là, c'est la définition des règles qui seront automatiquement appliquées pour chaque serveur. La plus claire est la règle à l'ID 200 qui est assignée au hostgroup 20, celui des reader. C'est là que ProxySQL va faire un tri. Le hostgroup 20 n'ayant que la possibilité de faire des SELECT, on comprend bien qu'on parle de lecteurs uniquement.
ConfigMap
On charge tout ça dans une ConfigMap :
dada@master:~/proxysql$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
Si on prend le temps de revenir sur le YAML pour comprendre la ConfigMap, on la repère ici :
containers:
[...]
volumeMounts:
- name: proxysql-config
mountPath: /etc/proxysql.cnf
subPath: proxysql.cnf
[...]
volumes:
- name: proxysql-config
configMap:
name: proxysql-configmap
On comprend que les pods ProxySQL vont aller parcourir la liste des ConfigMaps disponibles pour repérer celle qui porte le nom "proxysql-config" et la monter dans /etc/proxysql.cnf.
Vérifier tout le système
Une commande que vous devriez connaître par cœur va nous prouver que tout fonctionne :
dada@master:~/proxysql$ kubectl logs proxysql-5c47fb85fb-fdh4g
Chez moi, elle sort quelque chose comme ça :
2018-12-01 08:30:19 [INFO] Dumping mysql_servers
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+
| hostgroup_id | hostname | port | weight | status | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | mem_pointer |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+
| 10 | 192.168.0.17 | 3306 | 1 | 0 | 0 | 100 | 5 | 0 | 0 | | 140637072236416 |
| 20 | 192.168.0.17 | 3306 | 1 | 0 | 0 | 100 | 5 | 0 | 0 | | 140637022769408 |
| 20 | 192.168.0.77 | 3306 | 1 | 0 | 0 | 100 | 5 | 0 | 0 | | 140637085320960 |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+
On y retrouve la liste des serveurs et leurs rôles : mon master appartient aux groupes reader et writer. Normal puisqu'il doit écrire et lire. Mon slave, lui, n'appartient qu'au groupe des reader, comme convenu.
Le piège
Si j'ai pesté un certain temps contre ProxySQL sur Mastodon , c'est que ma configuration ne marchait pas alors que tout semblait normal. J'avais bien en tête ce que je viens de vous expliquer: Les reader, les writer, la configuration des users, etc. C'était clair.
Pourtant, quand je lancais mon conteneur Nextcloud pour qu'il s'installe comme un grand, il pété ma réplication en écrivant sur mon slave. Le con.
J'ai fini par ouvrir une issue pour crier à l'aide et on m'a expliqué ceci : ProxySQL vérifie la présence de la variable READ_ONLY sur tous les serveurs renseignés dans la rubrique mysql_replication_hostsgroups. Si la variable est a OFF sur votre slave, la réplication entre les deux serveurs MariaDB ne bronchera pas, mais ProxySQL ira taper dans votre slave, peu importe la configuration en place. Re le con.
Pour vous épargner des soucis, pensez bien à vérifier que votre slave affiche bien ceci :
MariaDB [(none)]> SHOW VARIABLES like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only | ON |
+---------------+-------+
1 row in set (0.00 sec)
Vous êtes bons. Vous pouvez maintenant aller configurer vos pods pour aller taper sur vos MariaDB via proxysql:6033 en place et lieu de localhost:3306.