Vou explicar minha configuração e como resolvi as recargas graciosas:
Eu tenho uma configuração típica com 2 nós executando HAproxy e keepalived. As faixas mantidas estão em interface com dummy0, para que eu possa fazer um "ifconfig dummy0 down" para forçar a alternância.
O verdadeiro problema é que, não sei por que, uma "recarga haproxy" ainda descarta todas as conexões ESTABELECIDAS :( Tentei o "iptables flipping" proposto por gertas, mas encontrei alguns problemas porque ele executa um NAT no destino Endereço IP, que não é uma solução adequada em alguns cenários.
Em vez disso, decidi usar um hack sujo do CONNMARK para marcar pacotes pertencentes a novas conexões e 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 de manutenção permanente usado no haproxy para vincular as interfaces).
Terceira e quarta regras marcam pacotes FIN / RST. (Não sei por que, o destino TEE "ignora" pacotes FIN / RST).
A 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 nas interfaces ou o kernel descartará esses pacotes marcianos.
E por último mas não menos importante, lembre-se dos pacotes retornados! No meu caso, há um roteamento assimétrico (as solicitações chegam ao cliente -> haproxy1 -> haproxy2 -> webserver e as respostas vão de webserver -> haproxy1 -> client), mas isso não afeta. Funciona bem.
Eu sei que a solução mais elegante seria usar o iproute2 para desviar, mas funcionou apenas para o primeiro pacote SYN. Quando recebeu o ACK (terceiro pacote do handshake de três vias), não o marcou :( Não pude gastar muito tempo para investigar, assim que vi que ele trabalha com o alvo TEE, ele o deixou lá. Obviamente, sinta-se à vontade para experimentá-lo com o iproute2.
Basicamente, a "atualização graciosa" funciona assim:
- Ativei o conjunto de regras iptables e imediatamente vi as novas conexões indo para o outro HAproxy.
- Fico de olho em "netstat -an | grep ESTABELECIDO | wc -l" para supervisionar o processo de "drenagem".
- Quando houver apenas algumas conexões (ou zero), "ifconfig dummy0 down" para forçar a manutenção do failover, todo o tráfego será direcionado para o outro HAproxy.
- Eu removo o conjunto de regras iptables
- (Apenas para "keepalive config" sem antecipação ")" ifconfig dummy0 up ".
O conjunto de regras IPtables pode ser facilmente integrado a um script start / stop:
#!/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