Resolução de Problema de RST em Requisições HTTP em Ambiente Linux

Contexto do Problema Em ambiente de produção, ao se comunicar com terceiros, frequentemente ocorrem perdas de pacotes. O fenômeno específico é que, às vezes, os servidores de aplicativos não recebem mensagens, e a captura de pacotes mostra RST.

A comunicação no ambiente de produção utiliza HTPS, com SSL e balanceador de carga F5 no frontend, conversão NAT na entrada de negócios, seguido por descarga SSL do HTTPS, e requisições HTTP passando pelo balanceador F5 para dois servidores de aplicação.

Análise do Problema Devido à longa cadeia de pacotes, o cliente relata erro de não receber resposta do servidor, mas o servidor mostra que não recebeu a transação. Isso nos força a realizar captura de pacotes para investigação.

Aálise de erro do cliente "Unexpected end of file from server" - fim inesperado de arquivo do servidor A causa direita desse erro é a perda do pacote de dados retornados, resultando nesse erro.

As causas fundamentais podem ser três:

O código do programa do servidor tem problemas; Algum dispositivo de rede na cadeia de rede está com falha, como SSL ou F5; A configuração da política de segurança do dispositivo de rede, como restrição de IP, rejeição de varredura maliciosa, etc. Quando não atende à política de segurança, o pacote de dados é descartado diretamente;

Problema no código do servidor Se for um problema de código, deveria ocorrer em cenários específicos, mas o probleam ocorre aleatoriamente, não em situações específicas, funcionando bem às vezes e mal outras.

Problema com dispositivo de rede na cadeia Após a captura de pacotes, os dispositivos de rede na cadeia receberam os pacotes e os encaminharam para trás, mas foram rejeitados pelo destino.

Problema com configuração de política de segurança do dispositivo de rede Isso é muito provável. Alguma situação causa o servidor a descartar pacotes de dados, e investigamos detalhadamente essa perspectiva.

Análise da situação do servidor O aplicativo do servidor não tem logs de pacotes recebidos, mas os dispositivos de rede já encaminharam, podemos julgar que o problema deve estar em algumas configurações do servidor de aplicação, mas não está relacionado ao código do aplicativo, mas sim a configurações do servidor Linux ou firewall de rede.

Solução do Problema

1. Verificação da configuração do firewall

Para investigar o problema, desligamos o firewall diretamente.

2. Verificação de parâmetros ipv4 no sysctl

sistema_rede.ipv4.tcp_tw_recicla = 1
sistema_rede.ipv4.marcas_temporais = 1
....

3. Verificação de parâmetros ssh correspondentes no linux

Localização Final do Problema

Finalmente, descobrimos que era um problema com os parâmetros do sysctl, modificado da seguinte forma:

sistema_rede.ipv4.tcp_tw_recicla = 1
sistema_rede.ipv4.marcas_temporais = 0
....

Descrição do cenário onde marcas_temporais e tcp_tw_recicla não são ativados simultaneamente

Sob NAT COMPLETO

Em NAT COMPLETO, quando o cliente solicita VIP, não apenas substitui o dst ip do pacote, mas também substitui o src ip; mas quando VIP retorna ao cliente, também substitui o src ip.

O backend do LVS é um servidor web.

Suponha que o servidor web tenha ativado os parâmetros marcas_temporais e tcp_tw_recicla do TCP. Então existe a seguinte situação

No RFC1323, há o seguinte trecho:

"Um mecanismo adicional poderia ser adicionado ao TCP, um cache por host do último timestamp recebido de qualquer conexão. Este valor poderia então ser usado no mecanismo PAWS para rejeitar segmentos duplicados antigos de encarnações anteriores da conexão, se o relógio do timestamp puder ser garantido para ter ticado pelo menos uma vez desde que a conexão antiga estava aberta. Isso exigiria que o atraso TIME-WAIT mais o RTT juntos deve ser pelo menos um tick do relógio de timestamp do remetente. Tal extensão não faz parte da proposta deste RFC."

A essência é que o TCP tem um comportamento que pode armazenar em cache o timestamp mais recente de cada conexão. Nas solicitações subsequentes, se o timestamp for menor que o timestamp armazenado em cache, ele será considerado inválido e o pacote correspondente será descartado. Isso significa que para o mesmo IP de origem se conectando à mesma porta de destino, os timestamps dos pacotes devem ser crescentes.

A ativação desse comportamento no Linux depende de marcas_temporais e tcp_tw_recicla. Como marcas_temporais é ativado por padrão, quando tcp_tw_recicla é ativado, esse comportamento na verdade é ativado.

Muitas empresas agora usam LVS para balanceamento de carga, geralmente um LVS na frente com vários servidores backend, o que na verdade é NAT. Quando a solicitação chega ao LVS, ele modifica o endereço e depois encaminha para o servidor backend, mas não modifica os dados do timestamp. Para o servidor backend, o endereço de origem da solicitação é o endereço do LVS, e as portas são reutilizadas, então da perspectiva do servidor backend, solicitações de diferentes clientes passadas pelo encaminhamento do LVS podem ser consideradas a mesma conexão. Além disso, os timestamps de diferentes clientes podem não ser consistentes, o que resulta em confusão de timestamps, e os pacotes subsequentes são descartados. O desempenho específico geralmente é que o cliente envia SYN claramente, mas o servidor não responde ao ACK. Você também pode confirmar o fenômeno de que os pacotes continuam sendo descartados através do seguinte comando:

Notas Importantes

1. tw_reutilizar e tw_reciclar devem estar ativados no cliente e no servidor marcas_temporais (ativado por padrão) para funcionar. Na verdade, significa que se um dos lados (cliente ou servidor) não tiver marcas_temporais ativados, tw_reutilizar e tw_reciclar não terão efeito.
2. tw_reutilizar só funciona no cliente. Quando ativado, o cliente recicla em 1s. Reutilizar significa reutilizar a conexão socket time_wait. Não há teoricamente limite para a mesma porta no servidor ser conectada.
3. tw_reciclar funciona tanto no cliente quanto no servidor. Quando ativado, recicla em 3.5*RTO. RTO varia de 200ms a 120s, dependendo da condição da rede. Em rede interna, é um pouco mais rápido que tw_reutilizar. Em rede pública, especialmente em redes móveis, é geralmente mais lento que tw_reutilizar. A vantagem é que pode reciclar o número de TIME_WAIT do servidor.

Para o cliente

1. Como cliente, devido ao problema de porta 65535, muitos TIME_WAIT afetam diretamente a capacidade de processamento. Ativar tw_reutilizar resolve o problema. Não é recomendável ativar tw_reciclar simultaneamente, pois não ajuda muito.
2. tw_reutilizar ajuda o cliente a reciclar a conexão em 1s, basicamente alcançando 60.000 solicitações/s por máquina. Se precisar de mais, aumente o número de IPs.
3. Em cenários de teste de carga de rede interna, e se o cliente não precisar receber conexões, tw_reciclar pode ter um pequeno benefício.

Para o servidor

1. Ativar tw_reutilizar é inútil, porque o cliente se conecta ao servidor web, o servidor definitivamente não reutilizará o socket para se conectar ao cliente ativamente. Esse parâmetro geralmente não é usado pelo servidor, a menos que o servidor web também atue como cliente para se conectar ao banco de dados. Mas quando o servidor web como cliente atinge o limite de 60.000 portas para se conectar ao banco de dados, seu banco de dados já não suporta a pressão e entra em colapso. Geralmente, 5.000 conexões de banco de dados já é muito alto.

O parâmetro tw_reutilizar só é útil para o cliente. Significa reutilizar a conexão socket em estado time_wait.

2. Em ambiente de produção, não ative tw_reciclar. Se o servidor estiver atrás de dispositivo NAT de carga, ou o cliente estiver atrás de NAT (o que é inevitável, pois redes de empresas e casas geralmente usam NAT);
Serviços em rede pública podem causar falhas de conexão parcial. Em rede interna, pode ser ativado conforme a situação. Alguns dispositivos de balanceamento de carga limpam todos os timestamps, então para o servidor web backend, ativar ou não tw_reciclar não importa.

3. Como lidar com muitos TIME_WAIT no servidor

Não se preocupe com time_wait no servidor, porque sou o servidor, muitos IPs e portas de cliente se conectam ativamente à minha porta, por exemplo, conectando-se à minha porta 80. Pode ocorrer uma situação em que, embora eu tenha 100.000 conexões time_wait na minha máquina, apenas uma porta (porta 80) está sendo usada.

Ao contrário do cliente que tem limitação de porta, lidar com muitos TIME_WAIT no Linux já está bem otimizado. Cada conexão em estado TIME_WAIT consome pouca memória, e você pode configurar o limite máximo através de tcp_max_tw_buckets = 262144. Máquinas modernas geralmente não têm falta dessa memória.

Resumo final

Em resumo, na produção, independentemente de o servidor estar atrás de dispositivo NAT.

Não ative tcp_tw_reciclar. O estado padrão é desativado, com valor 0

Mantenha marcas_temporais ativado por padrão, com valor 1

tcp_tw_reutilizar. É melhor ativá-lo no cliente. Quando o dispositivo de balanceamento de carga se conecta ao servidor web, tente ativar também nos dispositivos de balanceamento de carga auxiliares

Sobre muitos time_wait no servidor, algumas pessoas podem perguntar: sou servidor web, por que ocorre time_wait como no cliente?

Na verdade, sobre time_wait, ele aparece no lado que ativamente fecha a conexão. O servidor fecha ativamente a conexão HTTP e se torna o cliente.

A ação de iniciar o encerramento da conexão não significa necessariamente que o cliente inicie o encerramento. Muitas vezes é o servidor que inicia primeiro a operação de encerramento da conexão. Por exemplo, muitos servidores HTTP, conexões curtas. Muitas vezes o servidor encerra ativamente.

Quando ocorrem problemas de conexão tcp, pode verificar se há muitos time_wait. Muitas vezes é causado pela ativação de tcp_tw_reciclar.




Tags: Linux TCP rede rst HTTP

Publicado em 6-8 22:35 por Thomas