A MySQL replikációs és clusterezési lehetőségeinek bemutatása 2.

Munkánk során gyakran kerülünk olyan helyzetbe, amikor a normál felhasználási szokásoknál alkalmazott módszerek nem működőképesek, nem nyújtanak megfelelő szolgáltatásokat. Ilyenkor kell sokkal hibatűrőbb, nagyobb rendelkezésre állású rendszereket terveznünk és készítenünk, amelyek egyik fontos építő eleme az adatokat kiszolgáló relációs adatbázis-kezelő. Ma már az ingyenes MySQL is hatékony replikációs és fürtözési technológiákat biztosít a rendszertervezők/fejlesztők felé, jelen cikkünkben ezek közül a fürtözéssel ismertetjük meg olvasóinkat.

MySQL Cluster

Bemutatás

A MySQL-ben használt klaszterezési technológiában három különálló infrastrukturális feladatot ellátó kiszolgálót különböztetünk meg:

  • Data node (Linux alatt a mysql-cluster-server-datanode csomag): feladata az adatbázisok tárolása, gondoskodik azok szinkronizációjáról, transzparens módon szolgálja ki az SQL node-okat, terheléselosztás révén gondoskodik a minél hatékonyabb működtetésről, valamint képes a leállást követő újraszinkronizációra.
  • SQL node (vagy application node) (Linux alatt a mysql-cluster-server-sqlnode csomag, de ez megfelel a mysql-server csomagnak): Az alkalmazások felől elérhető kiszolgáló(k). Ők kezelik a data node-okról érkező adatfolyamokat és szolgálják ki az egyes eléréseket.
  • Management node (Linux alatt a mysql-cluster-server-mgmtnode csomag): Konfigurációs felada-tok és monitorozás céljára szolgáló kiszolgáló(k). A klaszter indítása és leállítása során kiemelt szerepük van, addig nem csatlakoztatható egy gép data node-ként a klaszterbe, amíg legalább egy management node nem indult el.

MySQL-ben cluster csak NDB engine-el használható, amely jelenleg is számos megszorítást tartalmaz a többi népszerű adatbázis engine-hez képest, de ezek száma folyamatosan csökken.

A MySQL cluster lehetővé teszi, hogy a több data node-ból kialakított cluster-eket akár egymásra replikáljuk és az SQL node-ok számára transzparens módon szolgáltassunk a clusterek között váltogatva.

MySQL Cluster létrehozása

  • A létrehozandó cluster-ben két data node-lesz: db0 és db1.
  • A load balancer-ként funkcionáló dedikált kiszolgáló: mysql-proxy.
  • Mindkét node egyben management funkciókat is el fog látni. (Két management node-al nagyobb rendelkezésre állást tudunk megvalósítani, mivel bármelyik kiesése esetén a cluster továbbra is menedzselhető marad a másik által.)
  • Létrehozunk egy MySQL Proxy-n alapuló load balancer eljárást.
  • Végezetül felhasználói script-ekkel automatizáljuk a kiszolgálók elindulásának és a cluster felépítésének folyamatát.

Nem túl elterjedt, hogy egy kiszolgálón kerüljön kialakításra a management és a data node is, azonban a fenti konfigurációval (két data node, és mindkettő management is egyben) teljes értékű failover rendszer alakítható ki két kiszolgálóval is.

Előkészületek (ezt mindkét kiszolgálón meg kell tenni):

  • hozzunk létre egy új felhasználói csoportot a kiszolgálókat futtató dedikált felhasználók számára:
    groupadd mysql
  • hozzunk létre egy, a mysql kiszolgálókat futtató felhasználót az előbbi csoportban:
    useradd -g mysql mysql
  • állítsuk be a megfelelő jogosultságokat a cluster telepítése során beállított adatok tárolását végző jegyzékeken:
    chown mysql:root /opt/oracle/disk/mysql_cluster/mysqld_data
  • next-next-finish módon csomagból telepítsük fel a MySQL Cluster-t, FONTOS: a telepítés végén ne indítsuk el a mysql-t!

1, Mindkét kiszolgálón módosítsuk a /etc/mysql/my.cnf konfigurációs állományt a következő módon:

[mysqld] 
basedir=/usr/local/mysql
#az az adatkönyvtár amit a telepítés során megadtunk. 
datadir=/opt/oracle/disk/mysql_cluster/mysqld_data 
event_scheduler=on
#csak ndb engine-el működik a cluster
default-storage-engine=ndbcluster 
[ndbcluster] 
# a management node-ok nevei vagy ip címei felsorolva
ndb-connectstring=db0,db1     
key_buffer = 512M
key_buffer_size = 512M
sort_buffer_size = 512M
table_cache = 1024
read_buffer_size = 512M
[mysql_cluster] 
# a management node-ok nevei vagy ip címei felsorolva
ndb-connectstring=db0,db1

2, Adjunk futási jogosultságot az ndb_mgm eszköznek a db0 kiszolgálón:

cd /usr/local/mysql
chmod +x bin/ndb_mgm*

3, Hozzuk létre a cluster konfigurációs beállításait leíró állományt a db0 kiszolgálón:

mkdir /var/lib/mysql-cluster
vim /var/lib/mysql-cluster/config.ini

4, Állítsuk be az alábbi paramétereket a 3. lépésben létrehozott fájl-ban:

[NDBD DEFAULT] 
NoOfReplicas=2
DataDir=/var/lib/mysql-cluster
# fontos: induláskor az ndb ezt le is fogja foglalni! 
DataMemory=8G    
IndexMemory=4G
[MYSQLD DEFAULT] 
[NDB_MGMD DEFAULT] 
[TCP DEFAULT] 
[NDB_MGMD] 
HostName=db0    # az első management node neve vagy ip címe
NodeId=1        # az első management node azonosítója
[NDB_MGMD] 
HostName=db1    # a második management node neve vagy ip címe
NodeId=2        # a második management node azonosítója
[NDBD] 
HostName=db0    # az első data node neve vagy ip címe
NodeId=3        # az első data node azonosítója
[NDBD] 
HostName=db1    # a második data node neve vagy ip címe
NodeId=4        # a második data node azonosítója
[MYSQLD] 
[MYSQLD]

5, A management node ezzel beállítva, elindítható. FONTOS, hogy első és csak az első indításkor az –initial paraméterrel kell indítani a ndb_mgmd -t a db0 kiszolgálón:

ndb_mgmd -f /var/lib/mysql-cluster/config.ini
 --initial --config-dir=/var/lib/mysql-cluster/

Ha mindent jól csináltunk, akkor a „MySQL Cluster Management Server verziószám„ üzenetet kell megkapunk.

6, Ismételjük meg a 2.-től 5.-ig lépéseket a db1 kiszolgálón is.

7, Indítsuk le a db0 kiszolgálón a data node-okat is:

/usr/local/mysql/bin/ndbd --initial

Ha mindent jól csináltunk, az

[ndbd] INFO     -- Angel connected to 'localhost:1186'
[ndbd] INFO     -- Angel allocated nodeid: 3

üzenetet kell kapnunk.

8, Indítsuk el a db1 kiszolgálón is a data node-ot: ott értelem szerűen a nodeid: 4 üzenettel kell bejelentkeznie a data node-nak.

9, Indítsuk el mindkét kiszolgálón a mysql-t (az sql node-ot):

/etc/init.d/mysql.server start

10, Ahhoz, hogy a két clusteren külön-külön is tudjunk dolgozni, szükséges, hogy a mysql jogosultságai, felhasználói is NDB adatbázis engine-ben legyenek tárolva. Ehhez hajtsuk végre a következő módosításokat a db0 kiszolgálón:

use mysql; 
ALTER TABLE mysql.user ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.db ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.host ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.tables_priv ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.columns_priv ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.procs_priv ENGINE=NDBCLUSTER; 
ALTER TABLE mysql.proxies_priv ENGINE=NDBCLUSTER;

11, Még mindig a db0 kiszolgáló MySQL adatbázisában dolgozva állítsuk be az esemény vezérlőt:

SET GLOBAL event_scheduler=1; 
CREATE EVENT `mysql`.`flush_priv_tables` 
ON SCHEDULE EVERY 30 second 
ON COMPLETION PRESERVE DO FLUSH PRIVILEGES;

12, Mivel a mysql adatbázisban tárolt jogosultságok még nem szinkronizálódtak át a db1-re (nem is tudnak átszinkronizálódni, mivel nincs meg hozzá a megfelelő jogosultságuk), azokat kézzel kell átvinnünk:

root@db0~# /etc/init.d/mysql.server stop
root@db1~# /etc/init.d/mysql.server stop
root@db0~# scp -r mysqld_data/ db1:/opt/oracle/disk/mysql_cluster/
root@db0~# /etc/init.d/mysql.server start
root@db1~# /etc/init.d/mysql.server start

13, Ezek után ellenőrizzük le a cluster management-ben, hogy él a kapcsolat a node-ok között:

root@db0:~# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: db0:1186
Cluster Configuration
---------------------
 [ndbd(NDB)]     2 node(s) 
id=3    @ipcím  (verzió) 
id=4    @ipcím  (verzió) 
 [ndb_mgmd(MGM)] 2 node(s) 
id=1    @ipcím  (verzió) 
id=2    @ipcím  (verzió) 
 [mysqld(API)]   2 node(s) 
id=5    @ipcím  (verzió) 
id=6    @ipcím  (verzió)

A MySQL Cluster működésének tesztelése

Hozzunk létre pár adatot a db0-n:

root@db0:~# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g. 
Your MySQL connection id is 2
mysql> use test
Database changed
mysql> CREATE TABLE testdata (i INT) ENGINE=NDBCLUSTER; 
mysql> INSERT INTO testdata () VALUES (1);

Ellenőrizzük le a meglétüket a db1-en:

root@db1:~# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g. 
Your MySQL connection id is 3
mysql> use test
mysql> SELECT * FROM testdb; 
+------+
| i    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

Látható, hogy a két szerver szinkronizálta az adatokat egymás között.

Nézzük meg mi történik, ha leállítjuk a db0-s data node-ot:

root@db0:~# /etc/init.d/mysql.server stop
root@db0:~# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: db0:1186
Cluster Configuration
---------------------
 [ndbd(NDB)]     2 node(s) 
id=3    @ipcím  (verzió) 
id=4    @ipcím  (verzió) 
 [ndb_mgmd(MGM)] 2 node(s) 
id=1    @ipcím  (verzió) 
id=2    @ipcím  (verzió) 
 [mysqld(API)]   2 node(s) 
id=5 (not connected, accepting connect from any host) 
id=6    @ipcím  (verzió)

A leállított db0 data node mellett hozzunk létre új adatokat a db1-en:

root@db1:~# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g. 
Your MySQL connection id is 4
mysql> use test; 
mysql> INSERT INTO testdb () VALUES (99); 
mysql> INSERT INTO testdb () VALUES (999); 
mysql> INSERT INTO testdb () VALUES (100);

Indítsuk el a db0-n a data node-ot és kérdezzük le az adatokat:

root@db0:~# /etc/init.d/mysql.server start
root@db0:~# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g. 
Your MySQL connection id is 2
mysql> use test; 
mysql> SELECT * FROM testdb; 
+------+
| i    |
+------+
|   99 |
|  999 |
|    1 |
|  100 |
+------+
4 rows in set (0.00 sec)

Mint látható az indulást követően szinkronizálódtak az adatok a két node között.

Load balancer MySQL Proxy-val

1, Telepítsük fel a MySQL Proxy-t és hozzuk létre a konfigurációs állományát a mysql-proxy kiszolgálón:

root@mysql-proxy:~# apt-get install mysql-proxy
root@mysql-proxy:~# mkdir /etc/mysql-proxy
root@mysql-proxy:~# cd /etc/mysql-proxy
root@mysql-proxy:/etc/mysql-proxy# vim mysql-proxy.conf

2, Állítsuk be a létrehozott konfigurációs állományban a következőket:

[mysql-proxy] 
daemon = true
keepalive = true
# a proxy elérhetősége
proxy-address = mysql-proxy:3306
# master (tehát írható / olvasható) adatbázis(ok) data node-ja(i)
proxy-backend-addresses = db0:3306, db1:3306

3, Indítsuk el a MySQL Proxy-t:

root@mysql-proxy:~# <code>mysql-proxy</code>
 --defaults-file=/etc/mysql-proxy/mysql-proxy.conf

Indítás ütemezése, automatizálása

Mivel se az ndbb, se az ndb-mgmd nem indul el automatikusan, ráadásul a cluster data node-jai csak abban az esetben tudnak bejelentkezni a cluster-be, ha legalább egy management node-ot már látnak: szükséges ezek indítását automatizálni. Erre jó megoldás lehet az /etc/init.d/ jegyzékbe létrehozott indító script-ek használata. Fontos, hogy a következő fájl-ok létrehozása után futtatási jogot adjunk rájuk:

chmod +x /etc/init.d/<filename>

valamint az

update-rc.d <filename>

paranccsal érvénybe léptessük a változtatásokat.

Az ndbd indítására: /etc/init.d/ndbd

#!/bin/bash
# Provides:          ndbd
# Required-Start:    $local_fs $network $syslog $remote_fs
# Required-Stop:     $local_fs $network $syslog $remote_fs
# Short-Description: mysql cluster manager client
# Description:       mysql cluster manager client

ndbd_bin=/usr/local/mysql/bin/ndbd

if ! test -x $ndbd_bin; then
        echo "Can't execute $ndbd_bin";
        exit; 
fi

start_ndbd(){
        number_of_ndbd_pids=`ps aux|grep -iv "grep"|grep -i "/usr/local/mysql/bin/ndbd"|wc -l`
        if [ $number_of_ndbd_pids -eq 0 ]; then
                $ndbd_bin
                echo "ndbd started." 
        else
                echo "ndbd is already running." 
        fi
}

stop_ndbd(){
        number_of_ndbd_pids=`ps aux|grep -iv "grep"|grep -i "/usr/local/mysql/bin/ndbd"|wc -l`
        if [ $number_of_ndbd_pids -ne 0 ]; then
                ndbd_pids=`pgrep ndbd`
                for ndbd_pid in $(echo $ndbd_pids); do
                        kill $ndbd_pid 2> /dev/null
                done

                number_of_ndbd_pids=`ps aux|grep -iv "grep"|grep -i "/usr/local/mysql/bin/ndbd"|wc -l`

                if [ $number_of_ndbd_pids -eq 0 ]; then
                        echo "ndbd stopped." 
                else
                        echo "Could not stop ndbd." 
                fi
        else
                echo "ndbd is not running." 
        fi
}

case "$1" in
    'start' ) 
        start_ndbd
        ;; 
    'stop' ) 
        stop_ndbd
        ;; 
    'restart' ) 
        stop_ndbd
        start_ndbd
        ;; 
    *)
        echo "Usage: $0 {start|stop|restart}" >&2
        ;; 
esac

Az ndb-mgmd indítására: /etc/init.d/ndb-mgmd

#!/bin/bash
# Provides:          ndb_mgmd
# Required-Start:    $local_fs $network $syslog $remote_fs
# Required-Stop:     $local_fs $network $syslog $remote_fs
# Short-Description: mysql cluster manager
# Description:       mysql cluster manager

ndb_mgmd=/usr/local/mysql/bin/ndb_mgmd
config_file=/var/lib/mysql-cluster/config.ini
config_dir=/var/lib/mysql-cluster

if ! test -x $ndb_mgmd; then
        echo "Can't execute $ndb_mgmd"  
        exit;
fi

start_ndb_mgmd(){
        number_of_ndb_mgmd_pids=`ps aux|grep -iv "grep"|grep -i "$ndb_mgmd"|wc -l`
        if [ $number_of_ndb_mgmd_pids -eq 0 ]; then
                $ndb_mgmd -f $config_file --config-dir=$config_dir
                echo "ndb_mgmd started."
        else
                echo "ndb_mgmd is already running."
        fi
}

stop_ndb_mgmd(){
        number_of_ndb_mgmd_pids=`ps aux|grep -iv "grep"|grep -i "$ndb_mgmd"|wc -l`
        if [ $number_of_ndb_mgmd_pids -ne 0 ]; then
                ndb_mgmd_pids=`pgrep ndb_mgmd`
                for ndb_mgmd_pid in $(echo $ndb_mgmd_pids); do
                        kill $ndb_mgmd_pid 2> /dev/null
                done
                number_of_ndb_mgmd_pids=`ps aux|grep -iv "grep"|grep -i "$ndb_mgmd"|wc -l`
                if [ $number_of_ndb_mgmd_pids -eq 0 ]; then
                        echo "ndb_mgmd stopped."
                else
                        echo "Could not stop ndb_mgmd."
                fi
        else
                echo "ndb_mgmd is not running."
        fi
}

case "$1" in
    'start' )
        start_ndb_mgmd
        ;;
    'stop' )
        stop_ndb_mgmd
        ;;
    'restart' )
        stop_ndb_mgmd
        start_ndb_mgmd
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}" >&2
        ;;
esac

A mysql-proxy indítására: /etc/init.d/mysql-proxy

#! /bin/bash
# Required-Start:       $local_fs $network $syslog $remote_fs
# Required-Stop:        $local_fs $network $syslog $remote_fs
# Short-Description:    MySQL Proxy
# Description:          MySQL Proxy

mysql_proxy=/usr/bin/mysql-proxy
config_file=/etc/mysql-proxy/mysql-proxy.conf

if ! test -x $mysql_proxy; then
        echo "Can't execute $mysql_proxy"       
        exit;
fi

start_mysql_proxy(){
        number_of_mysql_proxy_pids=`ps aux|grep -iv "grep"|grep -i "/usr/bin/mysql-proxy"|wc -l`
        if [ $number_of_mysql_proxy_pids -eq 0 ]; then
                $mysql_proxy --defaults-file=$config_file
                echo "mysql-proxy started."
        else
                echo "mysql-proxy is already running."
        fi
}

stop_mysql_proxy(){
        number_of_mysql_proxy_pids=`ps aux|grep -iv "grep"|grep -i "/usr/bin/mysql-proxy"|wc -l`
        if [ $number_of_mysql_proxy_pids -ne 0 ]; then
                mysql_proxy_pids=`pgrep mysql-proxy`
                for mysql_proxy_pid in $(echo $mysql_proxy_pids); do
                        kill $mysql_proxy_pid 2> /dev/null
                done
                number_of_mysql_proxy_pids=`ps aux|grep -iv "grep"|grep -i "/usr/bin/mysql-proxy"|wc -l`
                if [ $number_of_mysql_proxy_pids -eq 0 ]; then
                        echo "mysql-proxy stopped."
                else
                        echo "Could not stop mysql-proxy."
                fi
        else
                echo "mysql-proxy is not running."
        fi
}

case "$1" in
    'start' )
        start_mysql_proxy
        ;;
    'stop' )
        stop_mysql_proxy
        ;;
    'restart' )
        stop_mysql_proxy
        start_mysql_proxy
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}" >&2
        ;;
esac