Mysql : Maitre/esclave avec proxy

Mettre l'esclave au boulot ! Pour réaliser cet exploit il faut utiliser un "proxy intelligent" capable de distinguer les ordres en lecture seule (SELECT) et les autres (le SELECT ... FOR UPDATE) est un piège. 
Le proxy que j'ai choisi de tester s'appelle "ProxySql", nom très original s'il en est, mais il fonctionne fort bien et est assez simple à manipuler.

Le paquet est téléchargeable (V1.2.1 au moment de ces tests) à : 
https://github.com/sysown/proxysql/releases/download/v1.2.1/proxysql_1.2.1-debi… 
La liste complète des téléchargements est accessible à  l'URL: 
https://github.com/sysown/proxysql/releases/ 
L'installation se fait sans mystère par : 
dpkg -i proxysql_1.2.1-debian8_amd64.deb 
Un script d'init est installé ainsi qu'un fichier de configuration /etc/proxysql.cnf, un répertoire /var/lib/proxysql est créé, de la doc /usr/share/doc ... Tout a l'air présent ! 
Le service n'est pas démarré par défaut, après un "service proxysql initial" (pour la première fois) le service est lancé et écoute sur les ports 6032 et 6033 (paramètres par défaut). 
Ce paramètre "initial" permet d'initialiser la base de données interne de proxysql. 
Si vous n'avez pas personnalisé un minimum votre fichier "proxysql.cnf" vous pouvez le modifier et repasser un petit coup de "initial". Au passage renommer rapidement ce fichier sinon au prochain démarrage il sera réutilisé ! 
Proxysql utilise syslog et vous trouverez ses messages dans /var/log/daemon.log 
Par la suite on n'aura plus besoin de repasser le paramètre "initial", sauf pour réparer une "grosse bêtise". 
Testons maintenant la connexion à Proxysql (user/mot de passe fixés dans proxysql.cnf)  :

mysql -u admin -p  -h 127.0.0.1 -P 6032 
Enter password: admin 
Welcome to the MySQL monitor.  Commands end with ; or \g. 
Your MySQL connection id is 3 
Server version: 5.5.30 (ProxySQL Admin Module) 
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 
Oracle is a registered trademark of Oracle Corporation and/or its 
affiliates. Other names may be trademarks of their respective 
owners. 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 
mysql>

Cela a un petit air connu ... 
Remarque : la connexion par défaut est sur la base "main" :

mysql> show databases; 
+-----+---------+-------------------------------+ 
| seq | name    | file                          | 
+-----+---------+-------------------------------+ 
| 0   | main    |                               | 
| 2   | disk    | /var/lib/proxysql/proxysql.db | 
| 3   | stats   |                               | 
| 4   | monitor |                               | 
+-----+---------+-------------------------------+ 
4 rows in set (0,00 sec) 
mysql> use main; 
show tables; 
+--------------------------------------+ 
| tables                               | 
+--------------------------------------+ 
| global_variables                     | 
| mysql_collations                     | 
| mysql_query_rules                    | 
| mysql_replication_hostgroups         | 
| mysql_servers                        | 
| mysql_users                          | 
| runtime_global_variables             | 
| runtime_mysql_query_rules            | 
| runtime_mysql_replication_hostgroups | 
| runtime_mysql_servers                | 
| runtime_scheduler                    | 
| scheduler                            | 
+--------------------------------------+ 
mysql> use disk; 
show tables; 
+--------------------------------------+ 
| tables                               | 
+--------------------------------------+ 
| global_variables                     | 
| mysql_collations                     | 
| mysql_query_rules                    | 
| mysql_replication_hostgroups         | 
| mysql_servers                        | 
| mysql_users                          | 
| runtime_global_variables             | 
| runtime_mysql_query_rules            | 
| runtime_mysql_replication_hostgroups | 
| runtime_mysql_servers                | 
| runtime_scheduler                    | 
| scheduler                            | 
+--------------------------------------+ 
\q

Tiens ? Les mêmes tables sont présentes dans les bases "disk" et "main".

On peut "regarder" le contenu des tables par des ordres "SELECT", ajouter des choses par "INSERT", modifier par "UPDATE", et faire des "DELETE". La plupart des commandes de Mysql fonctionnent (sauf "desc" ?). 
Pour faire des modifications "rollbackables" il faut les faire au sein d'une transaction explicite (begin transaction;), le rollback sera ainsi possible. 
Le proxy dispose de deux règles par défaut "Select for update" et "select" qui envoyent respectivement sur les "hostgroups" 0 et 1, traditionnellement le "0" est le maître (celui qui peut effectuer des modifications de la base) et le groupe 1 celui des esclaves en lecture seule. 
On se dépêche de changer le user "admin:admin" en autre chose de plus sécurisé : 
update global_variables set variable_value = 'new_admin:admin_new' where variable_name = 'admin-admin_credentials'; 
Attention aux '-' , ":" et aux '_' !!! 
Vous avez certainement remarqué les noms des tables doublés entre : 
global_variables 
runtime_global_variables 
..... 
Après une modif dans les tables "sans préfixe" il faut les recopier dans les tables "runtime" puis les sauvegarder sur disque pour rendre les modifications permanentes. 
On peut ainsi préparer une nouvelle config dans les tables "sans préfixe" qui ne sont pas actives puis la basculer sur les tables "runtime" ce qui les rend actives. 
Cela se fait à l'aide de commandes "LOAD" et "SAVE". 
Dans notre cas de modfification de l'accès administrateur il faut : 
Le reporter dans le "runtime" 
LOAD MYSQL VARIABLES TO RUNTIME; 
et les sauvegarder pour la postérité : 
SAVE MYSQL VARIABLES TO DISK;

Dès que vous avez changé le user/mot de passe administrateur vous pouvez (devez) :

  • Détruire (ou renommer) le fichier /etc/proxysql.cnf
  • Redémarrer le service proxysql

Votre nouveau user/mot de passe sera alors actif.

Pour les tests le hostgroup 0 est une VM Mysql "master", le hostgroup 1 est une VM Mysql "slave" de la première qui ne recevra que les ordres "SELECT" purs exempts de toute clause "FOR UPDATE". 
On revient un peu en arrière et on va voir la config postée initialement dans le fichier "proxysql.cnf" pour nos serveurs Serveur1 (master) et Serveur2 (slave) :

mysql_servers = 
( 
   { address="mysql1" , port=3306 , hostgroup=0 , max_connections=10 }, 
   { address="mysql2" , port=3306 , hostgroup=1 , max_connections=10 } 
) 
mysql_users: 
( 
  { username = "test" , password = "test", default_hostgroup = 0, active = 1 } 
)

Si on regarde (avec notre nouvel "admin") l'état de la table mysql_servers :

select * main.from mysql_servers; 
*************************** 1. row *************************** 
       hostgroup_id: 0 
           hostname: mysql1 
               port: 3306 
             status: ONLINE 
             weight: 1 
        compression: 0 
    max_connections: 10 
max_replication_lag: 0 
            use_ssl: 0 
     max_latency_ms: 0 
*************************** 2. row *************************** 
       hostgroup_id: 1 
           hostname: mysql2 
               port: 3306 
             status: ONLINE 
             weight: 1 
        compression: 0 
    max_connections: 10 
max_replication_lag: 0 
            use_ssl: 0 
     max_latency_ms: 0 
2 rows in set (0,00 sec)

C'est bien l'état indiqué dans le fichier original ,vérifions maintenant nos règles de répartition :

mysql> select * from mysql_query_rules\G 
*************************** 1. row *************************** 
              rule_id: 1 
               active: 1 
             username: NULL 
           schemaname: NULL 
               flagIN: 0 
          client_addr: NULL 
           proxy_addr: NULL 
           proxy_port: NULL 
               digest: NULL 
         match_digest: NULL 
        match_pattern: ^SELECT .* FOR UPDATE$ 
 negate_match_pattern: 0 
              flagOUT: NULL 
      replace_pattern: NULL 
destination_hostgroup: 0 
            cache_ttl: NULL 
            reconnect: NULL 
              timeout: NULL 
              retries: NULL 
                delay: NULL 
       mirror_flagOUT: NULL 
     mirror_hostgroup: NULL 
            error_msg: NULL 
                  log: NULL 
                apply: 1 
*************************** 2. row *************************** 
              rule_id: 2 
               active: 1 
             username: NULL 
           schemaname: NULL 
               flagIN: 0 
          client_addr: NULL 
           proxy_addr: NULL 
           proxy_port: NULL 
               digest: NULL 
         match_digest: NULL 
        match_pattern: ^SELECT 
 negate_match_pattern: 0 
              flagOUT: NULL 
      replace_pattern: NULL 
destination_hostgroup: 1 
            cache_ttl: NULL 
            reconnect: NULL 
              timeout: NULL 
              retries: NULL 
                delay: NULL 
       mirror_flagOUT: NULL 
     mirror_hostgroup: NULL 
            error_msg: NULL 
                  log: NULL 
                apply: 1 
2 rows in set (0,00 sec)

C'est bien ce que nous voulions installer. 
Quand à notre user il semble lui aussi OK :

select * from MYSQL_USERS\G 
*************************** 1. row *************************** 
              username: test 
              password: test 
                active: 1 
               use_ssl: 0 
     default_hostgroup: 0 
        default_schema: 
         schema_locked: 0 
transaction_persistent: 0 
          fast_forward: 0 
               backend: 1 
              frontend: 1 
       max_connections: 10000


Tout cela a l'air bel et bon.


Site à voir : 
http://severalnines.com/blog/how-proxysql-adds-failover-and-query-control-your-mysql-replication-setup