Доброго времени суток. На хабре много писали (поиск) про то, как поднять MySQL кластер на основе решения Percona XtraDB Cluster. Но вот на днях ко мне подошёл программер и попросил сделать так, чтобы в MySQL можно было назначать хосты пользователям для разграничения доступа. Тут я вспомнил, что ip-то там отображаются далеко не клиентские, тут всё и началось :). В интернете было найдено решение аж 2009 года, которое заключалось в использовании tproxy патча, iproute2 и iptables, но это решило проблему частично, и только в пределах той машины где стоит haproxy, но что если мы хотим ещё и читать из разных мест? Вот что было сделано:
Я использую CentOS7.1.1503 (Core) с ядром 3.10.0-229.4.2.el7.x86_64.
1. Скачиваем исходники haproxy 1.5 haproxy-1.5.12-src.
2. Правим spec файл, который заботливо был подготовлен разработчиками, добавляем флаг USE_LINUX_TPROXY=1.
3. Собираем rpm пакет.
4. Устанавливаем на сервера, в моём случае это 3 сервера.
5. Убедиться, что haproxy собран с поддержкой tproxy, можно, набрав haproxy -vv.
Далее стандартная схема: на трёх нодах стоят keepalived для VIP (виртуальный ip, к которому будут подключаться клиенты), haproxy, MySQL.
Идея в слудующем: клиент использует для подключения один ip адрес (192.168.99.99) и 2 порта, порт 3306 для чтения и записи, порт 3307 только для чтения.
Работает это всё следующим образом: клиент соединяется c VIP 192.168.99.99:3306 и работает и на чтение, и на запись только на хосте с VIP. Если же он соединится с 192.168.99.99:3307, тогда он пойдёт на чтение на 2 другие ноды, отличные от той, где VIP.
Изначально проблема была именно с пробросом ip клиента при чтении. Решением оказалось использование протокола «proxy protocol» (строки send-proxy и accept-proxy в конфиге haproxy), написанного одним из разработчиков haproxy.
Я и подумал, а почему бы для проброса данных о src ip не использовать именно это решение. Жаль, что такие вещи как exim, postfix, nginx поддерживают протокол proxy, а PerconaCluster нет.
На каждом сервере делаем следующее:
iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 111
iptables -t mangle -A DIVERT -j ACCEPT
ip rule add fwmark 111 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.p4p2.rp_filter = 0
Конфиг haproxy node1:
global
log 127.0.0.1 local0 notice
maxconn 4096
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
#user haproxy
#group haproxy
daemon
defaults
log global
mode http
option dontlognull
retries 3
option redispatch
maxconn 3000
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
frontend status
bind 192.168.99.99:80
mode http
default_backend mysql-status
backend mysql-status
mode http
balance roundrobin
stats hide-version
stats scope mysql-backend-rw-3306
stats scope mysql-backend-ro-3307
stats scope mysql-backend-ro-end-3307
stats refresh 5s
stats show-node
stats uri /haproxy/stats
stats auth pwd:pwd
frontend mysql-rw
bind 192.168.99.99:3306
mode tcp
default_backend mysql-backend-rw-3306
frontend mysql-ro
bind 192.168.99.99:3307
mode tcp
default_backend mysql-backend-ro-3307
frontend mysql-ro-end
bind 192.168.99.28:3307 accept-proxy
mode tcp
default_backend mysql-backend-ro-end-3307
backend mysql-backend-rw-3306
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node1 192.168.99.28:3306 check port 9200 inter 12000 rise 3 fall 3
backend mysql-backend-ro-3307
mode tcp
balance leastconn
option httpchk
server node2 192.168.99.29:3307 send-proxy check port 9200 inter 12000 rise 3 fall 3
server node3 192.168.99.30:3307 send-proxy check port 9200 inter 12000 rise 3 fall 3
server node1 192.168.99.28:3306 source 0.0.0.0 usesrc clientip check port 9200 inter 12000 rise 3 fall 3 backup
backend mysql-backend-ro-end-3307
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node1 192.168.99.28:3306 check port 9200 inter 12000 rise 3 fall 3
Конфиг haproxy node2 (только отличия от node1):
frontend mysql-ro-end
bind 192.168.99.29:3307 accept-proxy
mode tcp
default_backend mysql-backend-ro-end-3307
backend mysql-backend-rw-3306
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node2 192.168.99.29:3306 check port 9200 inter 12000 rise 3 fall 3
backend mysql-backend-ro-3307
mode tcp
balance leastconn
option httpchk
server node1 192.168.99.28:3307 send-proxy check port 9200 inter 12000 rise 3 fall 3
server node3 192.168.99.30:3307 send-proxy check port 9200 inter 12000 rise 3 fall 3
server node2 192.168.99.29:3306 source 0.0.0.0 usesrc clientip check port 9200 inter 12000 rise 3 fall 3 backup
backend mysql-backend-ro-end-3307
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node2 192.168.99.29:3306 check port 9200 inter 12000 rise 3 fall 3
Конфиг haproxy node3 (только отличия от node1):
frontend mysql-ro-end
bind 192.168.99.30:3307 accept-proxy
mode tcp
default_backend mysql-backend-ro-end-3307
backend mysql-backend-rw-3306
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node3 192.168.99.30:3306 check port 9200 inter 12000 rise 3 fall 3
backend mysql-backend-ro-3307
mode tcp
balance leastconn
option httpchk
server node1 192.168.99.28:3307 send-proxy port 9200 inter 12000 rise 3 fall 3
server node2 192.168.99.29:3307 send-proxy port 9200 inter 12000 rise 3 fall 3
server node3 192.168.99.30:3306 source 0.0.0.0 usesrc clientip check port 9200 inter 12000 rise 3 fall 3 backup
backend mysql-backend-ro-end-3307
mode tcp
source 0.0.0.0 usesrc clientip
balance leastconn
option httpchk
server node3 192.168.99.30:3306 check port 9200 inter 12000 rise 3 fall 3
P.S Пробовал сделать DirectRouting, но странным образом не менялся src ip.
RaveNoX
Может подскажете, как вы решаете проблему автоматического запуска ( без ручного вмешательства ) при полной остановке кластера ( то есть всех нод примерно в одно время ) например из-за сбоя питания?
Я у себя пробовал использовать percona, но данная проблема ( необходимость принудительного бутстрапа одной из нод для старта кластера ) остановила внедрение.
uran238 Автор
У меня в виртуалке в другом месте крутится демон garbd, он же арбитр. Он должен быть внесён в .my.cnf на всех нодах как gcomm://<его ip> т.е. как полноценный член кластера.