Percona XtraDB Cluster пробрасываем ip клиента
Доброго времени суток. На хабре много писали (поиск) про то, как поднять 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
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
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
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.