MySQL/MariaDB: Ausfallsicheren Galera-Cluster erstellen
Eine eigene MySQL-Datenbank zu erstellen ist kein Problem. Aber der Stress beginnt dann, wenn Aliens auf das Rechenzentrum ballern und der Server ausfällt.
Während sich also die Welt darum kümmert, nicht von grünköpfigen Glubschaugen unterjocht zu werden, kämpft der Administrator an seiner eigenen Front: Den Datenbankserver wieder in die Gänge zu bekommen, damit die interstellare Abwehrarmee der Menschen ihre Dosenrationen online überwachen kann.
Das sind die wahren Helden!
Besser wäre es aber im vorhinein gewesen, wenn der Server ein Cluster gewesen wäre, also ein Netzwerk bestehend aus mindestens drei Servern. Fällt einer aus, sind mindestens zwei noch im Einsatz.
Doch Stop! MySQL kann das nicht von Haus aus. Stattdessen kann man hier das Pendant MariaDB einsetzen. Und das geht unter Ubuntu 18.0 so…
Zunächst mein Dank an howtoforge, deren Artikel die Grundlage für diesen hier bildet.
Los geht’s! Erstmal in der Linux-Shell das Paket installieren:
Ubuntu 18
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,i386,ppc64el] http://ftp.utexas.edu/mariadb/repo/10.4/ubuntu xenial main' sudo apt update sudo apt install mariadb-server rsync -y
Ubuntu 20
Achtung: Es ist wichtig, dass du MariaDB Version 10.5 auf Ubuntu 20 installierst; Galera startet sonst nicht.
sudo apt update && sudo apt upgrade sudo apt -y install software-properties-common sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc' sudo add-apt-repository 'deb [arch=amd64] http://mariadb.mirror.globo.tech/repo/10.5/ubuntu focal main' sudo apt update sudo apt install mariadb-server mariadb-client rsync -y
Als nächstes: Den Server konfigurieren und installieren. Das geht am Besten über das mitgelieferte Tool.
sudo mysql_secure_installation
Easy Peasy! Nächster Schritt: Die Kommunikation zwischen den Servern im Cluster absichern. Je nachdem, wie diese verteilt sind, sollte die interne Absprache verschlüsselt sein. Das geht natürlich über SSL – die Abkürzung für SuperSichereVerschLüsselung.
Also als nächstes ein paar Verzeichnisse anlegen, in denen die Zertifikate abgelegt werden. Die werden später an die richtige Stelle kopiert.
cd ~ mkdir ssl cd ssl
Dann werden die Zertifikate generiert; ich höre jetzt schon einen Aufschrei. Seit Anbeginn der Menschheit versuchen Administratoren, die Generierung von SSL-Zertifikaten zu deuten und zu verstehen. Viele sind beim Versuch gestorben. Insofern: Einfach die Schritte ausführen und hoffen, das es schon so passt.
Achtung: Unbedingt bei der Abfrage auf den angegebenen “Common Name” achten! Die anderen Felder müssen nicht ausgefüllt werden.
# "Common Name" = 'db' openssl genrsa 2048 > ca-key.pem openssl req -new -x509 -nodes -days 21900 -key ca-key.pem > ca-cert.pem # "Common Name" = 'server' openssl req -newkey rsa:2048 -days 21900 -nodes -keyout server-key-pkcs8.pem > server-req.pem openssl rsa -in server-key-pkcs8.pem -out server-key.pem openssl x509 -req -in server-req.pem -days 21900 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > server-cert.pem # "Common Name" = "client" openssl req -newkey rsa:2048 -days 10000 -nodes -keyout client-key-pkcs8.pem > client-req.pem openssl rsa -in client-key-pkcs8.pem -out client-key.pem openssl x509 -req -in client-req.pem -days 21900 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > client-cert.pem
Die Zertifikatsdateien sind jetzt verfügbar. Aber nicht lesbar und für den falschen Nutzer – das müssen wir ändern.
sudo mkdir /var/ssl sudo mkdir /var/ssl/mysql sudo cp *.pem /var/ssl/mysql # Benutzer außerhalb MySQL können es lesen (damit du dich lokal verbinden kannst) sudo chmod 664 /var/ssl/mysql/* sudo chmod 775 /var/ssl/mysql/ sudo chown -R mysql:mysql /var/ssl/mysql
Fertig. Hinweis: Du kannst die Zertifikatsdateien auch mit 500 für das Verzeichnis und 400 für Dateien schützen. Dann hat nur der mysql-User Zugriff, aber du kannst dich nicht lokal verbinden. In dem Fall setze einfach andere Rechte für die Client-Zertifikate. Jetzt müssen wir noch der Konfiguration von MariaDB mitteilen, dass wir SSL nutzen wollen.
Dafür wird die Datei /etc/mysql/conf.d/galera.cnf
(Ubuntu 18, MariaDB 10.3) oder /etc/mysql/mariadb.conf.d/60-galera.cnf
(Ubuntu 20, MariaDB 10.5) editiert. Achtung: Hier sollten später die IPs der anderen Server eingetragen werden, die im Cluster aktiv sind. Momentan reicht die des ersten Servers.
Achtung: Unter bind-address
wird eingestellt, dass der Server von außen erreichbar ist. Wenn du das nicht möchtest, kannst du die IP anpassen. Generell kann man den Zugriff aber dann auch über eine Firewall regeln.
[mysql] # Using mysql in case this is used ssl=true ssl-ca=/var/ssl/mysql/ca-cert.pem ssl-cert=/var/ssl/mysql/client-cert.pem ssl-key=/var/ssl/mysql/client-key.pem [client] # Also client to be sure ssl=true ssl-ca=/var/ssl/mysql/ca-cert.pem ssl-cert=/var/ssl/mysql/client-cert.pem ssl-key=/var/ssl/mysql/client-key.pem [mysqld] ssl=true ssl-ca=/var/ssl/mysql/ca-cert.pem ssl-cert=/var/ssl/mysql/server-cert.pem ssl-key=/var/ssl/mysql/server-key.pem [galera] binlog_format=ROW default-storage-engine=innodb innodb_autoinc_lock_mode=2 bind-address=0.0.0.0 # Galera Provider Configuration wsrep_on=ON wsrep_provider=/usr/lib/galera/libgalera_smm.so wsrep_cluster_name="galera_cluster" # Art der Synchronisation wsrep_sst_method = mariabackup # Achtung: Hier IP ersetzen! wsrep_cluster_address="gcomm://<SERVERIP 1>" wsrep_sst_method=rsync wsrep_provider_options="socket.ssl_key=/var/ssl/mysql/server-key.pem;socket.ssl_cert=/var/ssl/mysql/server-cert.pem;socket.ssl_ca=/var/ssl/mysql/ca-cert.pem" # Galera Node Configuration # Achtung: Hier IP ersetzen! wsrep_node_address="<SERVERIP 1>" wsrep_node_name="MariaDB1"
Wichtiger Hinweis: Die Nodes synchronisieren sich per Default mit der “rsync” Methode. Der Nachteil ist, dass während dieser Phase alle Nodes nicht erreichbar sind! Stattdessen solltest du “mariabackup” nutzen.
Jetzt wieder in die Shell wechseln und den neuen Galera Cluster starten. Zunächst muss aber der Service gestoppt werden.
sudo systemctl stop mysqld sudo galera_new_cluster
Gespannt ob es geklappt hat? Mal schnell über die Shell in die Datenbank schauen. Du wirst übrigens automatisch mit den SSL-Zertifikaten verbunden, weil das in der Galera-Config schon im [client] Bereich aktiviert ist.
mysql -u root -p -e "show status like 'wsrep_cluster_size'"
Als Antwort sollte jetzt “1” erscheinen.
Das war’s auch schon. Du hast jetzt einen Server-Cluster mit einem Server. Das ungefähr so sinnvoll wie eine 1-Mann-Armee. Aber dazu gleich mehr – erstmal versuchen wir, auf den Server zu connecten. Dazu musst du sicherstellen, dass die Config auf bind-address = 0.0.0.0
gesetzt ist.
Wenn du einen Client wie Workbench oder Navicat benutzt, achte darauf, dass “SSL” aktiviert ist. Die Zertifikate musst du nicht einfügen.
Solltes du komische Fehlermeldungen wie mariadb system error: 0 "Internal error/check"
erhalten, bedeutet das, dass dein MySQL root
User falsch konfiguriert ist. Um das zu beheben, wird diesem Zugriff von außen ermöglicht:
# Auf MySQL Client wechseln mysql -u root -p # Rechte für Root vergeben GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '<PASSWORT>' WITH GRANT OPTION; FLUSH PRIVILEGES;
Wenn du jetzt den Server auf Reboot-Sicherheit testen möchtest, muss ich dich enttäuschen: Da dies der einzige Server im Cluster ist, muss er wieder mit galera_new_cluster
gestartet werden. Das ist natürlich nicht nötig, wenn es mehrere gibt!
Um einen Cluster zu starten, sollte der Server noch zweimal geklont werden. Dann nicht vergessen, bei allen Servern die Datei /etc/mysql/conf.d/galera.cnf
anzupassen. Dadurch, dass die Server geklont werden, sind die SSL-Keys bereits an Ort und Stelle.
# Achtung: Hier IPs aller Server ersetzen! wsrep_cluster_address="gcomm://<SERVERIP 1>,<SERVERIP 2>,<SERVERIP 3>" # Achtung: Hier IP ersetzen! wsrep_node_address="<SERVERIP>" wsrep_node_name="MariaDB<ServerNummer>"
Anschließend die MySQL-Services auf allen Servern ausschalten:
sudo systemctl stop mysqld
Einmal ein Stoßgebet zum Servergott senden und auf dem ersten Server den Cluster starten:
sudo galera_new_cluster
Wenn das geklappt hat, die Services auf den anderen Servern starten:
sudo systemctl start mysqld
Fertig!
MariaDB wieder komplett entfernen
Manchmal kommt es vor, dass du z.B. die falsche Version installierst, oder dich irgendwas anderes nervt. Um ALLES zu löschen, kannst du folgende Befehle ausführen. Es müssen nicht unbedingt alle Pakete vorhanden sein!
sudo apt-get purge mariadb-* -y sudo apt-get remove --purge mysql* -y sudo apt-get purge mysql* -y sudo apt-get autoremove -y sudo apt-get autoclean sudo apt-get remove dbconfig-mysql # Anschließend noch vorhandene Dateien löschen rm -r /etc/mysql/*
Bonus: Datenbank mit User erstellen
Da man ja eigentlich den Root-User nicht remote aktivieren sollte, hier mal schnell die Befehle, um eine Datenbank inkl. User zu erstellen:
# Lokal einloggen mysql -u root -p # Datenbank anlegen CREATE DATABASE testdb; # Admin-User für diese DB anlegen (kann auch von außen erreicht werden) CREATE USER 'testdb-admin'@'%' IDENTIFIED BY '<password>'; GRANT ALL PRIVILEGES ON testdb.* TO 'testdb-admin'@'%'; GRANT GRANT OPTION ON testdb.* TO 'testdb-admin'@'%'; FLUSH PRIVILEGES;
Wenn du einen globalen User anlegen möchtest, ändere das testdb.*
in *.*
um.
Data-Verzeichnis verschlüsseln
Gemäß der neuen DSGVO (aber auch logisch schon vorher) solltest du die Daten deiner Datenbank so verschlüsseln, dass ein einfacher Diebstahl des Servers nicht den Zugriff auf alle Daten erlaubt. Ein Weg ist, das Data-Verzeichnis per ecryptfs zu verschlüsseln.
Dazu installieren wir ecryptfs, legen ein neues Verzeichnis an, verschlüsseln dieses und kopieren die MySQL-Daten.
# Galera/DB-Service stoppen sudo systemctl stop mysqld # Verhindern, dass der Dienst beim Booten gestartet wird, da wir zunächst manuell das Verzeichnis mounten müssen. sudo systemctl disable mysql # ecryptfs installieren sudo apt -y install ecryptfs-utils # Daten-Verzeichnis beim Galera-Cluster anpassen sudo mcedit /etc/mysql/mariadb.conf.d/60-galera.cnf
Anpassung der Config:
[mysqld] datadir = /var/lib/mysql-enc
Zurück in der Shell:
# Verzeichnis anlegen, das verschlüsselt wird sudo mkdir /var/lib/mysql-enc # MySQL-User zugreifen lassen sudo chown mysql:mysql /var/lib/mysql-enc # Verzeichnis verschlüsseln sudo mount -t ecryptfs /var/lib/mysql-enc /var/lib/mysql-enc -o ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=no,ecryptfs_enable_filename_crypto=no # Vorhandene MySQL-Daten kopieren und User anpassen sudo cp /var/lib/mysql/* /var/lib/mysql-enc -R sudo chown mysql:mysql /var/lib/mysql-enc/* -R # Cluster starten sudo galera_new_cluster
Das Verzeichnis sollte jetzt verschlüsselt sein. So kannst du es unmounten:
sudo umount /var/lib/mysql-enc
Wird der Server neu gestartet, musst du zunächst das Verzeichnis mounten, da es sonst Fehler gibt. ecryptfs verschlüsselt in dieser Einstellung nicht die Dateinamen, so dass Galera meckert!
sudo mount -t ecryptfs /var/lib/mysql-enc /var/lib/mysql-enc -o ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=no,ecryptfs_enable_filename_crypto=no # Beim Neustarten des Clusters (oder nur ein Server) sudo galera_new_cluster # Beim Neustarten einer Node eines Clusters sudo systemctl start mysql
Wie performt das Ganze?
Zeit für ein paar Tests! In meinem Fall habe ich drei MariaDB-Server zu einem Galera-Cluster zusammengefasst. Die Server stehen in unterschiedlichen Rechenzentrum (aber am gleichen Ort) und sind per SSL-Verschlüsselung miteinander verbunden.
Als nächstes habe ich eine SQL-Datei erstellt, die superduper viele Inserts enthält:
Die Idee ist, diese Datei per mysql-Shell Befehl an alle drei Server gleichzeitig zu senden. Fangen wir an mit Server 1:
Ca. 160 Writes pro Sekunde. Was passiert, wenn wir das gleiche parallel auf Server 2 machen?
Uh! Fast doppelt soviel! Und jetzt das ganze noch auf Server 3:
Ca. 390 Writes – das scheint das Maximum zu sein. Und wie hoch ist die Query-Time eines einfachen SQL-Statements, wenn alle Server ihren Import gleichzeitig durchführen?
Das ändert sich übrigens nicht, wenn der Import nicht aktiv ist.
Optimierungen, die man vornehmen kann
- Setze
innodb_buffer_pool_size
auf 80% des verfügbaren RAMs oder berechne es genauer. - Setze
innodb_buffer_pool_instances
auf die Anzahl deiner CPU-Cores. Dabei solltest du aberinnodb_buffer_pool_chunk_size
so einstellen, dassinstances * chunk_size = pool_size
ist, denn sonst ist die Größe des Buffer-Pools irrelevant. - Setze
wsrep_slave_threads
auf mindestens 2*CPU-Cores. - Den MySQLTuner laufen lassen. Der gibt viele wichtige Hinweise!
31. Januar 2020
Alle meine Artikel entstehen mit bestem Wissen und Gewissen, sind aber nicht perfekt und sollten immer nur als Ausgangspunkt für deine eigenen Recherchen bilden.
Sollte dir etwas Fehlerhaftes auffallen, freue ich mich über deine Nachricht!