You are here
MaxScale Konfigurations-Synchronisation
Inhaltsverzeichnis
- Übersicht
- Vorbereitungen
- MaxScale Konfigurations-Synchronisation aktivieren
- MaxScale Parameter ändern
- Neuer Slave hinzufügen und MaxScale bekannt machen
- Alter Slave entfernen und MaxScale bekannt machen
- Wie wird die Konfiguration synchronisiert?
- Was passiert im Konfliktfall?
- Tests
- MaxScale Konfigurations-Synchronisation wieder deaktivieren
- Literatur/Quellen
Übersicht
Ein Feature, welches ich beim Stöbern kürzlich entdecke habe, ist die MaxScale Konfigurations-Synchronisation Funktionalität.
Es geht hier nicht primär um einen MariaDB Replikations-Cluster oder einen MariaDB Galera Cluster sondern um einen Cluster bestehend aus zwei oder mehreren MaxScale Knoten. Bzw. etwas genauer gesagt, den Austausch der Konfiguration unter diesen MaxScale Knoten.
Pon Suresh Pandian hat bereits 2022 einen Blog-Artikel über dieses Feature geschrieben, der noch etwas ausführlicher ist, als dieser Beitrag hier.
Vorbereitungen
Es wurde eine Incus Container-Umgebung vorbereitet, bestehend aus 3 Datenbank-Containern (deb12-n1 (10.139.158.33), deb12-n2 (10.139.158.178), deb12-n3(10.139.158.39)) und 2 MaxScale Containern (deb12-mxs1 (10.139.158.66), deb12-mxs2(10.139.158.174)). Die Datenbankversion ist eine MariaDB 10.11.6 aus dem Debian-Repository und MaxScale wurde in der Version 22.08.5 von der MariaDB plc Website heruntergeladen.
Die Datenbank-Konfiguration sieht für alle 3 Knoten analog aus:
# # /etc/mysql/mariadb.conf.d/99-fromdual.cnf # [server] server_id = 1 log_bin = deb12-n1-binlog binlog_format = row bind_address = * proxy_protocol_networks = ::1, 10.139.158.0/24, localhost gtid_strict_mode = on log_slave_updates = on skip_name_resolve = on
Die MaxScale Knoten wurden wie im Artikel Sharding mit MariaDB MaxScale beschrieben gebaut.
Der maxscale_admin
User hat genau die selben Rechte wie dort beschrieben, der maxscale_monitor
User hat folgende Rechte erhalten:
RELOAD, SUPER, REPLICATION SLAVE, READ_ONLY ADMIN
Siehe auch hier: Required Grants.
Die MaxScale Start-Konfiguration sieht wie folgt aus:
# # /etc/maxscale.cnf # [maxscale] threads = auto admin_gui = false [deb12-n1] type = server address = 10.139.158.33 port = 3306 proxy_protocol = true [deb12-n2] type = server address = 10.139.158.178 port = 3306 proxy_protocol = true [Replication-Monitor] type = monitor module = mariadbmon servers = deb12-n1,deb12-n2 user = maxscale_monitor password = secret monitor_interval = 500ms auto_failover = true auto_rejoin = true enforce_read_only_slaves = true replication_user = replication replication_password = secret cooperative_monitoring_locks = majority_of_running [WriteListener] type = listener service = WriteService port = 3306 [WriteService] type = service router = readwritesplit servers = deb12-n1,deb12-n2 user = maxscale_admin password = secret transaction_replay = true transaction_replay_timeout = 30s
Wichtig: Die Konfiguration sollte auf allen MaxScale Knoten gleich aussehen!
Und dann noch ein paar Checks um sicher zu sein, dass alles stimmt:
shell> maxctrl list listeners ┌───────────────┬──────┬──────┬─────────┬──────────────┐ │ Name │ Port │ Host │ State │ Service │ ├───────────────┼──────┼──────┼─────────┼──────────────┤ │ WriteListener │ 3306 │ :: │ Running │ WriteService │ └───────────────┴──────┴──────┴─────────┴──────────────┘ shell> maxctrl list services ┌──────────────┬────────────────┬─────────────┬───────────────────┬────────────────────┐ │ Service │ Router │ Connections │ Total Connections │ Targets │ ├──────────────┼────────────────┼─────────────┼───────────────────┼────────────────────┤ │ WriteService │ readwritesplit │ 0 │ 0 │ deb12-n1, deb12-n2 │ └──────────────┴────────────────┴─────────────┴───────────────────┴────────────────────┘ shell> maxctrl list servers ┌──────────┬────────────────┬──────┬─────────────┬─────────────────┬────────┬─────────────────────┐ │ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────────┤ │ deb12-n1 │ 10.139.158.33 │ 3306 │ 0 │ Master, Running │ 0-1-19 │ Replication-Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────┼─────────────────────┤ │ deb12-n2 │ 10.139.158.178 │ 3306 │ 0 │ Slave, Running │ 0-1-19 │ Replication-Monitor │ └──────────┴────────────────┴──────┴─────────────┴─────────────────┴────────┴─────────────────────┘ SQL> SELECT @@hostname, test.* FROM test.test; +------------+----+-----------+---------------------+ | @@hostname | id | data | ts | +------------+----+-----------+---------------------+ | deb12-n2 | 1 | Some data | 2024-03-26 09:40:21 | +------------+----+-----------+---------------------+ SQL> SELECT @@hostname, test.* FROM test.test FOR UPDATE; +------------+----+-----------+---------------------+ | @@hostname | id | data | ts | +------------+----+-----------+---------------------+ | deb12-n1 | 1 | Some data | 2024-03-26 09:40:21 | +------------+----+-----------+---------------------+
Und noch ein weiterer Test ob MaxScale den Failover auch wirklich richtig ausführt:
shell> systemctl stop mariadb 2024-03-26 16:27:05 error : Monitor was unable to connect to server deb12-n2[10.139.158.178:3306] : 'Can't connect to server on '10.139.158.178' (115)' 2024-03-26 16:27:05 notice : Server changed state: deb12-n2[10.139.158.178:3306]: master_down. [Master, Running] -> [Down] 2024-03-26 16:27:05 warning: [mariadbmon] Primary has failed. If primary does not return in 4 monitor tick(s), failover begins. 2024-03-26 16:27:07 notice : [mariadbmon] Selecting a server to promote and replace 'deb12-n2'. Candidates are: 'deb12-n1'. 2024-03-26 16:27:07 notice : [mariadbmon] Selected 'deb12-n1'. 2024-03-26 16:27:07 notice : [mariadbmon] Performing automatic failover to replace failed primary 'deb12-n2'. 2024-03-26 16:27:07 notice : [mariadbmon] Failover 'deb12-n2' -> 'deb12-n1' performed. 2024-03-26 16:27:07 notice : Server changed state: deb12-n1[10.139.158.33:3306]: new_master. [Slave, Running] -> [Master, Running] shell> systemctl start mariadb 2024-03-26 16:28:03 notice : Server changed state: deb12-n2[10.139.158.178:3306]: server_up. [Down] -> [Running] 2024-03-26 16:28:03 notice : [mariadbmon] Directing standalone server 'deb12-n2' to replicate from 'deb12-n1'. 2024-03-26 16:28:03 notice : [mariadbmon] Replica connection from deb12-n2 to [10.139.158.33]:3306 created and started. 2024-03-26 16:28:03 notice : [mariadbmon] 1 server(s) redirected or rejoined the cluster. 2024-03-26 16:28:03 notice : Server changed state: deb12-n2[10.139.158.178:3306]: new_slave. [Running] -> [Slave, Running]
Welcher MaxScale Knoten gerade für das Monitoring und Failover zuständig ist (cooperatve_monitoring
) kann wir folgt eruiert werden:
shell> maxctrl show monitor Replication-Monitor | grep -e 'Diagnostics' -e '"primary"' -e 'lock_held' | uniq │ Monitor Diagnostics │ { │ │ │ "primary": true, │ │ │ "lock_held": true, │
Es sollte sichergestellt sein, das bis hier hin alles sauber funktioniert. Ansonsten haben die weiteren Schritte keinen wirklichen Sinn.
MaxScale Konfigurations-Synchronisation aktivieren
Für die Konfigurations-Synchronisation braucht es einen eigenen Datenbank-User mit folgenden Rechten:
SQL> CREATE USER 'maxscale_confsync'@'%' IDENTIFIED BY 'secret'; SQL> GRANT SELECT, INSERT, UPDATE, CREATE ON `mysql`.`maxscale_config` TO maxscale_confsync@'%';
Anschliessend muss MaxScale entsprechend konfiguriert werden (auf beiden MaxScale Knoten), damit die Konfigurations-Synchronisation aktiviert wird. Diese Konfiguration erfolgt im globalen MaxScale Abschnitt:
# # /etc/maxscale.cnf # [maxscale] config_sync_cluster = Replication-Monitor config_sync_user = maxscale_confsync config_sync_password = secret
Dann erfolgt der Neustart der MaxScale Knoten:
shell> systemctl restart maxscale
Die MaxScale Konfigurations-Synchronisation kann auch dynamisch aktiviert und deaktiviert werden:
shell> maxctrl show maxscale | grep config_sync │ │ "config_sync_cluster": null, │ │ │ "config_sync_db": "mysql", │ │ │ "config_sync_interval": "5000ms", │ │ │ "config_sync_password": null, │ │ │ "config_sync_timeout": "10000ms", │ │ │ "config_sync_user": null, │
Hier ist wichtig, die richtige Reihenfolge der 3 Befehle einzuhalten, da es sonst einen Fehler gibt:
shell> MAXCTRL_WARNINGS=0 maxctrl alter maxscale config_sync_user='maxscale_confsync' shell> MAXCTRL_WARNINGS=0 maxctrl alter maxscale config_sync_password='secret' shell> MAXCTRL_WARNINGS=0 maxctrl alter maxscale config_sync_cluster='Replication-Monitor'
MaxScale Parameter ändern
Als ersten Test haben wir die MaxScale Monitor Variable monitor_interval
ins Auge gefasst, die in diesem Fall auf beiden MaxScale-Knoten sogar unterschiedlich ist:
shell> maxctrl show monitor Replication-Monitor | grep monitor_interval │ │ "monitor_interval": "750ms", shell> maxctrl show monitor Replication-Monitor | grep monitor_interval │ │ "monitor_interval": "1000ms",
Mit dem alter monitor
Befehl kann die Variable jetzt auf einem MaxScale Knoten gesetzt werden:
shell> MAXCTRL_WARNINGS=0 maxctrl alter monitor Replication-Monitor monitor_interval=500ms OK
was einerseits im MaxScale Error Log ersichtlich ist:
2024-03-26 14:09:16 notice : (ConfigManager); Updating to configuration version 1
anderseits sollte der Wert innerhalb von 5 Sekunden (config_sync_interval
) auf den zweiten MaxScale Knoten propagiert werden, was mit dem obigen Befehl überprüft werden kann.
Neuer Slave hinzufügen und MaxScale bekannt machen
Ein neuer Slave (deb12-n3) wird zuerst erstellt und dem MariaDB Replikations-Cluster von Hand hinzugefügt. Anschliessen wird der Slave einem MaxScale Knoten bekannt gemacht:
shell> maxctrl create server deb12-n3 10.139.158.39 shell> MAXCTRL_WARNINGS=0 maxctrl link monitor Replication-Monitor deb12-n3 OK shell> MAXCTRL_WARNINGS=0 maxctrl link service WriteService deb12-n3 OK shell> maxctrl list servers ┌──────────┬────────────────┬──────┬─────────────┬─────────────────┬────────────┬─────────────────────┐ │ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────────┼─────────────────────┤ │ deb12-n1 │ 10.139.158.33 │ 3306 │ 3 │ Slave, Running │ 0-2-479618 │ Replication-Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────────┼─────────────────────┤ │ deb12-n2 │ 10.139.158.178 │ 3306 │ 3 │ Master, Running │ 0-2-479618 │ Replication-Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────────┼─────────────────────┤ │ deb12-n3 │ 10.139.158.39 │ 3306 │ 1 │ Slave, Running │ 0-2-479618 │ Replication-Monitor │ └──────────┴────────────────┴──────┴─────────────┴─────────────────┴────────────┴─────────────────────┘
Alter Slave entfernen und MaxScale bekannt machen
Bevor ein Slave gelöscht werden kann, sollte er bei einem MaxScale Knoten aus dem Replikations-Cluster entfernt werden:
shell> maxctrl destroy server deb12-n1 --force OK shell> maxctrl list servers ┌──────────┬────────────────┬──────┬─────────────┬─────────────────┬────────────┬─────────────────────┐ │ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────────┼─────────────────────┤ │ deb12-n2 │ 10.139.158.178 │ 3306 │ 3 │ Master, Running │ 0-2-493034 │ Replication-Monitor │ ├──────────┼────────────────┼──────┼─────────────┼─────────────────┼────────────┼─────────────────────┤ │ deb12-n3 │ 10.139.158.39 │ 3306 │ 1 │ Slave, Running │ 0-2-493032 │ Replication-Monitor │ └──────────┴────────────────┴──────┴─────────────┴─────────────────┴────────────┴─────────────────────┘
Anschliessend kann der Slave abgebaut werden.
Wie wird die Konfiguration synchronisiert?
Die Konfiguration der beiden MaxScale Knoten wird über die Datenbank synchronisiert, was ich persönlich als unglückliche Design-Entscheidung ansehe, da eine Konfigurationsänderung potentiell hackelt, wenn der Master kaputt geht oder Netzwerkprobleme zwischen den Datenbanknoten auftreten...
Die Konfiguration wird in der Tabelle mysql.maxscale_config
abgespeichert, welche wie folgt aussieht:
CREATE TABLE `maxscale_config` ( `cluster` varchar(256) NOT NULL, `version` bigint(20) NOT NULL, `config` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`config`)), `origin` varchar(254) NOT NULL, `nodes` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`nodes`)), PRIMARY KEY (`cluster`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
Diese Tabelle hat in etwa folgenden Inhalt:
SQL> SELECT cluster, version, CONCAT(SUBSTR(config, 1, 32), ' ... ', SUBSTR(config, -32)) AS config , origin, nodes FROM mysql.maxscale_config; +---------------------+---------+-----------------------------------------------------------------------+------------+------------------------------------------+ | cluster | version | config | origin | nodes | +---------------------+---------+-----------------------------------------------------------------------+------------+------------------------------------------+ | Replication-Monitor | 2 | {"config":[{"id":"deb12-n1","typ ... ter_name":"Replication-Monitor"} | deb12-mxs1 | {"deb12-mxs1": "OK", "deb12-mxs2": "OK"} | +---------------------+---------+-----------------------------------------------------------------------+------------+------------------------------------------+
Eine lokale Kopie liegt zur Sicherheit auf jedem Knoten vor:
shell> cut -b-32 /var/lib/maxscale/maxscale-config.json {"config":[{"id":"deb12-n2","typ
Was passiert im Konfliktfall?
Siehe hierzu auch: Error Handling in Configuration Synchronization
Wenn zeitgleich (innerhalb von config_sync_interval
?) auf zwei unterschiedlichen MaxScale Knoten die Konfiguration geändert wird erhalten wir folgende Fehlermeldung:
Error: Server at http://127.0.0.1:8989 responded with 400 Bad Request to `PATCH monitors/Replication-Monitor` { "errors": [ { "detail": "Cannot start configuration change: Configuration conflict detected: version stored in the cluster (3) is not the same as the local version (2), MaxScale is out of sync." } ] }
Folgender Befehl hilft allenfalls bei grösseren Störungen das Problem zu erkennen:
shell> maxctrl show maxscale | grep -A9 'Config Sync' │ Config Sync │ { │ │ │ "checksum": "0052fe6f775168bf00778abbe37775f6f642adc7", │ │ │ "nodes": { │ │ │ "deb12-mxs1": "OK", │ │ │ "deb12-mxs2": "OK" │ │ │ }, │ │ │ "origin": "deb12-mxs2", │ │ │ "status": "OK", │ │ │ "version": 3 │ │ │ } │
Tests
Alle Tests wurden auch unter Last ausgeführt. Folgende Tests liefen parallel:
insert_test.php
insert_test.sh
mixed_test.php
while [ true ] ; do mariadb -s --user=app --host=10.139.158.174 --port=3306 --password=secret --execute='SELECT @@hostname, COUNT(*) FROM test.test GROUP BY @@hostname' ; sleep 0.5 ; done
while [ true ] ; do mariadb -s --user=app --host=10.139.158.174 --port=3306 --password=secret --execute='SELECT @@hostname, COUNT(*) FROM test.test GROUP BY @@hostname FOR UPDATE' ; sleep 0.5 ; done
Alle Tests sind bei allen Manipulationen einwandfrei und ohne Probleme durchgelaufen.
MaxScale Konfigurations-Synchronisation wieder deaktivieren
Auf beiden MaxScale Knoten ist folgender Befehl auszuführen und damit ist die Konfigurations-Synchronisation wieder beendet:
shell> MAXCTRL_WARNINGS=0 maxctrl alter maxscale config_sync_cluster=''
Literatur/Quellen
- MaxScale globale Konfigurationsvariable: config_sync_cluster
- MaxScale Configuration Synchronization
- Pon Suresh Pandian, MariaDB, 24. August 2022: MariaDB MaxScale 6.0 Native Clustering
- Where to get maxscale 6 mysql.maxscale_config table source sql
- Setting up MariaDB MaxScale
- Configuring the MariaDB Monitor
- MariaDB MaxScale Load Balancer with Master/Master Replication
- MariaDB Monitor - Configuration