Ir para o conteúdo

Blogue ·

Planear pools de endereços IPv4 para um produto VPS

Os endereços são o único recurso que não se compra numa terça-feira. Como moldar pools, porque é a base de dados que arbitra e o que acontece quando um seca.

Por Provibr

Todo o resto num produto VPS é elástico. Precisa de mais CPU, compra um nó. Precisa de mais disco, compra disco. Precisa de mais endereços e fica numa lista de espera, a pagar a um corretor, ou a preencher um formulário de justificação para um registo regional.

O IPv4 é o único recurso que se comporta como um bem físico e é, normalmente, o que recebe menos atenção de desenho, porque no início tem-se um /24 e 254 parece muito. Deixa de parecer muito exatamente no ponto em que o redesenho fica caro.

Comece pelo que um bloco lhe dá de facto

A contagem utilizável nunca é o tamanho do bloco. Dois endereços vão para a rede e para o broadcast, e mais um vai para o gateway. Isso não é um erro de arredondamento em tamanhos pequenos, é a maior parte do bloco:

BlocoEndereçosRede + broadcastGatewayVendáveisDesperdício
/24256212531%
/273221299%
/2816211319%
/29821538%
/30421175%

Um /29 soa a oito endereços até ao momento exato em que se vendem cinco. Quem compra espaço de endereçamento em blocos pequenos deve orçamentar o desperdício como rubrica própria; num /29 está a deitar fora três oitavos daquilo que pagou.

Dois limites que vale a pena inscrever no que quer que construa. Aceitamos um /8 no máximo, porque para blocos maiores uma barra de ocupação deixa de significar seja o que for, e um /30 no mínimo, porque um /31 e um /32 têm zero endereços utilizáveis segundo as regras normais de anfitrião e aceitá-los só produz um pool que nunca consegue atribuir nada. Recusá-los explicitamente é melhor do que aceitá-los e ficar vazio.

Mais uma subtileza: o endereço de gateway pode ficar dentro do seu intervalo de atribuição e é simplesmente saltado. Isso é deliberado, porque é o arranjo mais fácil de o cliente compreender, mas quer dizer que a capacidade de um pool é um cálculo e nunca um número guardado. Tudo o que mantenha um contador de endereços livres vai discordar da realidade no dia em que alguém mudar um gateway.

Molde os pools em torno do que vende, não do que comprou

A unidade que o cliente vê é um plano. A unidade que comprou a um corretor é um bloco. Essas duas quase nunca têm a mesma forma, e amarrá-las uma à outra corre mal na primeira vez que comprar um segundo bloco.

O arranjo que se aguentou: um plano aponta para um pool, e um pool é um prefixo isolado ou um grupo de prefixos com nome. Acrescentar capacidade a um produto passa então a ser acrescentar um prefixo a um grupo, o que é uma linha. O atribuidor percorre o grupo por ordem e leva o primeiro endereço que conseguir, pelo que um grupo é também uma ordem de esgotamento: ponha à frente o bloco que quer ver consumido primeiro.

Sub-blocos, e o erro que atribui a dobrar em silêncio

Os clientes não usam um /24 como uma rede plana. Cortam-no: um pedaço atrás de um router, um pedaço noutra VLAN, um pedaço com o seu próprio gateway. Um plano de endereçamento precisa portanto de blocos filhos.

É aqui que vive a falha interessante, e vale a pena enunciá-la com precisão, porque falha em silêncio.

O mecanismo que impede duas encomendas simultâneas de receberem o mesmo endereço é um índice único parcial sobre (prefixo, endereço), limitado às linhas que não foram libertadas. Esse índice é por prefixo. Se duas linhas de prefixo puderem distribuir cada uma o mesmo endereço, então, no que toca ao índice, esses são dois endereços diferentes, e a plataforma vai entregar o mesmo IP a duas máquinas sem que absolutamente nada corra mal no software.

O índice não consegue corrigir isso. A disposição é que tem de o fazer. Por isso o espaço de endereçamento é uma partição estrita, e as regras que a garantem são estas:

  1. Um filho fica inteiramente dentro do pai e é estritamente mais pequeno.
  2. Os irmãos não se podem sobrepor.
  3. O pai não distribui nada que caia dentro de um bloco filho. É o bloco inteiro que é retirado da janela do pai, e não apenas o intervalo de atribuição do próprio filho, porque isso faria a resposta depender de um intervalo que algum administrador muda para a semana.
  4. O aninhamento tem um só nível de profundidade.

Com essas quatro, «a que prefixo pertence 192.0.2.70» tem sempre exatamente uma resposta, e o índice existente é suficiente.

O que um filho herda é um conjunto de decisões à parte, e as respostas não são uniformes. Gateway: nunca (um filho sem gateway próprio não tem razão para existir). DNS: sim, se o seu próprio campo estiver vazio, porque os resolvedores pertencem à rede e não ao bloco. Intervalo de atribuição: só onde o intervalo do pai toque efetivamente no filho, uma vez que o intervalo do pai é expresso nos endereços do pai e normalmente não se sobrepõe nada ao filho; herdá-lo às cegas produz uma janela vazia.

Deixe a base de dados arbitrar

A tentação, quando chegam duas encomendas ao mesmo tempo, é bloquear alguma coisa ou manter um contador. Ambas são piores do que a resposta aborrecida.

As duas encomendas calculam o mesmo «endereço livre mais baixo» e ambas tentam inserir. Uma inserção vence, a outra bate na violação de unicidade, e a perdedora passa ao candidato seguinte. A base de dados é a única parte que vê todos os escritores, incluindo o que está noutro processo, noutro worker ou noutro separador do navegador.

O corolário interessa na hora de escrever o código: não verifique primeiro. Ler «este endereço está livre» e inserir a seguir abre precisamente a janela que a restrição existe para fechar. A inserção falhada é a resposta a «estava livre», e é a única fiável.

Um pormenor de implementação que apanha as pessoas: muitos ORM embrulham os erros do driver no seu próprio tipo de erro, pelo que o SQLSTATE que procura não está na exceção que apanha mas na causa dela. Percorra a cadeia toda e use o nome da restrição para decidir que mensagem mostrar. Uma só tabela tem muitas vezes quatro regras de unicidade diferentes, e fazê-las dizer todas o mesmo manda o cliente para o campo errado.

O que acontece quando um pool seca

Escondem-se três falhas diferentes atrás de «nenhum endereço disponível», e juntá-las produz um pedido de apoio de cada vez:

SituaçãoO que significaO que o cliente deve ler
O plano não aponta para pool nenhumConfiguração, e não capacidadeO plano não tem pool de endereços
Um prefixo tem dados inutilizáveisUma linha avariada, as outras bemEstes prefixos não puderam ser lidos
Todos os candidatos estão ocupadosEsgotamento genuínoEste pool está cheio, e aqui fica qual

A do meio vale o código a mais. Um único prefixo com dados ilegíveis não deve bloquear os outros prefixos do seu grupo; registe-o, tente os restantes e só o comunique se nada tiver funcionado de todo. Caso contrário, uma linha avariada derruba as encomendas de uma linha de produto inteira.

E a mensagem de esgotamento deve nomear o prefixo e o seu CIDR. «Nenhum IP disponível» manda alguém ler registos. «Pool cheio: customer-block-2 (198.51.100.0/28)» manda-o comprar endereços.

Devolva os endereços, e tenha cuidado com o quando

A libertação é a metade que faz fugir capacidade em silêncio.

Duas regras que acabaram por interessar mais do que se esperava. Primeiro, libertar não é eliminar a linha: é escrever uma data de libertação. O índice único parcial só olha para as linhas vivas, pelo que o endereço fica imediatamente reutilizável, ao mesmo tempo que «quem tinha o 192.0.2.10 no ano passado» continua a ter resposta. É essa a maior parte da razão para se fazer gestão de endereços, e é a pergunta que uma queixa de abuso faz.

Segundo, a libertação acontece quando a máquina está confirmada como desaparecida, e não quando a eliminação foi pedida. Só quem está ao lado do hipervisor sabe que a VM desapareceu mesmo; libertar no comando de saída entrega o endereço ao cliente seguinte enquanto a máquina antiga ainda corre e ainda responde nele. O caso espelhado é uma encomenda que falha a meio: aí a atribuição é descartada em vez de libertada, porque nunca chegou a ser configurada em coisa nenhuma.

Há aqui uma assimetria deliberada que vale a pena assinalar, porque fazemos a escolha oposta uma camada acima. Os endereços são reutilizados, o livre mais baixo primeiro. Os identificadores de máquina nunca são reutilizados. Uma mensagem de estado atrasada sobre uma VM eliminada a aterrar numa linha nova com o mesmo identificador corrompe o histórico de duas máquinas; um endereço não transporta histórico dessa maneira, e nunca reutilizar nenhum esgotaria em definitivo um /24 ao fim de 254 máquinas. Mesmo produto, regras opostas, razões diferentes.

As reservas pertencem à mesma tabela que as atribuições

Vai precisar de guardar endereços de lado: um balanceador de carga, um gateway num bloco que ainda não implantou, os cinco endereços que alguém prometeu a um cliente para o trimestre seguinte.

Ponha-os na mesma tabela das atribuições reais. Todos os leitores que perguntam «o que está ocupado», o atribuidor, a barra de ocupação, a lista de sugestões num campo de endereço, passam a contá-los automaticamente, e não há uma segunda administração para ficar dessincronizada. Uma tabela de reservas à parte é uma segunda fonte de verdade que vai discordar da primeira dentro de um mês.

Com uma exceção que é fácil de fazer mal: um endereço reservado nunca pode acabar na lista de permissões do firewall de uma máquina. Essa lista é construída a partir das atribuições que pertencem a um servidor, e uma reserva não pertence a servidor nenhum. Falhe nisso e a VM de um cliente começa a aceitar tráfego endereçado ao balanceador de carga de outra pessoa.

E o IPv6

Reconhecemos entrada de IPv6 e recusamo-la com educação, em vez de a aceitar e calcular algo plausível: um caminho de IPv6 meio funcional é mais perigoso do que caminho nenhum, porque os números que produz parecem corretos. A decisão barata que se paga a si própria é guardar endereços, gateways e servidores DNS como texto e não como um inteiro de 32 bits, para que acrescentar IPv6 mais tarde seja código e não uma migração de todas as linhas que possui.

A versão curta

  • Orçamente o desperdício. Um /29 são cinco endereços vendáveis, e não oito.
  • Aponte os planos para pools, e os pools para prefixos. Comprar mais espaço devia ser uma linha.
  • Se permitir sub-blocos, faça do espaço de endereçamento uma partição estrita, ou a regra de unicidade que o protege passa a ser, em silêncio, por prefixo, e deixa de proteger seja o que for.
  • Deixe a restrição de unicidade decidir a corrida. Nunca verifique primeiro.
  • Liberte na confirmação, e não no pedido, e guarde o histórico.
  • Nomeie o pool na mensagem de esgotamento. É a diferença entre um pedido de apoio e uma nota de encomenda.

Isto saiu da construção da gestão de endereços no Provibr, onde os endereços são emitidos quando uma encomenda é feita e devolvidos quando o agente confirma que a máquina desapareceu mesmo.

Comece com um host e um agente

Crie uma conta, instale o agente com um único comando e ligue o seu primeiro host Proxmox. O período experimental dura 14 dias e não pede quaisquer dados de pagamento.