Translate

понеділок, 9 грудня 2013 р.

MySQL/Galera replication with Haproxy balancing

MySQL/Galera - синхронний мульти-майстер кластер для MySQL/InnoDB бази данних, що володіє такими перевагами:
- синхронна реплікація.
- аctive-active мульти-майстер топологія
- читання чи запис на будь-яку ноду кластеру.
- автоматичний контроль членів кластеру: проблемні члени будуть автоматично виключені з діючого кластеру.
- автоматичне (просте) додавання додаткових серверів до кластеру. Зручне масштабування.
- справжня паралельна реплікація, на рівні рядків.

Додам, що звичайна реплікація типу master-master не є синхронною. На активному майстрі всі запити спочатку будуть записані в binlog і лише потім будуть передані на інший пасивний MySQL-майстер (слейв), запити на пасивному сервері можуть виконуватись із значною затримкою. У випадку із Galera запити на кожній ноді виконуються одночасно, або майже одночасно.

MySQL/Galera кластер використовує бібліотеку Galera для реалізації реплікації, що працює через MySQL wsrep API (патчена версія MySQL)



Як я вже згадував Galera допускає запис на будь-яку ноду кластеру, навіть одночасні записи із різних нод в одну таблицю. Все це завдяки алгоритму сертифікації транзакцій:
Логіка алгоритму сертифікації приблизно така: SQL-запити приходить на різні сервери кластеру і якщо вони конфліктують між собою (наприклад, запис в той самий рядок таблиці) відбувається роллбек і послідовне виконання запитів (виправте, якщо я помиляюсь).

Приведу приклад налаштування зв'язки haproxy+galera cluster.
Моє тестове середовище виглядає так:

Нода haproxy - це балансувальник, а gnode1, gnode2, gnode3 - ноди galera-кластеру.

Перш за все встановлюємо залежності для подальших установлень:

# apt-get install libaio1 libssl0.9.8 mysql-client libdbd-mysql-perl libdbi-perl
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  libmysqlclient18 libnet-daemon-perl libplrpc-perl libterm-readkey-perl mysql-client-5.5 mysql-client-core-5.5 mysql-common
The following NEW packages will be installed:
  libaio1 libdbd-mysql-perl libdbi-perl libmysqlclient18 libnet-daemon-perl libplrpc-perl libssl0.9.8 libterm-readkey-perl mysql-client mysql-client-5.5 mysql-client-core-5.5 mysql-common
0 upgraded, 12 newly installed, 0 to remove and 0 not upgraded.
Need to get 13.2 MB of archives.
After this operation, 46.7 MB of additional disk space will be used.
Do you want to continue [Y/n]? 

Завантажуємо і встановлюємо galera та патчений mysql із wresp api:

# wget https://launchpad.net/codership-mysql/5.6/5.6.14-25.1/+download/mysql-server-wsrep-5.6.14-25.1-amd64.deb
# wget https://launchpad.net/galera/3.x/25.3.1/+download/galera-25.3.1-amd64.deb

# dpkg -i galera-25.3.1-amd64.deb mysql-server-wsrep-5.6.14-25.1-amd64.deb

Для завантаження останніх версій чи версій для RedHat-based дистрибутивів варто скористатись цими посиланнями:

https://launchpad.net/galera
https://launchpad.net/codership-mysql
https://launchpad.net/codership-maria

Після установки треба створити директорію для логів (схоже, це баг скачаної версії від Сodership):

# mkdir -pv /var/log/mysql
# chown mysql:mysql -R /var/log/mysql

Ще необхідно перевірити опцію 'bind-address' на кожному інстансі mysql, і прибрати її чи дозволити всі конекти.

Все вищесказане необхідно повторити для всіх машин майбутнього galera-кластеру. 

Переходимо до конфігурації кластера. Правимо /etc/mysql/conf.d/wsrep.cnf на першому хосту gnode1 :

wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://"
wsrep_sst_method=rsync_wan
wsrep_node_address=192.168.1.51

wsrep_provider - адреса бібліотеки, що забезпечує логіку роботи кластеру.

wsrep_cluster_address - адреса кластеру. Для його запуску спочатку параметр необхідно встановити в "gcomm://", проте в перспективі його необхідно замінити на список серверів, що входять до кластеру, щось на кшталт "gcomm://node1,node2,node3".

wsrep_node_address - ip-адреса інтерфейсу, котрий має слухати інстанс. Якщо не задати цей параметр - буде слухатись перший мережевий інтерфейс.

wsrep_sst_method - спосіб, використовуючи який буде відбуватись копія даних, у випадку, коли буде підключено додатковий сервер кластеру чи після довгого даунтайму одного із існуючих серверів. wsrep_sst_method може бути реалізований за допомогою rsync/rsync_wan (найшвидший варіант, дефолтне значення параметру), mysqldump (найповільніший) і xtrabackup. У випадку використання останніх двох варіантів - слід вказади також логін/пароль доступу до бази (параметр wsrep_sst_auth). Значення "wsrep_sst_method=xtrabackup" радить Percona http://www.percona.com/doc/percona-xtradb-cluster/5.5/howtos/ubuntu_howto.html як варіант, що не блокує донора на відміну двох інших.

Також корисна опція, про яку необхідно знати - це wsrep_sst_donor=node_name/ip. Описує хост, що буде використовуватись задля повного копіювання стану кластера, інакше хост-донор буде обиратись випадковим чином.

Про інші параметри можна прочитати тут.

На gnode2 та gnode3 нодах кластеру /etc/mysql/conf.d/wsrep.cnf має виглядати відповідно наступним чином:

wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.1.51,192.168.1.52,192.168.1.53"
wsrep_sst_method=rsync_wan
wsrep_node_address=192.168.1.52

wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.1.51,192.168.1.52,192.168.1.53"
wsrep_sst_method=rsync_wan
wsrep_node_address=192.168.1.53

Перезапускаємо mysql на всіх нодах кластеру.

Нарешті на першому сервері кластеру gnode1 замінюємо значення параметру wsrep_cluster_address="gcomm://" на wsrep_cluster_address="gcomm://192.168.1.51,192.168.1.52,192.168.1.53" і знову перезапускаємо mysql. Не лишайте заданий параметр пустим, адже інакше після кожного рестарту буде заново ініціалізуватись кластер!

Деякі статті пропонують в wsrep_cluster_address описувати лише один IP, інші - всі IP серверів, що будуть входити до кластеру. Як я розумію, різниці в цьому немає.

Перевіряємо стан кластеру переглядом змінних в mysql:

Значення, на котрі перш за все необхідно звернути увагу відмічено червоним.

Тестуємо роботу кластеру: створимо базу та таблицю і заповнимо її даними:

mysql@gnode1> CREATE DATABASE `my_db`;
mysql@gnode2> use `my_db`;
mysql@gnode3> CREATE TABLE
`users` (
        `id` INT(11) NOT NULL AUTO_INCREMENT,
        `name` CHAR(30) NOT NULL,
        `age` SMALLINT(6) NOT NULL,
        PRIMARY KEY(`id`)
    );

mysql@gnode1> INSERT INTO my_db.users (`name`, `age`) VALUES
    ('John', 12),
    ('David', 18),
    ('Sasha', 16),
    ('Andrew', 20);

mysql@gnode2> SELECT * FROM my_db.users;
+----+--------+-----+
| id | name   | age |
+----+--------+-----+
|  2 | John   |  12 |
|  5 | David  |  18 |
|  8 | Sasha  |  16 |
| 11 | Andrew |  20 |
+----+--------+-----+
4 rows in set (0.00 sec)

Звертаю увагу, що mysql-інструкції для наявності були введені на різних серверах кластеру, що демонструє роботу реплікації.

Як я вже згадав, Galera дозволяє запис на будь-яку ноду кластеру причому навіть в одну таблицю, тому після його налаштування необхідно задуматись, як буде відбуватись балансування навантаження. Тут вже все залежить від файнтазії і потреб. Наприклад, я опишу варіант із Haproxy, проте можна також спробувати GLB (Galera Load Balancer daemon)  чи MySQL Proxy та ін.

Спочатку додамо скрипт, що перевірятиме стан ноди в кластері, на основі якого haproxy прийматиме рішення щодо балансування. Ідея скрипта взята звідси і заснована вона на перевірці стану ноди в кластері. Сервер кластеру буде вважатись готовим до з'єднань, якщо змінна wsrep_local_state буде мати значення 4 (Synced). Опис станів гарно зображений тут.

Встановимо суперсервер xinetd на кожну із нод кластеру:

# sudo aptitude install xinetd

І скопіюємо скрипт такого вигляду:

# vim /etc/xinetd.d/mysqlchk

# default: on
# description: mysqlchk
service mysqlchk
{
# this is a config for xinetd, place it in /etc/xinetd.d/
    disable = no
    flags = REUSE
    socket_type = stream
    port = 9200
    wait = no
    user = nobody
    server = /usr/bin/clustercheck
    log_on_failure += USERID
    only_from = 192.168.1.50
    # recommended to put the IPs that need
    # to connect exclusively (security purposes)
    per_source = UNLIMITED
}

192.168.1.50 - IP-адреса ноди із майбутньою інсталяцією Haproxy. Заразервуємо tcp-порт 9200 (або інший, якщо цей зайнятий):

# vim /etc/services

mysqlchk 9200/tcp # mysqlchk

xinetd при зверненні на порт 9200 буде викликати перевірку, що реалізується скриптом /usr/bin/clustercheck (звісно, що його необхідно попередньо створити):

# vim /usr/bin/clustercheck

#!/bin/bash

MYSQL_HOST=localhost
MYSQL_USERNAME=clustercheckuser
MYSQL_PASSWORD=clustercheckpassword!


WSSREP_STATUS=$(/usr/bin/mysql --host=$MYSQL_HOST --user=$MYSQL_USERNAME --password=$MYSQL_PASSWORD -e "show status like 'wsrep_local_state';" | awk '{if (NR!=1){print $2}}' 2>/dev/null)


# Check the galera cluster consistent on node, your solution still allow connect to node even if cluster is desynced but mysql hear on 3306 


if [ "$WSSREP_STATUS" == "4" ]
then
        # mysql is fine, return http 200 
        /bin/echo -e "HTTP/1.1 200 OK\r\n" 
        /bin/echo -e "Content-Type: Content-Type: text/plain\r\n" 
        /bin/echo -e "\r\n" 
        /bin/echo -e "MySQL is running.\r\n" 
        /bin/echo -e "\r\n" 
else
        # mysql is fine, return http 503 
        /bin/echo -e "HTTP/1.1 503 Service Unavailable\r\n" 
        /bin/echo -e "Content-Type: Content-Type: text/plain\r\n" 
        /bin/echo -e "\r\n" 
        /bin/echo -e "MySQL is *down*.\r\n" 
        /bin/echo -e "\r\n" 
fi

Додаємо права на запуск скрипту clustercheck створюємо додаткового read-only користувача для mysql та перезапускаємо xinetd:

# chmod +x /usr/bin/clustercheck

mysql> grant process on *.* to 'clustercheckuser'@'localhost' identified by 'clustercheckpassword!';

# service xinetd restart

Повторюсь, що додати вищезгаданий скрипт необхідно на кожну ноду galera-кластеру.

Наразі протестуємо роботу скрипту із майбутньої haproxy-ноди (ip ноди 192.168.1.50):

# telnet 192.168.1.53 9200
Trying 192.168.1.53...
Connected to 192.168.1.53.
Escape character is '^]'.
HTTP/1.1 200 OK

Content-Type: Content-Type: text/plain

MySQL is running.

Connection closed by foreign host.

Все добре, отже скрипт було додано вірно.

Налаштовуємо сам Haproxy. Встановлюємо його:

# apt-get install haproxy

Створюємо новий конфігураційний файл:

# mv /etc/haproxy/haproxy.cfg{,.original}

# vim /etc/haproxy/haproxy.cfg

global 
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 1024
        user haproxy
        group haproxy
        daemon
        #debug
        #quiet

defaults
        log     global
        mode    http 
        retries 3
        option redispatch dontlognull tcplog 
        maxconn 1024
        timeout connect 5000ms
        timeout client 50000ms
        timeout server 50000ms

listen mysql_cluster 0.0.0.0:3306
        mode tcp
        balance roundrobin
        option httpchk
        server gnode1 192.168.1.51:3306 check port 9200 inter 12000 rise 3 fall 3
        server gnode2 192.168.1.52:3306 check port 9200 inter 12000 rise 3 fall 3
        server gnode3 192.168.1.53:3306 check port 9200 inter 12000 rise 3 fall 3

roundrobin - один із найпростіших методів балансування і заключається він в тому, що кожен запит буде адресуватись кожній наступній ноді по порядку. Трішки більше про алгоритми балансування можна почитати тут.

Haproxy буде надсилати запит кожній із описаних нод, попередньо перевіривши http-код, який вона повертатиме на 9200 порту. Якщо код повернення буде 503 - нода буде виключена з пулу.

Перезапускаємо Haproxy і перевіряємо його роботу:

# mysql -uipeacocks-h192.168.1.50 -e "show variables like 'wsrep_node_name' ;"
+-----------------+--------+
| Variable_name   | Value  |
+-----------------+--------+
| wsrep_node_name | gnode1 |
+-----------------+--------+

# mysql -uipeacocks -h192.168.1.50 -e "show variables like 'wsrep_node_name' ;"
+-----------------+--------+
| Variable_name   | Value  |
+-----------------+--------+
| wsrep_node_name | gnode2 |
+-----------------+--------+

# mysql -uipeacocks -h192.168.1.50 -e "show variables like 'wsrep_node_name' ;"
+-----------------+--------+
| Variable_name   | Value  |
+-----------------+--------+
| wsrep_node_name | gnode3 |
+-----------------+--------+
root@haproxy:/etc/haproxy# 

Зміна значень wsrep_node_name вказує на правильну роботу балансування запитів.
Звісно, що не слід забувати, що будь-яка технологія має свої недоліки.

Посилання:

Haproxy
http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
https://www.digitalocean.com/community/articles/how-to-use-haproxy-to-set-up-http-load-balancing-on-an-ubuntu-vps
http://habrahabr.ru/post/198448/

Galera
http://www.codership.com/wiki/doku.php?id=galera_wiki
http://www.percona.com/doc/percona-xtradb-cluster/5.5/howtos/ubuntu_howto.html
http://www.percona.com/doc/percona-xtradb-cluster/5.5/index.html
http://www.mysqlperformanceblog.com/2012/06/20/percona-xtradb-cluster-reference-architecture-with-haproxy/
http://www.sebastien-han.fr/blog/2012/04/01/mysql-multi-master-replication-with-galera/
http://getasysadmin.com/2013/03/how-to-setup-galera-3-node-cluster-on-ubuntu-12-04/
http://www.slideshare.net/henrikingo/introduction-to-galera
http://www.slideshare.net/Severalnines/galera-cluster-for-mysql-introduction-slides
http://planet.mysql.com/entry/?id=282416

Немає коментарів:

Дописати коментар