3 pontos por GN⁺ 2024-10-27 | 1 comentários | Compartilhar no WhatsApp
  • O operador do OpenFreeMap agrupou VPS de várias regiões como registros A do mesmo subdomínio para montar um DNS round robin e testou quais servidores o navegador e o Cloudflare realmente escolhem
  • Sem um load balancer separado, é possível esperar distribuição de carga e desvio de falhas, mas o resultado real depende muito de como o cliente ordena endereços e faz tentativas novamente
  • Em testes com 3 VPS nos EUA, Europa e Singapura, Chrome e Firefox tenderam a fixar um servidor aleatório no início, enquanto Safari e curl convergiram para o servidor da UE mais próximo após requisições repetidas
  • Quando alguns servidores ficam offline, navegadores e curl mudam rapidamente para um servidor alternativo, mas requisições via proxy do Cloudflare podem continuar usando a origin definida por IP do cliente e gerar erro 521
  • Se o Cloudflare não escolher corretamente uma origin offline ou o servidor com menor latência, a distribuição baseada em DNS round robin pode acabar conectando usuários a um servidor lento, independentemente da localização deles

A ideia básica do DNS round robin

  • Um site comum baseado em VPS envia tráfego para um IP específico ao adicionar um único registro A no provedor de DNS
  • DNS round robin é uma forma de definir vários IPs de servidor para o mesmo subdomínio
    • O exemplo usa uma configuração com vários registros A em rr-direct.hyperknot.com e rr-cf.hyperknot.com
  • Nessa configuração, pode-se esperar dividir a carga entre vários servidores e evitar servidores offline
  • Na maioria dos provedores de DNS, isso pode ser configurado sem um load balancer dedicado, sendo uma abordagem simples e quase gratuita
  • Recursos de balanceamento de carga em serviços como Cloudflare podem ter custo elevado

Com base em quê o cliente pode escolher um servidor?

  • Os padrões relacionados incluem RFC 8305 Happy Eyeballs e RFC 6724
  • A seção de ordenação de endereços do RFC 8305 explica que, se um cliente com estado tiver registro do tempo de ida e volta (RTT) esperado para cada caminho de endereço, ele deve adicionar regras de seleção de endereço de destino que priorizem endereços com RTT mais baixo
  • O experimentador interpreta isso como o seguinte comportamento
    • verificar se o servidor está online ou offline
    • ordenar os servidores online com base no tempo de ping

Configuração do experimento

  • Foram criadas VPS em 3 regiões do mundo
    • Estados Unidos
    • Europa
    • Singapura
  • No Cloudflare, foram configurados 3 registros A com proxy e 3 registros A sem proxy
  • Cada servidor usa nginx para fornecer a mesma estrutura de resposta
    • todas as requisições de caminho são reescritas para color.png
    • /server retorna /etc/hostname como text/plain
  • color.png é um arquivo PNG de 1px, com uma cor diferente em cada servidor
    • EUA: verde
    • UE: azul
    • SG: vermelho
  • Os nomes de host são diferenciados como test-eu, test-us e test-sg
  • Como o local de teste é na Europa, o comportamento esperado é selecionar o servidor da UE mais próximo
  • A página HTML de teste preenche uma grade 10x10 com imagens aleatórias para visualizar o resultado da escolha do servidor

Com todos os servidores online: comportamento por cliente

  • O Chrome tende a escolher um local meio aleatório entre vários e, depois de escolhido, permanecer nesse servidor
    • ele reavalia a escolha algumas horas depois
    • no teste, chegou a ficar preso por horas ao servidor mais lento, em Singapura
    • quando não usa HTTP/2, às vezes escolhe aleatoriamente entre dois servidores e forma um padrão
  • O Firefox se comporta de forma parecida com o Chrome
    • escolhe um local aleatório ao iniciar
    • ao reiniciar o navegador, pode escolher outro local aleatório
  • O Safari sempre escolhe corretamente o servidor mais próximo
    • mesmo que o servidor fique offline por um momento e volte, após alguns refreshs ele encontra novamente o servidor da UE
  • O curl também se ajusta para o servidor mais próximo
    • na primeira execução isso pode não acontecer, mas ao rodar o comando duas vezes ele sempre migra para o servidor mais próximo
    • no exemplo, a primeira requisição vai para test-us e a seguinte muda para test-eu

Comportamento via proxy do Cloudflare

  • O Cloudflare escolhe uma localização aleatória com base no IP do cliente e continua usando sempre essa mesma localização
  • O comportamento observado se parece com client_ip_hash modulo server_num
  • No IP residencial, não importa o que fosse feito, o Cloudflare conectava ao servidor dos EUA
  • Em um hotspot móvel, ele sempre conectava ao servidor da UE
  • Ao executar o mesmo comando curl em várias VPS, cada VPS conectava a uma localização aleatória no mundo, mas sempre usava o mesmo servidor
    • no exemplo, o resultado era test-sg

Diferenças quando alguns servidores ficam offline

  • O nginx foi interrompido no servidor dos EUA com service nginx stop para verificar o comportamento
  • Chrome, Firefox, Safari e curl detectaram o servidor offline e escolheram outro servidor
  • Mesmo desligando o servidor durante o carregamento, a conexão alternativa se ajustou rapidamente, em menos de 1 segundo
  • O Cloudflare não conseguiu detectar o servidor offline
    • ele continuou acessando o servidor definido uma vez para aquele IP de cliente, independentemente de estar online ou não
    • se esse servidor estiver offline, o usuário recebe erro
    • o resultado do curl foi error code: 521

Dúvidas e limitações sobre o Cloudflare

  • Considera-se que o comportamento do Cloudflare de não detectar uma origin offline provavelmente seja um bug da rede
  • Com base na documentação do Cloudflare sobre zero downtime failover, a avaliação é que ele deveria se comportar como navegadores e curl
  • No mínimo, servidores offline deveriam ser detectados
  • Seria melhor ainda se ele pudesse escolher o servidor com menor latência, como o Safari
  • No comportamento atual, se houver 1 servidor nos EUA e 1 na Nova Zelândia, 50% dos usuários dos EUA podem receber resposta do servidor da Nova Zelândia
  • Usuários do Safari podem ficar mais lentos usando Cloudflare do que sem Cloudflare
  • Na discussão no HN relacionada, o CEO e o CTO do Cloudflare responderam
  • O autor também pergunta se existe uma plataforma serverless que suporte HTTPS e DNS round robin sem custo para manter o experimento rodando com 3 VPS espalhadas pelo mundo

1 comentários

 
GN⁺ 2024-10-27
Opiniões no Hacker News
  • Bem, pedi à equipe de DNS autoritativo que explicasse o que está acontecendo aqui.
    Quando eu tiver uma resposta definitiva, aviso no HN. Faz alguns anos que vi o código, e muita gente continuou mexendo nele desde então :-)
    Meu palpite é que isso esteja relacionado ao comportamento de manter uma preferência entre IP do cliente e servidor de backend, como o autor mencionou no blog. A pergunta central é: “se um servidor de backend cair, essa preferência deve ser quebrada?”; quando eu souber mais, deixo uma resposta ao meu comentário.

    • Muitos pecados foram cometidos em nome da preferência de sessão.
    • Atualização: uma mudança está sendo implantada para que também haja failover sem interrupção em contas gratuitas.
  • Uma das primeiras soluções para esse problema foram os registros DNS SRV. Eram parecidos com registros MX, mas a ideia era aplicá-los a todos os serviços, não só a e-mail.
    Em registros MX e SRV, é possível especificar a lista de servidores que o cliente deve tentar e suas prioridades; no SRV também havia um parâmetro weight para balanceamento de carga. Mas, para evitar a briga política de basicamente interceptar todos os protocolos padrão e fazer com que todos os clientes consultassem SRV, ficou definido que SRV só seria usado quando o padrão daquele protocolo especificasse seu uso. Assim, tecnicamente, clientes HTTP não puderam usar SRV. Depois, na criação do HTTP/2 e dos padrões HTTP posteriores, argumentos inadequados vindos do Google e outros impediram que SRV fosse especificado nos novos protocolos HTTP. SRV está praticamente morto para novos desenvolvimentos e parece ser usado apenas em alguns padrões antigos.
    A nova solução de balanceamento de carga parece ser os registros DNS HTTPS e SVCB. Pelo que entendo, isso foi padronizado por pessoas que queriam colocar parâmetros adicionais no DNS para iniciar mais cedo o handshake do TLS 1.3 e reduzir o número de viagens de ida e volta. O tipo de registro SVCB é igual ao HTTPS, mas em uma forma generalizada como o SRV. HTTPS e SVCB têm os parâmetros de prioridade de SRV e MX, mas não têm o parâmetro weight do SRV. O padrão foi publicado e parece que alguns navegadores já têm suporte, mas nem todos o ativaram. Teremos de ver o que os navegadores farão de fato no futuro próximo.

    • Outra grande vantagem dos registros HTTPS é que eles permitem uma delegação adequada, tipo CNAME, no apex do domínio.
      Eles não exigem o truque de CNAME flattening, que pode causar problemas de roteamento em CDNs que usam GeoDNS junto com anycast ou no lugar dele. Se você já viu alguma plataforma recomendar o uso do subdomínio www em vez do domínio apex, é por isso; e isso também é um dos motivos pelos quais a Akamai, por usar GeoDNS, impulsionou a padronização dos registros HTTPS.
    • Eu realmente gostaria que algum registro no estilo SRV ou MX utilizável com HTTP fosse adotado direito.
      Como as pessoas frequentemente querem hospedar sites no apex do domínio, a falta desse tipo de registro dói especialmente. Dito isso, se não for possível depender de DNSSEC, pode ser complicado usar registros no estilo MX com segurança.
  • O balanceamento de carga via DNS tem casos de borda bem sujos. Já lidei com uma situação em que o cliente Go HTTP/2 usava DNS round-robin, e deu problema.
    O cliente Go HTTP/2 continua reutilizando o primeiro servidor ao qual consegue se conectar e não resolve o DNS novamente. Por causa disso, mesmo que você adicione um novo servidor ao pool, o cliente pode nunca descobri-lo.
    Um caso particularmente patológico é quando todos os backends caem e então apenas o primeiro backend volta: todos os clientes ficam presos nesse servidor e não saem dele. Mesmo que os outros servidores voltem, como eles já estão conectados ao primeiro servidor, quase não há clientes fazendo novas conexões.
    Um problema parecido acontece no grpc-go. O resolvedor DNS do gRPC só resolve novamente quando a conexão com o backend é interrompida. Assim, clientes gRPC podem se concentrar em um único host e continuar lá. Há também a sugestão de configurar MAX_CONNECTION_AGE no lado do servidor para, depois de certo tempo, desconectar periodicamente os clientes e fazê-los resolver o DNS de novo.
    Eu gostaria que houvesse uma solução padrão melhor para descoberta de serviços. No fim, parece que o melhor que dá para fazer é implementar um balanceador de carga por requisição baseado em IP virtual e fazer o balanceador executar health checks. Mas isso também apenas empurra o problema para o sistema que implementa o IP virtual. Parece que se assume que o sistema de roteamento é relativamente mais estático que os backends, e é daí que vem o ganho.
    Fico curioso para saber como isso é feito em bare metal. Sei que AWS/GCP etc. têm balanceadores de carga internos, mas tenho curiosidade sobre qual é o segredo para implementar isso. Recomendações de posts de blog ou white papers relacionados também seriam bem-vindas.

    • Não sou especialista em DNS, mas não deveria resolver novamente quando o TTL expira?
  • O texto diz: “o que acontece se um servidor ficar offline? Digamos que paremos o servidor dos EUA: service nginx stop”, mas você não deve testar assim.
    O cliente vê a conexão recusada e passa para o próximo IP. Mas, no mundo real, o servidor pode simplesmente não responder, ou pode aceitar a conexão e depois ficar em silêncio.
    Nesse caso, você passa a depender do timeout do cliente, e o DNS round-robin, que deveria aumentar a confiabilidade, de repente parece bem menos atraente.

    • Exato. Dá para testar desligando a máquina física ou VM que tem aquele IP, ou desconectando o cabo.
      Parar o serviço é uma operação planejada; nesse caso, você pode lidar com isso atualizando o DNS antes.
    • SIG_STOP ou DROP com ip/nftables é um teste muito mais realista.
  • “Como você pode ver, todos os clientes detectam isso corretamente e escolhem um servidor alternativo” é o ponto central problemático. A confiabilidade é decidida no lado do cliente
    Por exemplo, o systemd-resolved certa vez se comportava de forma maximamente tecnicamente correta, sempre retornando o endereço IP mais baixo. A lógica era que, como round-robin de DNS não é bem definido, retornar sempre o menor IP não estaria errado. Depois da confusão isso mudou, mas, até onde sei, o Debian 11 ficou preso a esse comportamento, ou ficou assim por muito tempo
    Também lido com muitos aplicativos cujo comportamento de retry é péssimo ou inexistente. Eles agem como: “deu uma conexão recusada, vamos cancelar tudo, encerrar e nunca mais tentar”. Aí 20–30% de todas as requisições simplesmente viram fumaça
    Se não houver outra opção, é uma solução aceitável. Como o texto diz, se você tiver um cliente HTTP de boa qualidade, como um navegador, com algumas tentativas de retry configuradas, o round-robin de DNS serve bem para encontrar um balanceador de carga real com health checks etc., e pode entregar uma taxa de sucesso de 100%
    Mas round-robin de DNS não é um balanceador de carga, e um balanceador de carga é melhor

    • Por outro lado, se você controla os clientes e consegue garantir o comportamento deles, balanceamento de carga via DNS é muito eficaz
      Em um lugar onde trabalhei antes havia um servidor DNS interno com centenas de milhões de registros e TTL de 60 segundos, usado em um sistema interno de roteamento customizado que conectava as conexões de entrada dos clientes aos recursos corretos dentro da rede. Na prática, era excelente. Alterações de roteamento eram tão simples quanto atualizações de DDNS, e com NOTIFY as mudanças eram empurradas para todos os servidores subordinados, com latência média de propagação total abaixo de 60 segundos. Isso facilitava criar ferramentas mais complexas, e também criamos um painel de controle para remover do serviço, com um clique, desde um único servidor até um datacenter inteiro
      Esse sistema certamente tinha arestas, mas, para esse tipo de sistema, era rápido, fácil de inspecionar e relativamente à prova de falhas
    • Você acaba colocando a confiabilidade nas mãos do cliente ou de algum resolvedor DNS com cache arbitrário por trás dele
      O failover é a mesma coisa. Se uma região cair, você quer que o tráfego se espalhe uniformemente para outras regiões, ou que se concentre na região vizinha mais próxima? Se esse comportamento importa, você precisa manter o controle do gerenciamento de tráfego, não entregá-lo a terceiros
    • Também é difícil concordar com a ideia de que “se não houver outra opção, é uma solução aceitável”
      Hoje em dia há outras soluções a escolher muito antes de chegar a esse ponto de “não há outra opção”
  • Para implicar com cuidado com a expressão “é possível dividir a carga entre vários servidores e detectar automaticamente quais servidores estão offline para escolher servidores online”, no que diz respeito à detecção automática de offline via DNS, o round-robin de DNS no estado básico só serve mesmo para balanceamento de carga
    A menos que você coloque lógica inteligente no cliente, nada acontece automaticamente em termos de detecção de estado de disponibilidade. A introdução do texto até fala disso em certa medida, mas precisei reler várias vezes para entender o sentido. Para ser justo, pode ser um problema da minha compreensão. Ao ler o restante do texto depois, vi que ele era todo sobre essa lógica inteligente
    Se o registro do servidor 1/N escolhido pelo navegador estiver indisponível, não há recuperação automática nem retry no nível do protocolo
    Além disso, como “curiosidade relacionada”: não esquecer do TTL de DNS do Java [1] e do comportamento de .equals() [2]
    [1] https://stackoverflow.com/questions/1256556/how-to-make-java...
    [2] https://news.ycombinator.com/item?id=21765788 (5 anos atrás, 168 comentários)

    • No Route53, eles lidam com isso removendo da resposta DNS o servidor que não está saudável e fornecendo todas as respostas com um TTL muito baixo
      Existem clientes que ignoram o TTL, mas são bem raros
    • Fazendo um pouco de divulgação: é um projeto livre e open source que oferece failover para round-robin de DNS e recebe apoio da NLnet: https://codeberg.org/FedericoCeratto/rrdnsd
  • Se um servidor cair, os endereços IP distribuídos e em cache pelo mundo continuam existindo, e não há como impedir que as pessoas acessem por esses endereços
    https://www.cloudflare.com/learning/dns/glossary/round-robin...

    • Vale a pena considerar pular camadas intermediárias desnecessárias
      Balanceamento de carga também tem custo, e há ainda o problema de balanceadores de carga quebrarem conexões de forma sutil ou explícita. Em um provedor, a disponibilidade do balanceador de carga já foi pior do que a dos nossos hosts
      Se você controla o cliente, também é razoável chamar a API de DNS da plataforma para obter a lista de IPs, embaralhá-la e percorrê-la adequadamente. Melhor ainda se você puder colocar no binário do cliente alguns IPs alocados de forma estável para o caso de o DNS quebrar. Mas o DNS normalmente não está quebrado, e é útil para mudanças operacionais sem precisar distribuir uma nova configuração ou um novo binário toda vez que o cluster é atualizado
      Se o cliente for um navegador, o comportamento padrão é até bom. Normalmente ele usa os IPs em ordem, o que pode ser um problema [1], mas, fora isso, o comportamento de novas tentativas é bom. Se houver recusa de conexão, ele tenta outro IP imediatamente; se houver timeout, tenta pelo menos alguns outros IPs. Não é ideal, então eu usaria um balanceador de carga para navegadores e, se possível, pelo menos no carregamento inicial da página. Para WebSocket e afins, talvez desse para usar round-robin DNS com uma lógica de cliente em JS razoavelmente inteligente. Ainda assim, é possível usar round-robin DNS para o site inteiro
      Se o cliente não for um navegador e eu também não o controlar, só resta desejar boa sorte
      Reconheço 100% que, às vezes, é preciso presumir que alguém criou um resolvedor DNS com cache que interpreta o campo TTL em dias, não em segundos. Clientes atrás desses resolvedores terão problemas quando o DNS for atualizado. Mas, se o balanceador de carga estiver atrás de um nome DNS e chegar a hora de trocar esse endereço, você acabará enfrentando esse mesmo problema — e, nesse momento, sem experiência
      [1] Um dos RFCs sugere que a API do SO deve ordenar as respostas por correspondência de prefixo. Isso poderia fazer sentido se os prefixos IP fossem hierárquicos e servissem como proxy para escolher o servidor com menor distância de rede. Mas, na prática, /24 numericamente adjacentes muitas vezes não são adjacentes na rede. Se os endereços dos servidores estiverem bem espalhados, você pode ver o tráfego de alguns IPs de clientes se concentrando em IPs de servidores numericamente parecidos
    • Os clientes testados no artigo se comportaram corretamente e escolheram um dos servidores alcançáveis
      Claro que sempre haverá alguém com DNS local mal configurado ou usando um cliente ruim. É preciso aceitar a indisponibilidade para pessoas com configurações quebradas, ou realocar o IP para outro servidor no mesmo datacenter
    • Hoje, a abordagem padrão é usar um TTL relativamente baixo e fazer o servidor DNS executar health checks nos membros do pool
  • Olá. Sou o CTO da Cloudflare. Implementamos uma mudança em todas as contas gratuitas da Cloudflare para que tenham o mesmo comportamento das contas pagas
    O problema mencionado aqui foi corrigido, e o Zero Downtime Failover deve funcionar em todos os tipos de conta. Você poderia testar novamente?
    Obrigado por documentar isso em um post. Fico feliz por podermos mudar esse comportamento para todos

    • Testei de novo e funciona muito bem
      Vou atualizar o artigo de acordo. Obrigado por aplicar isso também às contas gratuitas; é um ótimo resultado
  • A versão remix sombria disso é fast flux hosting, uma técnica usada por muitos provedores de bulletproof hosting
    https://unit42.paloaltonetworks.com/fast-flux-101/

  • Pode valer a pena mencionar que failover sem downtime é um recurso Pro ou superior
    Lembro que isso era documentado assim antigamente, quando a documentação de proteção do servidor de origem ainda era dividida por nível de plano. Por isso, o comportamento ou as novas tentativas podem parecer diferentes