Доброго времени суток. На хабре много писали (поиск) про то, как поднять MySQL кластер на основе решения Percona XtraDB Cluster. Но вот на днях ко мне подошёл программер и попросил сделать так, чтобы в MySQL можно было назначать хосты пользователям для разграничения доступа. Тут я вспомнил, что ip-то там отображаются далеко не клиентские, тут всё и началось :). В интернете было найдено решение аж 2009 года, которое заключалось в использовании tproxy патча, iproute2 и iptables. Вот что было сделано:
Я использую 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 только для чтения.
На каждом сервере делаем следующее:
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
Работает это всё следующим образом:
клиент соединяется c VIP 192.168.99.99:3306 и работает и на чтение, и на запись только на хосте с VIP. Если же он соединится с 192.168.99.99:3307, тогда он пойдёт на чтение на 2 другие ноды, отличные от той, где VIP.
Изначально проблема была именно с пробросом ip клиента при чтении. При порте 3306 всё заработало сразу.
Решением оказалось использование протокола «proxy protocol» (строки send-proxy и accept-proxy в конфиге haproxy), написанного одним из разработчиков haproxy.
Я и подумал, а почему бы для проброса данных о src ip не использовать именно это решение. Жаль, что такие вещи как exim, postfix, nginx поддерживают протокол proxy, а PerconaCluster нет.
P.S. Статью накидал по-быстрому, дабы не забыть.
P.P.S Пробовал сделать DirectRouting, но странным образом не менялся src ip.
ссылка на оригинал статьи http://habrahabr.ru/post/261103/
Добавить комментарий