Estou executando um servidor de balanceamento de carga HAProxy para equilibrar a carga em vários servidores Apache. Preciso recarregar o HAProxy a qualquer momento para alterar o algoritmo de balanceamento de carga.
Isso tudo funciona bem, exceto pelo fato de eu ter que recarregar o servidor sem perder um único pacote (no momento, uma recarga está me dando 99,76% de sucesso, em média, com 1000 solicitações por segundo por 5 segundos). Eu fiz muitas horas de pesquisa sobre isso e encontrei o seguinte comando para "recarregar normalmente" o servidor HAProxy:
haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
No entanto, isso tem pouco ou nenhum efeito em relação ao antigo service haproxy reload
, ainda está caindo 0,24% em média.
Existe alguma maneira de recarregar o arquivo de configuração HAProxy sem um único pacote descartado de qualquer usuário?
Respostas:
De acordo com https://github.com/aws/opsworks-cookbooks/pull/40 e, consequentemente, http://www.mail-archive.com/[email protected]/msg06885.html, você pode:
fonte
iptables v1.4.14: invalid port/service
--syn 'especificado'$PORT
pela porta real quehaproxy
está ouvindo. Se o haproxy estiver escutando em várias portas, escreva substituir--dport $PORT
por--dports $PORTS_SEPARATED_BY_COMMAS
, por exemplo--dports 80,443
,.O Yelp compartilhou uma abordagem mais sofisticada com base em testes meticulosos. O artigo do blog é um mergulho profundo e vale a pena o investimento de tempo para apreciá-lo totalmente.
Recarregamentos HAProxy com zero tempo de inatividade verdadeiro
tl; dr usa Linux tc (controle de tráfego) e iptables para enfileirar temporariamente pacotes SYN enquanto o HAProxy está recarregando e tem dois pids anexados à mesma porta (
SO_REUSEPORT
).Não me sinto à vontade republicando o artigo inteiro no ServerFault; no entanto, aqui estão alguns trechos para despertar seu interesse:
Gist: https://gist.github.com/jolynch/97e3505a1e92e35de2c0
Felicidades ao Yelp por compartilhar idéias incríveis.
fonte
Há outra maneira muito mais simples de recarregar a haproxy com o tempo de inatividade zero real - ele é chamado iptables flipping (o artigo é realmente a resposta Unbounce à solução do Yelp). É mais limpo do que a resposta aceita, pois não há necessidade de descartar pacotes que possam causar problemas com recargas longas.
Resumidamente, a solução consiste nas seguintes etapas:
iptable
comandos simples .Além disso, a solução pode ser adotada para qualquer tipo de serviço (nginx, apache etc.) e é mais tolerante a falhas, pois você pode testar a configuração em espera antes de ficar on-line.
fonte
Editar: Minha resposta assume que o kernel envia tráfego apenas para a porta mais recente a ser aberta com SO_REUSEPORT, enquanto na verdade envia tráfego para todos os processos, conforme descrito em um dos comentários. Em outras palavras, a dança do iptables ainda é necessária. :(
Se você estiver em um kernel que suporta SO_REUSEPORT, esse problema não deve acontecer.
O processo que o haproxy leva ao reiniciar é:
1) Tente definir SO_REUSEPORT ao abrir a porta ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798 )
2) Tente abrir a porta (terá êxito com SO_REUSEPORT)
3) Se não tiver êxito, sinalize o processo antigo para fechar sua porta, aguarde 10ms e tente tudo novamente. ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/haproxy.c#L1554-L1577 )
Foi suportado pela primeira vez no kernel Linux 3.9, mas algumas distribuições o suportaram. Por exemplo, os kernels EL6 de 2.6.32-417.el6 o suportam.
fonte
SO_REUSEPORT
em algum cenário específico - especialmente sob tráfego pesado. Quando o SYN é enviado para o antigo processo haproxy e, no mesmo momento, fecha o soquete de escuta, o que resulta em RST. Consulte o artigo do Yelp mencionado em outra resposta acima.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:
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:
O conjunto de regras IPtables pode ser facilmente integrado a um script start / stop:
fonte