Ajustando os parâmetros de roteamento IP do Linux - secret_interval e tcp_mem

30

Hoje tivemos um pequeno problema de failover com uma de nossas VMs HAProxy. Quando nos aprofundamos, descobrimos o seguinte:

26 de janeiro 07:41:45 Kernel haproxy2: [226818.070059] __ratelimit: 10 retornos de chamada suprimidos
26 de janeiro 07:41:45 kernel haproxy2: [226818.070064] Memória insuficiente de soquete
26 de janeiro 07:41:47 kernel do haproxy2: [226819.560048] Memória insuficiente
26 de janeiro 07:41:49 kernel haproxy2: [226822.030044] Memória insuficiente

O que, por esse link , aparentemente tem a ver com as configurações padrão baixas para net.ipv4.tcp_mem. Portanto, aumentamos em 4x os padrões (esse é o Ubuntu Server, não tenho certeza se o sabor do Linux é importante):

os valores atuais são: 45984 61312 91968
novos valores são: 183936 245248 367872

Depois disso, começamos a ver uma mensagem de erro bizarra:

26 de janeiro 08:18:49 haproxy1 kernel: [2291.579726] Rota da cadeia de hash por muito tempo!
26 de janeiro 08:18:49 kernel haproxy1: [2291.579732] Ajuste seu secret_interval!

Shh .. é um segredo !!

Aparentemente, isso tem a ver com o /proc/sys/net/ipv4/route/secret_intervalpadrão de 600 e controla a liberação periódica do cache de rota

Ele secret_intervalinstrui o kernel com que freqüência remover TODAS as entradas de hash da rota, independentemente de quão novas / antigas elas sejam. Em nosso ambiente, isso geralmente é ruim. A CPU estará ocupada reconstruindo milhares de entradas por segundo sempre que o cache for limpo. No entanto, configuramos isso para funcionar uma vez por dia para manter vazamentos de memória (embora nunca tivéssemos um).

Embora tenhamos prazer em reduzir isso, parece estranho recomendar a remoção de todo o cache da rota em intervalos regulares , em vez de simplesmente empurrar valores antigos para fora do cache da rota mais rapidamente.

Após algumas investigações, descobrimos /proc/sys/net/ipv4/route/gc_elasticityqual parece ser uma opção melhor para manter o tamanho da tabela de rotas sob controle:

gc_elasticitypode ser melhor descrito como a profundidade média do bucket que o kernel aceitará antes de começar a expirar as entradas de hash da rota. Isso ajudará a manter o limite superior das rotas ativas.

Ajustamos a elasticidade de 8 para 4, na esperança de que o cache da rota se recuperasse de forma mais agressiva. O secret_intervalnão parece correto para nós. Mas há várias configurações e não está claro qual é o caminho certo a seguir.

  • / proc / sys / net / ipv4 / route / gc_elasticity (8)
  • / proc / sys / net / ipv4 / route / gc_interval (60)
  • / proc / sys / net / ipv4 / route / gc_min_interval (0)
  • / proc / sys / net / ipv4 / route / gc_timeout (300)
  • / proc / sys / net / ipv4 / route / secret_interval (600)
  • / proc / sys / net / ipv4 / route / gc_thresh (?)
  • rhash_entries (parâmetro do kernel, padrão desconhecido?)

Nós não queremos fazer o Linux roteamento pior , por isso estamos espécie de medo de mexer com algumas dessas configurações.

Alguém pode aconselhar quais parâmetros de roteamento são melhores para ajustar, para uma instância HAProxy de alto tráfego?

Jeff Atwood
fonte

Respostas:

28

Eu nunca encontrei esse problema. No entanto, você provavelmente deve aumentar a largura da tabela de hash para reduzir sua profundidade. Usando "dmesg", você verá quantas entradas você possui atualmente:

$ dmesg | grep '^IP route'
IP route cache hash table entries: 32768 (order: 5, 131072 bytes)

Você pode alterar esse valor com o parâmetro de linha de comando de inicialização do kernel rhash_entries. Primeiro tente manualmente e adicione-o ao seu lilo.confou grub.conf.

Por exemplo: kernel vmlinux rhash_entries=131072

É possível que você tenha uma tabela de hash muito limitada porque atribuiu pouca memória à sua VM HAProxy (o tamanho do hash da rota é ajustado dependendo da RAM total).

Preocupante tcp_mem, tenha cuidado. Suas configurações iniciais me fazem pensar que você estava executando com 1 GB de RAM, 1/3 dos quais poderiam ser alocados aos soquetes TCP. Agora você alocou 367872 * 4096 bytes = 1,5 GB de RAM para soquetes TCP. Você deve ter muito cuidado para não ficar sem memória. Uma regra prática é alocar 1/3 da memória ao HAProxy e outro 1/3 à pilha TCP e o último 1/3 ao restante do sistema.

Eu suspeito que a mensagem "memória insuficiente" vem das configurações padrão em tcp_rmeme tcp_wmem. Por padrão, você tem 64 kB alocados na saída para cada soquete e 87 kB na entrada. Isso significa um total de 300 kB para uma conexão proxy, apenas para buffers de soquete. Adicione a isso 16 ou 32 kB para o HAProxy e você verá que, com 1 GB de RAM, você suportará apenas 3000 conexões.

Alterando as configurações padrão de tcp_rmeme tcp_wmem(parâmetro do meio), você pode reduzir muito a memória. Eu obtenho bons resultados com valores tão baixos quanto 4096 para o buffer de gravação e 7300 ou 16060 em tcp_rmem(5 ou 11 segmentos TCP). Você pode alterar essas configurações sem reiniciar, no entanto, elas serão aplicadas apenas a novas conexões.

Se você preferir não tocar muito nos seus sysctls , o HAProxy mais recente, 1.4-dev8, permite ajustar esses parâmetros na configuração global e por lado (cliente ou servidor).

Espero que isso ajude!

Willy Tarreau
fonte
8

A Out of socket memory erroré muitas vezes enganosa. Na maioria das vezes, nos servidores voltados para a Internet, isso não indica nenhum problema relacionado à falta de memória. Como expliquei em detalhes muito maiores em uma postagem no blog , o motivo mais comum é o número de soquetes órfãos. Um soquete órfão é um soquete que não está associado a um descritor de arquivo. Em certas circunstâncias, o kernel emitirá o Out of socket memory errormesmo que você esteja 2x ou 4x fora do limite ( /proc/sys/net/ipv4/tcp_max_orphans). Isso acontece com frequência nos serviços voltados para a Internet e é perfeitamente normal. O curso de ação certo nesse caso é ajustar tcp_max_orphanspelo menos quatro vezes o número de órfãos que você normalmente vê com seu pico de tráfego.

Não dê ouvidos a qualquer conselho que recomenda ajuste tcp_memou tcp_rmemou tcp_wmema menos que você realmente sabe o que está fazendo. Aqueles que dão esses conselhos normalmente não o fazem. O vodu deles geralmente é errado ou inapropriado para o seu ambiente e não resolve o seu problema. Pode até piorar.

tsuna
fonte
11
Quando isso acontece, a mensagem é diferente no dmesg, você vê "muitos soquetes órfãos". No entanto, concordo com você que os órfãos podem consumir uma quantidade enorme de memória.
Willy Tarreau
Quando você exceder o número, /proc/sys/net/ipv4/tcp_max_orphansvocê experimentará um erro diferente. A pilha inteira do Stack Exchange, por exemplo, possui /proc/sys/net/ipv4/tcp_max_orphans65536 e /proc/net/sockstatresulta em TCP: inuse 2996 órfão 171 tw 15972 aloca 2998 mem 1621 - uma diferença que não pode ser ignorada.
Geoff Dalgas
-4

Ajustamos alguns desses parâmetros regularmente. Nosso padrão para plataformas de negociação de alta produtividade e baixa latência é:

net.ipv4.tcp_rmem = 4096 16777216 33554432
net.ipv4.tcp_wmem = 4096 16777216 33554432
net.ipv4.tcp_mem = 4096 16777216 33554432
net.core.rmem_default = 16777216
net.core.wmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 30000
net.core.netdev_max_backlog = 30000
Scott Alan Miller
fonte
11
de acordo com a matemática de Willy, isso significa que a pressão de memória padrão # (número do meio) é de 68 GB ?! Vezes três (rmem, wmem, mem) ??
Jeff Atwood
10
Esses ajustáveis ​​estão errados e são freqüentemente encontrados em ambientes de bancada, que são copiados e colados às cegas. Eles não terão nenhum problema com apenas algumas sessões simultâneas, mas mesmo com 100 soquetes TCP, você alocará 3,2 GB de RAM. Enquanto a latência for baixa, você não notará nada suspeito. Você só precisa desconectar uma máquina remota durante uma transferência para ver os buffers de saída preenchidos ou congelar uma tarefa local e ver o preenchimento do buffer de entrada. Isso é uma loucura ...
Willy Tarreau
6
Jeff, isso não é três vezes. tcp_mem está em páginas e define o tamanho global. tcp_rmem e tcp_wmem estão em bytes e definem o tamanho por soquete.
Willy Tarreau
Esses ajustes parecem errados, para servidores simultâneos com dados pequenos, você não deseja reservar tantos buffers de soquete e o tcp_mem é totalmente diferente de r / wmem, usar os mesmos números não faz muito sentido (um é bytes por conexões, o outro páginas por sistema)
eckes