Percona XtraDB Cluster пробрасываем ip клиента

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

© Habrahabr.ru