Blogue ·
Escolher um intervalo de VMID no Proxmox
Um VMID é uma identidade, não um contador. O intervalo que escolher decide o que a plataforma pode criar, o que pode ler e o que acontece quando enche.
Por Provibr
Todas as instalações do Proxmox começam no 100 e contam para cima. Isso funciona bem até a máquina deixar de ser só sua: até haver um painel de controlo a criar convidados nela, um colega a criar convidados à mão, uma biblioteca de modelos e uma tarefa de cópia de segurança que repõe alguma coisa às três da manhã.
Nesse momento, o VMID deixa de ser um contador e passa a ser uma identidade, e o intervalo que entrega à sua automatização deixa de ser uma comodidade e passa a ser uma fronteira. Vale a pena escolhê-lo de forma deliberada, porque duas das três coisas que um intervalo decide são invisíveis até correrem mal.
O que o Proxmox permite de facto
O espaço completo é mais largo do que a maioria das pessoas supõe:
| Valor | |
|---|---|
| VMID mais baixo que o Proxmox aceita | 100 |
| VMID mais alto que o Proxmox aceita | 999999999 |
| Utilização manual habitual | 100 até algumas centenas |
Nove dígitos. Não é um convite a usá-los todos, mas quer dizer que a preocupação habitual, «vou ficar sem números», é a preocupação errada. Um intervalo mal escolhido acaba muito antes de acabarem os números.
Esses dois limites são os únicos que o hipervisor impõe. Todo o resto, a parte que interessa, é uma decisão sua.
Um intervalo é uma fronteira de permissões antes de ser um atribuidor
Aqui está a parte que é fácil deixar passar. Quando se diz a uma plataforma «atribui de 5000 a 8999», dizem-se-lhe duas coisas ao mesmo tempo:
- O que pode criar. Os convidados novos recebem um número dessa janela e de mais nenhum sítio.
- O que não lhe pertence. Tudo o que está fora dessa janela foi feito por outra pessoa, e a plataforma não tem nada que lhe tocar.
A segunda merece ficar escrita na parede. Quando construímos a integração com o Proxmox, o intervalo de vmid nas definições da integração é descrito no código exatamente assim: a plataforma atribui apenas dentro de [vmidRangeStart, vmidRangeEnd], pelo que uma integração não consegue, por construção, chegar a uma VM fora dele. Quem mantiver as suas VM de produção fora do intervalo mantém-nas fora de alcance.
Esse enquadramento muda a forma de escolher os números. Não está a escolher onde a sua automatização começa a contar. Está a traçar uma linha à volta das máquinas que está disposto a deixar que um programa elimine.
O lado da leitura é uma questão separada, e morde mais tarde
Criar é a metade óbvia. Ler é a metade que nos surpreendeu.
O nosso agente consulta o cluster uma vez por ronda com um único GET /cluster/resources?type=vm, que não toca em convidado nenhum. Mas, para cada convidado em execução nessa resposta, fazia depois mais duas chamadas ao agente convidado do QEMU, network-get-interfaces e get-fsinfo, para saber endereços e utilização de disco. Essa lista vinha diretamente da consulta ao cluster, sem filtro.
Num hipervisor de testes que corre também cargas de produção, essa lista incluía as máquinas de produção. As leituras são inofensivas no sentido de que nada é escrito. Não são inofensivas no sentido que interessa, porque «só de leitura» não é resposta à pergunta porque está o vosso agente a falar com o agente convidado de uma máquina que não é vossa.
A correção é que o mesmo intervalo limita agora as leituras. Se estiver a construir isto por si próprio, a lição generaliza-se: uma fronteira que só cobre escritas é meia fronteira, e a metade da leitura é a que ninguém nota que falta.
Onde deve viver o intervalo
Tivemos de responder a isto uma vez e vale a pena expor o raciocínio, porque a resposta óbvia está errada.
A resposta óbvia é pôr o intervalo ao lado das credenciais, no bloco que a plataforma envia ao agente quando a integração é criada. É uma mensagem só, chega com tudo o resto, feito.
Isso falha num reinício. As credenciais são enviadas uma única vez; depois de um reinício, o agente reconstrói as suas integrações a partir do cofre local, sem envio nenhum. Uma fronteira entregue por esse caminho desaparece em silêncio à primeira vez que o processo reinicia, e uma fronteira que deixa de se aplicar sem dizer nada é pior do que fronteira nenhuma, porque ninguém vai à procura dela.
Por isso o intervalo viaja na sua própria mensagem, enviada em cada aperto de mão de sessão, com o estado completo. Dois caminhos de entrega, uma única fonte de verdade. Existe também uma variável de ambiente ao nível do anfitrião como recurso alternativo, e a ordem entre as duas é deliberada:
| Fonte | Quando vence |
|---|---|
| A definição da plataforma | Sempre que tenha enviado alguma |
| Variável de ambiente do anfitrião | Só quando a plataforma nada disse |
| Nenhum limite | Quando nenhuma das duas disse nada |
A ordem tentadora é a inversa, na teoria de que um cinto apertado no anfitrião é mais forte. Produz um estado que ninguém consegue ver: um administrador que um dia definiu uma variável mantém em silêncio uma fronteira que já não corresponde ao que o painel mostra, e um cliente que alarga o intervalo na interface não percebe porque é que nada mudou.
Um pormenor pequeno com um modo de falha grande: um valor ilegível é tratado como «ninguém», e não como «toda a gente». Quem pede uma fronteira e comete um erro de escrita fica sem leituras nenhumas, mais uma linha de registo a dizer porquê. O custo é cosmético: falta um endereço e um número de disco no painel. A alternativa, recuar para «tudo é permitido», é a falha clássica de uma lista de permissões: falha aberta, em silêncio, exatamente no caso em que alguém estava a tentar ter cuidado.
Os modelos ficam fora do intervalo. Sempre.
Esta é uma regra e não uma preferência, e é o erro que se come a si próprio.
Um atribuidor distribui números do intervalo. Ponha-se um modelo dentro do intervalo e um dia o atribuidor chega lá, uma encomenda nova é criada nesse número e o modelo passa a ser uma VM de cliente. A encomenda seguinte clona a partir de uma máquina que já não é um modelo. Por isso o validador recusa um VMID de modelo do Proxmox que caia dentro do intervalo da integração, e o atribuidor trata além disso todos os modelos associados como ocupados, para o caso de alguém ter alargado o intervalo depois de associar um.
Fora do intervalo, um modelo pode viver em qualquer sítio. O intervalo limita o que a plataforma cria, não o sítio onde o material de origem pode estar.
Nunca reutilizar um número, mesmo quando a máquina desapareceu
O atribuidor distribui o VMID livre mais baixo do intervalo, e «livre» é mais estrito do que se esperaria: todos os VMID que a integração alguma vez distribuiu contam como ocupados, incluindo os que pertenciam a servidores eliminados.
Isso parece um desperdício durante uns cinco segundos, até se pensar no que a reutilização significaria. Um evento de estado atrasado da VM antiga aterra na linha nova. O histórico de auditoria de dois servidores diferentes junta-se numa só cronologia. Alguém repõe a cópia de segurança de ontem num número que agora pertence a outro cliente.
Curiosamente, fazemos a escolha oposta para os endereços IP no mesmo produto, e por uma boa razão: um endereço é um endereço, e nunca reutilizar nenhum esgotaria em definitivo um /24 ao fim de 254 máquinas. Um VMID é o nome do histórico de uma máquina. Os intervalos são baratos; a confusão não é.
Há uma terceira categoria de «ocupado» que só aparece quando existe mais do que uma coisa a criar máquinas. Um restauro ainda em curso já reclamou o VMID de destino mas ainda não tem linha de servidor, pelo que um segundo restauro no mesmo minuto receberia o mesmo número «livre». Tudo o que reserve um VMID antes de a linha existir tem de ser contado.
O que acontece quando o intervalo enche
Duas respostas, e ambas devem existir.
A simpática é aritmética: o atribuidor percorre o intervalo, não encontra nada livre e devolve uma falha específica que nomeia o início e o fim do intervalo. O cliente lê «este intervalo está cheio, de 5000 a 8999» em vez de um erro do hipervisor quatro camadas mais abaixo.
A verdadeira é a base de dados. A restrição de unicidade sobre (integração, identificador externo) é o árbitro: duas encomendas feitas no mesmo instante podem ver ambas o mesmo número livre mais baixo, e exatamente uma inserção sobrevive. A perdedora volta a tentar, até um número fixo e pequeno de tentativas. Verificar primeiro e inserir depois abre precisamente a corrida que a restrição fecha, pelo que a restrição é o mecanismo e a mensagem simpática é uma cortesia por cima dele.
Repare no que não usamos: o Proxmox tem um endpoint de próximo identificador livre, e é a ferramenta errada aqui. Responde a «que número está livre neste cluster», que é uma pergunta diferente de «que número está livre e dentro da janela que me pertence». Pergunte-se ao hipervisor e, mais cedo ou mais tarde, recebe-se um número fora da própria fronteira.
Então, na prática
- Comece alto e deixe os números baixos em paz. De 100 até algumas centenas é onde vivem as máquinas feitas à mão em todas as instalações do Proxmox, incluindo a sua daqui a três anos, quando já se tiver esquecido desta decisão.
- Escolha um intervalo com uma forma óbvia. De 5000 a 8999 lê-se como «nosso» num relance na interface web. Um intervalo de 4096 números são quatro mil máquinas numa só integração.
- Ponha os modelos fora dele, no seu próprio bloco, bem longe.
- Escreva o intervalo onde a plataforma o consiga ver, e não apenas num ficheiro de configuração do anfitrião, e garanta que é reenviado em vez de enviado uma só vez.
- Verifique que as leituras também estão limitadas, e não apenas as criações e as eliminações.
O número em si não interessa muito. O que interessa é que a fronteira seja uma só coisa, visível num só sítio, aplicada a leituras e escritas por igual, e que falhe fechada quando não a conseguir ler.
Isto saiu da construção da integração com o Proxmox para o Provibr, onde o agente corre dentro da rede do próprio cliente e só se liga para fora.