Blog ·
Een VMID-range kiezen voor je hostingbedrijf
Een VMID is een identiteit, geen teller. Wat de range die je kiest bepaalt: wat je platform mag aanmaken, wat het mag lezen, en wat er gebeurt als hij vol is.
Door Provibr
Elke Proxmox-installatie start bij 100 en telt omhoog. Dat werkt prima tot de machine ophoudt alleen van jou te zijn: tot er een controlepaneel gasten op aanmaakt, een collega met de hand gasten aanmaakt, er een sjabloonbibliotheek staat, en een backuptaak om drie uur 's nachts iets terugzet.
Op dat moment houdt de VMID op een teller te zijn en wordt hij een identiteit, en houdt de range die je aan je automatisering geeft op een gemak te zijn en wordt hij een grens. Het is de moeite waard die keuze bewust te maken, want twee van de drie dingen die een range bepaalt zijn onzichtbaar tot ze misgaan.
Wat Proxmox werkelijk toestaat
De volledige ruimte is breder dan de meeste mensen aannemen:
| Waarde | |
|---|---|
| Laagste VMID die Proxmox accepteert | 100 |
| Hoogste VMID die Proxmox accepteert | 999999999 |
| Typisch handmatig gebruik | 100 tot een paar honderd |
Negen cijfers. Dat is geen uitnodiging om ze allemaal te gebruiken, maar het betekent wel dat de gebruikelijke zorg, "raak ik ze op", de verkeerde zorg is. Je raakt een slecht gekozen range veel eerder op dan de nummers.
Die twee grenzen zijn de enige die de hypervisor afdwingt. Al het andere, het deel dat ertoe doet, is een beslissing die jij neemt.
Een range is eerst een rechtengrens en pas daarna een toewijzer
Hier zit het deel dat makkelijk over het hoofd te zien is. Als een platform te horen krijgt "jij geeft uit van 5000 tot 8999", dan zijn dat twee mededelingen tegelijk:
- Wat het mag aanmaken. Nieuwe gasten krijgen een nummer uit dat venster en nergens anders vandaan.
- Wat niet van jou is. Alles buiten dat venster is door iemand anders gemaakt, en het platform heeft daar niets te zoeken.
De tweede mag op de muur. Bij het bouwen van de Proxmox-integratie hebben we de vmid-range in de integratie-instellingen in de code precies zo omschreven: het platform geeft alleen uit binnen [vmidRangeStart, vmidRangeEnd], dus een integratie kan per constructie geen VM buiten die range bereiken. Wie zijn productie-VM's buiten de range houdt, houdt ze buiten bereik.
Die formulering verandert hoe je de nummers kiest. Je kiest niet waar je automatisering begint te tellen. Je trekt een lijn om de machines die je bereid bent door een programma te laten verwijderen.
De leeskant is een aparte vraag, en die bijt later
Aanmaken is de voor de hand liggende helft. Lezen is de helft die ons verraste.
Onze agent bevraagt het cluster één keer per ronde met één GET /cluster/resources?type=vm, en raakt daarmee geen enkele gast aan. Maar voor elke draaiende gast in dat antwoord deed hij daarna nog twee aanroepen naar de QEMU-gastagent, network-get-interfaces en get-fsinfo, om adressen en schijfgebruik te weten te komen. Die lijst kwam ongefilterd rechtstreeks uit de clusterquery.
Op een testhypervisor waar ook productieworkloads op draaien, stonden de productiemachines in die lijst. De leesacties zijn onschuldig in de zin dat er niets geschreven wordt. Ze zijn niet onschuldig in de zin die ertoe doet, want "alleen-lezen" is geen antwoord op de vraag waarom praat jouw agent met de gastagent van een machine die niet van jou is.
De reparatie is dat dezelfde range nu ook de leesacties begrenst. Bouw je dit zelf, dan generaliseert de les: een grens die alleen schrijfacties dekt is een halve grens, en de leeshelft is de helft waarvan niemand merkt dat hij ontbreekt.
Waar de range hoort te staan
We moesten dit één keer beantwoorden en de redenering is het opschrijven waard, want het voor de hand liggende antwoord is fout.
Het voor de hand liggende antwoord is de range naast de inloggegevens zetten, in welk blok het platform ook naar de agent stuurt zodra de integratie wordt aangemaakt. Het is één bericht, het komt met de rest mee, klaar.
Dat faalt bij een herstart. Inloggegevens worden één keer gestuurd; na een herstart bouwt de agent zijn integraties opnieuw op uit zijn lokale kluis, zonder push. Een grens die over dat pad wordt afgeleverd verdwijnt stilletjes bij de eerste herstart van het proces, en een grens die stil ophoudt te gelden is erger dan geen grens, want niemand gaat ernaar zoeken.
Dus reist de range in zijn eigen bericht, verstuurd bij elke sessiehandshake, met de volledige stand erin. Twee afleverwegen, één bron van waarheid. Er is daarnaast een omgevingsvariabele op de host als val-terug, en de volgorde ertussen is bewust gekozen:
| Bron | Wanneer hij wint |
|---|---|
| De instelling van het platform | Zodra het platform er een stuurde |
| Omgevingsvariabele op de host | Alleen als het platform niets zei |
| Helemaal geen grens | Als geen van beide iets zei |
De verleidelijke volgorde is de omgekeerde, in de veronderstelling dat een riem die op de host is vastgemaakt sterker zit. Die levert een toestand op die niemand kan zien: een beheerder die ooit een variabele zette houdt stilletjes een grens die niet meer overeenkomt met wat het paneel toont, en een klant die de range in de interface verbreedt kan niet achterhalen waarom er niets veranderde.
Eén klein detail met een groot faalbeeld: een onleesbare waarde geldt als "niemand" en niet als "iedereen". Wie om een grens vraagt en een tikfout maakt krijgt helemaal geen leesacties, plus een logregel die zegt waarom. De prijs is cosmetisch: een adres en een schijfgetal die in het paneel ontbreken. Het alternatief, terugvallen op "alles is toegestaan", is de klassieke allowlist-fout: hij faalt open, stilletjes, precies in het geval waarin iemand voorzichtig probeerde te zijn.
Sjablonen staan buiten de range. Altijd.
Dit is een regel en geen voorkeur, en het is de fout die zichzelf opeet.
Een toewijzer deelt nummers uit de range uit. Zet je een sjabloon binnen de range, dan bereikt de toewijzer hem op een dag, wordt er een nieuwe bestelling op dat nummer aangemaakt, en wordt het sjabloon een klant-VM. De bestelling daarna kloont van een machine die geen sjabloon meer is. Dus weigert de validator een Proxmox-sjabloon-VMID die binnen de range van de integratie valt, en behandelt de toewijzer bovendien elk gekoppeld sjabloon als bezet, voor het geval iemand de range verbreedde nadat hij er een koppelde.
Buiten de range mag een sjabloon overal staan. De range begrenst wat het platform aanmaakt, niet waar bronmateriaal mag staan.
Hergebruik nooit een nummer, ook niet als de machine weg is
De toewijzer geeft de laagste vrije VMID in de range uit, en "vrij" is strenger dan je zou verwachten: elke VMID die de integratie ooit heeft uitgegeven telt als bezet, ook die van verwijderde servers.
Dat ziet er ongeveer vijf seconden lang verspillend uit, tot je bedenkt wat hergebruik zou betekenen. Een laat statusbericht van de oude VM landt op de nieuwe rij. De audithistorie van twee verschillende servers loopt door elkaar in één tijdlijn. Iemand zet de backup van gisteren terug op een nummer dat nu van een andere klant is.
Interessant genoeg maken we in hetzelfde product de omgekeerde keuze voor IP-adressen, en met een goede reden: een adres is een adres, en nooit hergebruiken zou een /24 na 254 machines voorgoed uitputten. Een VMID is een naam voor de geschiedenis van een machine. Ranges zijn goedkoop; verwarring niet.
Er is een derde soort "bezet" die pas opduikt zodra er meer dan één ding machines aanmaakt. Een terugzetactie die nog loopt heeft zijn doel-VMID al geclaimd maar heeft nog geen serverrij, dus een tweede terugzetactie in dezelfde minuut zou anders hetzelfde "vrije" nummer krijgen. Alles wat een VMID reserveert voordat de rij bestaat, moet meegeteld worden.
Wat er gebeurt als de range vol raakt
Twee antwoorden, en ze horen allebei te bestaan.
Het vriendelijke antwoord is rekenwerk: de toewijzer loopt de range af, vindt niets vrij, en geeft een specifieke fout terug die het begin en het einde van de range noemt. De klant leest "deze range is vol, van 5000 tot 8999" in plaats van een hypervisorfout vier lagen dieper.
Het echte antwoord is de database. De unieke constraint op (integratie, extern id) is de scheidsrechter: twee bestellingen die op hetzelfde moment binnenkomen kunnen allebei hetzelfde laagste vrije nummer zien, en precies één insert overleeft. De verliezer probeert het opnieuw, tot een klein vast aantal pogingen. Eerst controleren en daarna invoegen opent precies de race die de constraint sluit, dus de constraint is het mechanisme en de vriendelijke melding is een beleefdheid daarbovenop.
Let op wat we niet gebruiken: Proxmox heeft een endpoint voor het eerstvolgende vrije id, en dat is hier het verkeerde gereedschap. Het beantwoordt "welk nummer is vrij op dit cluster", en dat is een andere vraag dan "welk nummer is vrij én ligt binnen het venster dat van mij is". Vraag het de hypervisor en je krijgt vroeg of laat een nummer buiten je eigen grens.
Dus, praktisch
- Begin hoog en laat de lage nummers met rust. 100 tot een paar honderd is waar op elke Proxmox-installatie de handgemaakte machines staan, ook op die van jou over drie jaar, als je deze beslissing vergeten bent.
- Kies een range met een herkenbare vorm. 5000 tot 8999 leest in de webinterface in één oogopslag als "van ons". Een range van 4000 nummers is vierduizend machines op één integratie.
- Zet sjablonen erbuiten, in hun eigen blok, ver weg.
- Schrijf de range op waar het platform hem kan zien, niet alleen in een configuratiebestand op de host, en zorg dat hij opnieuw verstuurd wordt in plaats van één keer.
- Controleer dat ook de leesacties begrensd zijn, niet alleen het aanmaken en verwijderen.
Het nummer zelf doet er weinig toe. Wat ertoe doet is dat de grens één ding is, zichtbaar op één plek, toegepast op lezen en schrijven, en dat hij dichtvalt als hij niet gelezen kan worden.
Dit kwam voort uit het bouwen van de Proxmox-integratie voor Provibr, waar de agent binnen het eigen netwerk van de klant draait en uitsluitend uitgaand verbindt.