Отже, я почав впроваджувати в своїй організації Percona XtraDB Cluster - переводити бази даних зі звичайного MySQL сервера в кластерну архітектуру.
Коротко про завдання і ввідні дані
У кластері нам потрібно тримати:
- БД кількох веб-сайтів з користувачами
- БД зі статистичними даними цих користувачів
- БД для тікет-систем, систем управління проектами та інші дрібниці
Іншими словами, БД практично всіх наших проектів, з тих що крутяться у нас на MySQL, тепер повинні жити в кластері.
Більшість проектів ми тримаємо віддалено в ДЦ, тому і кластер буде знаходиться там.
Завдання рознести кластер географічно за різними дата-центрам не варто.
Для побудови кластера використовуються 3 сервера однаковою конфігурації: HP DL160 G6, 2X Xeon E5620, 24 GB RAM, 4x SAS 300GB в апаратній RAID 10. Непогане брендове залізо, яке я використовую вже давно і яке мене поки не підводило.
Чому Percona?
- синхронна true multi-master реплікація (Galera)
- можливість комерційної підтримки від Percona
- форк MySQL c значним списком оптимізацій
схема кластеру
У кластері 3 Ноди, для кожної вищеописаний фізичний сервер (OS Ubuntu 12.04).
Node A використовується в якості Reference (Backup) Node, що її будуть завантажувати запитами наші програми. При цьому вона буде повноправним членом кластера, і брати участь в реплікації. Це робиться у зв'язку з тим, що в разі збою в кластері або порушення цілісності даних ми матимемо ноду, яка майже напевно містить найбільш консистентні дані, які додатки просто не могли порушити через відсутність доступу. Можливо, це виглядає марнотратним витрачанням ресурсів, але для нас 99% надійність даних все ж важливіше ніж доступність 24/7. Саме цю ноду ми будемо використовувати для SST - State Snapshot Transfer - автоматичного зливу дампа на приєднувану до кластеру нову ноду або піднімається після збою. Крім того, Node A - відмінний кандидат для сервера, звідки ми будемо знімати стандартні періодичні резервні копії.
Схематично це все можна зобразити приблизно так:
Node B і Node C це робочі конячки, які тримають навантаження, але при цьому операції записи бере на себе тільки одна з них. Така рекомендація багатьох фахівців, і нижче я зупинюся на цьому питанні детально.
деталі балансування
Запити, які надходять на порт 3306. HAProxy розкидає по Round Robin між нодамі B і C.
Те, що приходить на 3307. проксіруется тільки на Node B. При цьому якщо Node B раптом впаде, запити перейдуть на зазначену в якості резервної Node C.
Для реалізації нашої ідеї (писати тільки на одну з нод) додатки повинні бути написані так, щоб запити на читання йшли через з'єднання з 10.0.0.70:3306 (10.0.0.70 - наш VIP), а запити на запис прямували на 10.0.0.70: 3307.
У нашому випадку це зажадає деякої роботи по створенню в конфіги PHP додатки нового конекту, і заміні імені змінної-DBHandler на інше значення. В цілому, це не так складно для тих додатків, які написані нами. Для сторонніх проектів, чиї бази теж будуть в кластері, ми просто вкажемо порт 3307 за замовчуванням. Навантаження ці проекти створюють невелику, і втрата можливості розподіленого читання не так критична.
Конфиг HAProxy (/etc/haproxy/haproxy.cfg):
Для того, щоб HAProxy міг визначити чи живий вузол кластера, використовується утиліта clustercheck. (Входить в пакет percona-xtradb-cluster), яка інформує вас про стан Ноди (Synced / Not Synced) у вигляді HTTP відповіді. На кожному з вузлів повинен бути налаштований xinetd сервіс:
конфігурація нод
В першу чергу, не забудьте синхронізувати час на всіх нодах. Я упустив цей момент, і досить довго не міг зрозуміти, чому у мене намертво підвисає SST - він починався, висів у процесах, але по факту нічого не відбувалося.
my.cnf на Node A (в моїх конфігах це node105):
Далі - тільки відрізняються параметри:
В останніх двох конфігах ми недвозначно повідомляємо серверу, де потрібно шукати першу ноду в кластері (яка знає де живуть всі члени групи), і що саме з неї, а не з іншого з доступних, потрібно забирати дані для синхронізації.
Саме на такій конфігурації я зупинився зараз, і збираюся поступово переводити проекти в кластер. Планую продовжити писати про свій досвід і далі.
Проблемні питання
Позначу тут питання, на які я далеко не відразу знайшов відповідь, але відповідь на які особливо важливий для розуміючи технології і правильної роботи з кластером.
Чому рекомендується писати на одну ноду з усіх доступних в кластері? Адже здавалося б, це суперечить ідеї мульти-майстер реплікації.
Мої власні тести показали, що при агресивній записи на всі ноди вони лягали одна за одною, залишаючи робочої тільки Reference Node, тобто за фактом можна сказати, що кластер припиняв роботу. Це, безумовно мінус такої конфігурації, адже третій вузол міг би в цьому випадку взяти навантаження на себе, але зате ми впевнені, що дані там в цілості й схоронності і в самому крайньому випадку ми можемо вручну запустити його в роботу в режимі одиночного сервера.
Для цього є 2 директиви:
Значення цих директив спочатку у мене викликали особливу плутанину.
Справа в тому, що багато мануали радили на першій ноді кластера залишати порожнє значення gcomm: // в wsrep_urls.
Виявилося, що це неправильно. Наявність gcomm: // означає ініціалізацію нового кластера. Тому відразу після старту першої Ноди в її конфіге потрібно видаляти це значення. В іншому випадку після рестарту цього вузла ви отримаєте два різних кластера, один з яких буде складатися тільки з першої Ноди.
Для себе я вивів порядок конфігурації при запуску і перезапуску кластера (вже описаний вище більш детально)
1. Node A: запуск c gcomm: // B, gcomm: // C, gcomm: //
2. Node A: видалення gcomm: // в кінці рядка
3. Nodes B, C: запуск з gcomm: // A
NB: потрібно обов'язково вказувати номер порту для Group Communication запитів, за замовчуванням це 4567. Тобто, правильний запис: gcomm: // A: 4567
Чи можна з неблокуючим xtrabackup як SST method писати на ноду-донор?
Під час SST clustercheck на донора буде виводити HTTP 503, відповідно для HAProxy або іншого LB, який використовує цю утиліту для визначення статусу, нода-донор буде вважатися недоступною, так само як і нода, на яку робиться трансфер. Але це поведінка можна змінити правкою clustercheck. який по суті звичайний bash-скрипт.
Це робиться наступною правкою:
NB: зауважте, що робити так можна тільки в тому випадку, коли ви впевнені що в якості SST використовується xtrabackup. а не якийсь інший метод. У нашому ж випадку, коли ми використовуємо в якості донора ноду, позбавлену навантаження, подібна правка взагалі не має сенсу.