Выберите продукт

MySQL HA на keepalived + ProxySQL: VIP failover без split-brain и с проверками здоровья

Разбираем рабочую схему MySQL HA: два узла ProxySQL под VIP на keepalived (VRRP unicast). Настраиваем health checks, maintenance mode и защиту от split-brain, плюс барьеры записи через read_only/super_read_only. С примерами конфигов и диагностики.
MySQL HA на keepalived + ProxySQL: VIP failover без split-brain и с проверками здоровья

Связка 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 нарушены ограничения записи).

Минимальный набор защит (практический)

  1. VRRP unicast и явные peer-адреса (в облаках и VLAN multicast часто ненадёжен).
  2. Смысловой health check перед выдачей VIP: не только «порт открыт», а «есть ONLINE writer в runtime».
  3. Maintenance mode: понятный способ гарантированно снять VIP с узла перед работами.
  4. 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 нам важно одно: этот узел способен безопасно принимать трафик прямо сейчас.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Ниже пример скрипта: он учитывает maintenance-флаг, проверяет клиентский и админский порты, и убеждается, что в runtime_mysql_servers есть хотя бы один ONLINE writer в нужном hostgroup.

Схема VRRP unicast: VIP переходит между двумя узлами ProxySQL по результатам health check

/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).

Диагностика VRRP и VIP: логи keepalived и захват VRRP-пакетов tcpdump

Типовые проблемы и быстрые команды

Ситуация 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';"

Чек-лист перед продакшеном

  1. VRRP в unicast, peer-адреса указаны, firewall пропускает VRRP (и вы тестировали реальный failover).
  2. vrrp_script проверяет не только порты, но и наличие ONLINE writer в runtime ProxySQL.
  3. Есть maintenance mode, протестирован: VIP гарантированно уходит.
  4. Реплики всегда в super_read_only; процедура failover корректно переключает флаги.
  5. Протестированы «плохие» сценарии: разрыв сети, зависание MySQL, частичный разрыв, возврат старого writer.
  6. Собираются логи keepalived и ProxySQL, настроены алерты на flapping VIP и отсутствие writer.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Если вы строите схему для внешних клиентов (или проксируете через публичный VIP), не забывайте про транспортное шифрование: для MySQL-клиентов и админских интерфейсов удобнее заранее подготовить SSL-сертификаты и единые правила доступа, чем потом разбирать утечки и «случайно открытые» порты.

Поделиться статьей

Вам будет интересно

Docker/Compose: IPAM, DNS и MTU — как диагностировать и исправлять сетевые проблемы OpenAI Статья написана AI (GPT 5)

Docker/Compose: IPAM, DNS и MTU — как диагностировать и исправлять сетевые проблемы

Сети в Docker обычно «просто работают», пока не появляется: container cannot resolve, резолвинг через раз, таймауты без ошибок или ...
IDN и Punycode: как работают международные домены и как защититься от homograph attack OpenAI Статья написана AI (GPT 5)

IDN и Punycode: как работают международные домены и как защититься от homograph attack

IDN-домены удобны для брендов и проектов, но из-за похожих символов появляются homograph-атаки и фишинг. Разбираем punycode (xn--) ...
PostgreSQL timeouts: как настроить statement_timeout, lock_timeout и idle_in_transaction_session_timeout OpenAI Статья написана AI (GPT 5)

PostgreSQL timeouts: как настроить statement_timeout, lock_timeout и idle_in_transaction_session_timeout

Таймауты в PostgreSQL спасают от «зависших» запросов, долгого ожидания блокировок и забытых транзакций. Разбираем statement_timeou ...