Связка keepalived + ProxySQL часто становится «легковесным HA» для MySQL: приложение подключается к одному virtual IP (VIP), а дальше ProxySQL маршрутизирует запросы на текущий writer (primary) и readers (replicas), делает read/write split и переживает переключения. Но как только появляется VIP и два узла, вы упираетесь в главный риск — split-brain: оба узла считают себя активными и/или запись уезжает «не туда».
Ниже — практичная схема, которая снижает вероятность split-brain за счёт корректных health checks, явных условий выдачи VIP, режима обслуживания и «последнего рубежа» на уровне MySQL (read-only fencing). Примеры ориентированы на Linux-серверы и MySQL с топологией primary/replica.
Референсная архитектура: кто за что отвечает
Базовая цель: VIP всегда указывает на один активный ProxySQL, а ProxySQL всегда отправляет запись на текущий writer MySQL.
Компоненты
- 2 узла ProxySQL (например,
pxy-1иpxy-2) — точка входа для приложения. - keepalived на обоих узлах — держит VIP по VRRP и решает, кто «активный».
- MySQL writer и MySQL readers — набор серверов, за которыми следит ProxySQL (и/или ваш оркестратор).
Что keepalived делать не должен
keepalived не должен «определять primary MySQL». Его зона ответственности — выдать VIP только тому узлу, который сейчас безопасно может принимать трафик: ProxySQL жив, админка доступна, и есть корректный writer в runtime.
VIP — это про доступность точки входа. Правильность роли MySQL обеспечивается отдельной логикой (ProxySQL checks, оркестратор, read-only барьеры).
Split-brain: откуда берётся и как его гасить
В этой схеме split-brain обычно проявляется в двух формах:
- VIP split-brain: оба ProxySQL-узла одновременно держат VIP (сетевой разрыв, неверный VRRP, не тот интерфейс, проблемы multicast).
- Write split-brain: запись уходит не на writer (ProxySQL «не успел» перестроиться, writer сменился, или в MySQL нарушены ограничения записи).
Минимальный набор защит (практический)
- VRRP unicast и явные peer-адреса (в облаках и VLAN multicast часто ненадёжен).
- Смысловой health check перед выдачей VIP: не только «порт открыт», а «есть ONLINE writer в runtime».
- Maintenance mode: понятный способ гарантированно снять VIP с узла перед работами.
- Read-only fencing на MySQL: всё, что не writer, должно быть
read_only=ONиsuper_read_only=ON.
Если вы поднимаете ProxySQL/keepalived на VPS в разных зонах или на «голом железе», обычно удобнее сразу закладывать запас по ресурсам и сети (низкие задержки между узлами, отдельная mgmt-сеть, предсказуемый firewall). Для таких задач чаще выбирают VDS, чтобы держать под контролем сетевые правила, VRRP-unicast и диагностику на уровне пакетов.
keepalived: VIP failover с проверками и без сюрпризов
Ниже пример конфигурации keepalived для двух ProxySQL-узлов. Схема делает следующее:
- использует unicast VRRP;
- перед выдачей VIP запускает
vrrp_script(health check); - «роняет» приоритет при деградации, инициируя failover;
- поддерживает ручной maintenance mode через файловый флаг.
/etc/keepalived/keepalived.conf (узел pxy-1)
global_defs {
router_id pxy-1
enable_script_security
script_user root
}
vrrp_script chk_proxysql {
script "/usr/local/sbin/chk-proxysql.sh"
interval 2
timeout 2
fall 2
rise 3
weight -50
}
vrrp_instance VI_10 {
state BACKUP
interface eth0
virtual_router_id 10
priority 150
advert_int 1
unicast_src_ip 10.0.0.11
unicast_peer {
10.0.0.12
}
authentication {
auth_type PASS
auth_pass change-me
}
virtual_ipaddress {
10.0.0.100/24 dev eth0
}
track_script {
chk_proxysql
}
}
/etc/keepalived/keepalived.conf (узел pxy-2)
global_defs {
router_id pxy-2
enable_script_security
script_user root
}
vrrp_script chk_proxysql {
script "/usr/local/sbin/chk-proxysql.sh"
interval 2
timeout 2
fall 2
rise 3
weight -50
}
vrrp_instance VI_10 {
state BACKUP
interface eth0
virtual_router_id 10
priority 100
advert_int 1
unicast_src_ip 10.0.0.12
unicast_peer {
10.0.0.11
}
authentication {
auth_type PASS
auth_pass change-me
}
virtual_ipaddress {
10.0.0.100/24 dev eth0
}
track_script {
chk_proxysql
}
}
Health check для keepalived: что проверять «по-взрослому»
Плохой check — «6033 открыт». Он не поймёт, что ProxySQL не видит writer, что мониторинг MySQL «залип» или что конфигурация не применена в runtime. Для VIP нам важно одно: этот узел способен безопасно принимать трафик прямо сейчас.
Ниже пример скрипта: он учитывает maintenance-флаг, проверяет клиентский и админский порты, и убеждается, что в runtime_mysql_servers есть хотя бы один ONLINE writer в нужном hostgroup.

/usr/local/sbin/chk-proxysql.sh
#!/bin/sh
MAINT_FLAG="/var/run/pxy-maintenance"
ADMIN_USER="admin"
ADMIN_PASS="admin-pass"
WRITER_HG="10"
if [ -f "$MAINT_FLAG" ]; then
exit 1
fi
nc -z -w 1 127.0.0.1 6033
if [ $? -ne 0 ]; then
exit 1
fi
nc -z -w 1 127.0.0.1 6032
if [ $? -ne 0 ]; then
exit 1
fi
mysql -h 127.0.0.1 -P 6032 -u "$ADMIN_USER" -p"$ADMIN_PASS" -Nse "SELECT COUNT(*) FROM runtime_mysql_servers WHERE hostgroup_id=$WRITER_HG AND status='ONLINE';" 2>/dev/null | grep -q '^[1-9]'
if [ $? -ne 0 ]; then
exit 1
fi
exit 0
Смысл check’а: не «жив ли процесс», а «можно ли выдавать VIP без риска принять трафик в никуда или писать в неправильное место».
ProxySQL: read/write split и привязка к роли MySQL
Обычно в ProxySQL настраивают hostgroup’ы: один для writer, другой для readers. Типовая заготовка:
hostgroup_id=10— writer (primary);hostgroup_id=20— readers (replicas).
Почему важно проверять MySQL на уровне роли
Если ProxySQL ошибся и продолжает слать запись на бывший primary, вы получите либо ошибки (если read-only барьеры настроены), либо расхождение данных (если барьеров нет). На практике MySQL HA почти всегда требует сочетания:
- понятного источника истины о writer (оркестратор, ручная процедура, автоматизация);
- быстрого обновления маршрутизации/статусов в ProxySQL;
- запрета записи на «не-writer» на уровне MySQL.
Базовая защита от write split-brain: read_only и super_read_only
На всех репликах держите read_only=ON и super_read_only=ON. На writer — выключено. При failover новый writer должен снять флаги, старый writer (если «воскрес») — включить обратно. Это не идеальный fencing, но отличный практический барьер.
Если хотите детальнее про режимы read-only и обслуживание MySQL (когда и какие флаги включать, как не словить сюрпризы от суперпользователей), пригодится заметка: read_only/super_read_only для обслуживания MySQL.
Fencing: что делать, когда сеть «порвало»
Fencing — это способ гарантировать, что проблемный узел не сможет вредить. В контексте keepalived + ProxySQL + MySQL обычно комбинируют несколько уровней:
- VIP fencing: keepalived убирает VIP при сомнениях (health check, maintenance, деградация).
- DB fencing: узел MySQL, который не writer, принудительно read-only.
- Network fencing: firewall-правила, которые ограничивают доступ к 3306 и/или админским портам по источникам.
Практичный принцип для VIP: лучше «нет VIP», чем «VIP в никуда»
Если оба ProxySQL-узла потеряли writer (массовая авария, разрыв кластера, проблема сети), держать VIP на одном из них часто хуже, чем уронить входную точку: приложение будет получать быстрый и честный отказ, а не таймауты и полуработу.
Ровно поэтому в health check мы требуем наличие ONLINE writer в runtime. Нет writer — нет VIP.
Команды read-only fencing для MySQL (встраивайте в вашу процедуру failover)
Когда узел теряет роль writer, он должен включить read-only:
mysql -e "SET GLOBAL super_read_only=ON; SET GLOBAL read_only=ON;"
А новый writer — снять:
mysql -e "SET GLOBAL super_read_only=OFF; SET GLOBAL read_only=OFF;"
Эти шаги выполняйте как часть единой процедуры переключения, вместе с проверкой репликации и пониманием вашего RPO/RTO. Если используете Orchestrator или похожую автоматику, полезно иметь документированную топологию и правила promote/demote; для этого можно опереться на материал: топология и promote в MySQL Orchestrator.
Maintenance mode: обслуживание ProxySQL/keepalived без ручного шаманства
Для плановых работ нужен предсказуемый способ снять VIP с узла, не ломая второй. Самый простой подход — файл-флаг, который читает chk-proxysql.sh.
Включить обслуживание на pxy-1
touch /var/run/pxy-maintenance
systemctl reload keepalived
Проверить, где VIP
ip a show dev eth0 | grep -F 10.0.0.100
Вернуть узел в работу
rm -f /var/run/pxy-maintenance
systemctl reload keepalived
Maintenance mode удобен тем, что вы не трогаете приоритеты вручную: вы делаете узел «негодным для VIP», а keepalived сам передаёт VIP второму.
Health checks и диагностика: что мониторить и как быстро найти проблему
Разделите проверки на два уровня
- VIP-level (ProxySQL узлы): можно ли принимать трафик сейчас.
- DB-role-level (MySQL топология): действительно ли writer writable и кто им является.
VIP-level: минимум сигналов, которые стоит алертить
- ProxySQL слушает 6033 и отвечает на handshake (не только порт открыт).
- Админка 6032 доступна.
- В
runtime_mysql_serversесть ONLINE writer в writer hostgroup. - VIP не «флапает» (частые смены MASTER/BACKUP).
DB-role-level: минимум проверок роли
- На writer:
read_only=OFFиsuper_read_only=OFF. - На readers: оба флага включены.
- Репликация не в аварии (или вы осознанно живёте в degraded).

Типовые проблемы и быстрые команды
Ситуация 1: оба узла держат VIP. Обычно это VRRP/unicast/firewall или неверный интерфейс. Проверяйте логи и VRRP-трафик:
journalctl -u keepalived -n 200 --no-pager
ip a | grep -F 10.0.0.100
tcpdump -ni eth0 vrrp
Ситуация 2: VIP переехал, а клиенты всё равно «сыпятся». Смотрите ARP/neighbor, доступность 6033/6032 и то, что check действительно не пропускает деградацию:
ip neigh show | grep -F 10.0.0.100
ss -lntp | grep -E '6032|6033'
Ситуация 3: ProxySQL считает writer ONLINE, но запись не работает. Часто это read-only не сняли после failover или права пользователя. На предполагаемом writer проверьте флаги:
mysql -e "SHOW VARIABLES LIKE 'read_only'; SHOW VARIABLES LIKE 'super_read_only';"
Чек-лист перед продакшеном
- VRRP в unicast, peer-адреса указаны, firewall пропускает VRRP (и вы тестировали реальный failover).
vrrp_scriptпроверяет не только порты, но и наличие ONLINE writer в runtime ProxySQL.- Есть maintenance mode, протестирован: VIP гарантированно уходит.
- Реплики всегда в
super_read_only; процедура failover корректно переключает флаги. - Протестированы «плохие» сценарии: разрыв сети, зависание MySQL, частичный разрыв, возврат старого writer.
- Собираются логи keepalived и ProxySQL, настроены алерты на flapping VIP и отсутствие writer.
Если вы строите схему для внешних клиентов (или проксируете через публичный VIP), не забывайте про транспортное шифрование: для MySQL-клиентов и админских интерфейсов удобнее заранее подготовить SSL-сертификаты и единые правила доступа, чем потом разбирать утечки и «случайно открытые» порты.


