Eu explicarei minha configuração e como resolvi as recargas graciosas:
Eu tenho uma configuração típica com 2 nós executando o HAproxy e keepalived. Keepalived segue a interface dummy0, então eu posso fazer um "ifconfig dummy0 down" para forçar a mudança.
O verdadeiro problema é que, não sei por que, um "recarregamento haproxy" ainda abandona todas as conexões ESTABLISHED :( eu tentei o "iptables flipping" proposto pelo gertas, mas encontrei alguns problemas porque ele executa um NAT no endereço IP de destino, o que não é uma solução adequada em alguns cenários.
Em vez disso, decidi usar um ataque CONNMARK para marcar pacotes pertencentes a conexões NOVAS e, em seguida, redirecionar esses pacotes marcados para o outro nó.
Aqui está o conjunto de regras do iptables:
iptables -t mangle -A PREROUTING -i eth1 -d 123.123.123.123/32 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
As duas primeiras regras marcam os pacotes pertencentes aos novos fluxos (123.123.123.123 é o VIP keepalived usado no haproxy para ligar os frontends).
Terceira e quarta regras marcam pacotes FIN / RST. (Eu não sei porque, alvo TEE "ignora" pacotes FIN / RST).
Quinta regra envia uma duplicata de todos os pacotes marcados para o outro HAproxy (192.168.0.2).
A sexta regra descarta pacotes pertencentes a novos fluxos para evitar atingir seu destino original.
Lembre-se de desabilitar o rp_filter em interfaces ou o kernel removerá esses pacotes marcianos.
E por último mas não menos importante, lembre-se dos pacotes que retornam! No meu caso, há roteamento assimétrico (as solicitações chegam ao cliente - > haproxy1 - > haproxy2 - > servidor da Web, e as respostas vão do servidor da Web - > haproxy1 - > cliente), mas isso não afeta. Funciona bem.
Eu sei que a solução mais elegante seria usar o iproute2 para fazer o desvio, mas ele só funcionou para o primeiro pacote SYN. Quando recebeu o ACK (3º pacote do handshake 3-way), ele não marcou :( Eu não poderia gastar muito tempo para investigar, assim que eu vi ele funciona com o alvo TEE, ele deixou lá. Claro, sinta-se à vontade para experimentar com o iproute2.
Basicamente, o "recarregamento normal" funciona assim:
- Eu ativo o conjunto de regras iptables e vejo imediatamente as novas conexões indo para o outro HAproxy.
- Fico de olho em "netstat -an | grep ESTABLISHED | wc -l" para supervisionar o processo de "drenagem".
- Uma vez que há apenas algumas (ou zero) conexões, "ifconfig dummy0 down" para forçar keepalived para failover, então todo o tráfego irá para o outro HAproxy.
- eu removo o conjunto de regras iptables
- (somente para configuração keepalive "não preempting") "ifconfig dummy0 up".
O conjunto de regras IPtables pode ser facilmente integrado em um script de início / parada:
#!/bin/sh
case $1 in
start)
echo Redirection for new sessions is enabled
# echo 0 > /proc/sys/net/ipv4/tcp_fwmark_accept
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
iptables -t mangle -A PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
;;
stop)
iptables -t mangle -D PREROUTING -i eth1 -m mark --mark 1 -j DROP
iptables -t mangle -D PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -D PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
echo Redirection for new sessions is disabled
;;
esac