Blog ·
Een container opnieuw installeren is vervangen
Een container heeft geen sjabloonschijf en geen cloud-init: opnieuw installeren is vernietigen en opnieuw bouwen. Dat verandert wat je de klant schuldig bent.
Door Provibr
Een virtuele machine opnieuw installeren op Proxmox is bijna saai. Stop hem, verwijder hem, kloon het sjabloon over hetzelfde ID heen, laat cloud-init de rest doen. Het sjabloon draagt een schijf en de schijf draagt een besturingssysteem, dus "opnieuw installeren" is in werkelijkheid "een nieuwe kopie maken van iets waarvan we weten dat het goed is".
Een Linux-container heeft geen van beide. Hij wordt uitgepakt uit een tarball, en alles wat hij daarna is, de cores, het geheugen, de swap, de grootte van het rootfilesystem, de netwerkkaart, de resolver, de hostnaam, staat in een configuratiebestand op de hypervisor en nergens anders. Er is geen image om overheen te klonen. Een container opnieuw installeren is dus een destroy gevolgd door een create, en zodra je dat hardop zegt volgt er een reeks verplichtingen die een VM-herinstallatie nooit heeft.
Zo hebben we het gebouwd, en het deel dat het langst duurde was niet het vernietigen.
Twee fasen, want het platform weet niet wat er gekoppeld is
De flow is gesplitst. De eerste fase leest alleen, en verandert niets: de agent haalt de configuratie van de container op en geeft die terug. De tweede fase stopt, vernietigt, maakt aan en start, met wat de eerste fase las als voorwaarde.
Het voor de hand liggende alternatief is één fase met een voorvertoning die is opgebouwd uit wat het platform al weet. Dat is hier niet beschikbaar. Hardware-inspectie voor containers is niet gebouwd, want de parser die daarvoor gebruikt zou worden leest QEMU-configuratiesleutels, dus er staat nergens in onze database een opgeslagen momentopname van de mount points van een container. Een voorvertoning die elk mount point "unknown" noemt is geen voorvertoning maar een voorbehoud.
Vijf woorden voor wat er met de data gebeurt
Elk mount point in de configuratie krijgt precies één etiket, en het etiket gaat over de data, niet over de mount.
| Etiket | Betekenis |
|---|---|
erased | het volume wordt samen met de container vernietigd; het rootfilesystem, altijd |
excluded | het volume hoort bij een andere gast, dus Proxmox slaat het over en er wordt niets aangeraakt |
externally_managed | een bind mount, een hostpad; de map blijft bestaan, de mount komt niet terug |
unknown | de regel kon niet gelezen worden, en dit is het enige etiket dat de hele operatie blokkeert |
preserved | de data blijft en de mount komt terug |
De laatste rij staat in de lijst en wordt nooit geproduceerd. Een volume behouden over een destroy heen betekent het loskoppelen, de container verwijderen zonder ongerefereerde schijven op te ruimen, en het daarna weer koppelen: drie stappen waarvan de middelste bij een mislukking een volume achterlaat dat niemand meer bezit. Dat is een apart stuk werk met een eigen opruimlus. De waarde bestaat in het vocabulaire zodat een latere versie hem niet stilletjes onder een andere naam hoeft onder te brengen, een functie zegt dat hij nog niet wordt aangeboden, en een test legt vast dat de classificatie hem nooit teruggeeft.
Het belangrijke onderscheid is dat uit de eerste zin. Van een mount point kan de data blijven bestaan zonder dat de mount terugkomt, want de create-aanroep die wij versturen zet alleen ooit het rootfilesystem. Elk ander mount point verdwijnt uit de nieuwe container, ongeacht wat er met zijn bytes is gebeurd. Dat omdraaien levert de gevaarlijkste zin uit deze hele functie op: "preserved" naast een mount die er daarna niet zal zijn.
Het eigenaarschap staat in de volume-identificatie
Proxmox schrijft een volume in een van twee vormen, en allebei zeggen ze welke gast de eigenaar is. local-lvm:vm-5001-disk-0 en local:5001/vm-5001-disk-0.raw horen allebei bij container 5001. Een bind mount is geen van beide: dat is een absoluut pad, en de leidende slash is waar Proxmox zelf op toetst.
Die ontleding beslist excluded tegenover erased. Een mount point in de configuratie van deze container dat naar het volume van een andere gast wijst wordt door de destroy overgeslagen, want Proxmox controleert het eigenaarschap, dus die data wordt niet gewist. Zonder de vergelijking zou de voorvertoning "erased" zeggen over data die blijft. Iemand zou daarna een backup terugzetten die hij niet nodig had, wat een kleinere ramp is dan andersom maar nog steeds een die wij veroorzaakt hebben.
De voorvertoning is een voorwaarde, geen plaatje
Wat de klant zag reist met het apply-commando mee terug als een lijst met bevestigingen, en de agent leest de configuratie opnieuw, vlak voor het vernietigen. Wijkt die af, een mount point erbij, een ander volume onder dezelfde sleutel, dan weigert hij.
Tussen de voorvertoning en de klik zit een mens, en in die tijd kan een beheerder een schijf koppelen in de Proxmox-interface. "Bind mounts en door de host beheerde mount points horen niet automatisch bij een herinstallatie" is alleen waar als iets dat afdwingt.
Elke bevestiging is een sleutel en een volume, en nadrukkelijk niet het etiket. Het etiket is het oordeel van het platform en heeft aan de andere kant niets te beslissen; kreeg de agent het mee als onderdeel van waarmee hij vergelijkt, dan zou er een tak ontstaan die erop koos, en dan zouden de regels op twee plekken staan. Het etiket wordt wél opgeschreven, naast de bevestigingen in het commandorecord en in de intentierij, want dat is de auditkant, en daar hoort precies te staan wat de klant te zien kreeg.
Alleen groeien, vergeleken in bytes, getoond in gibibytes
Een herinstallatie mag het rootfilesystem niet verkleinen. De vergelijking loopt op bytes en het scherm rondt naar boven af op hele gibibytes.
Naar boven afronden is de behoudende richting, en het tegenvoorbeeld zegt waarom. Rond 8,5 GiB op het scherm naar beneden af tot "8 GiB" en accepteer daartegen een verzoek om 8 GiB, dan is er een halve gibibyte weg waar niemand om vroeg. Gemeten tegen een nagebouwde Proxmox is een container die 16 GiB meldde waar het plan om 8 vroeg geweigerd.
Er zit een valkuil één laag dieper. Proxmox schrijft groottes als 8G, 512M of als een kaal getal, en een kaal getal is kibibytes. Als bytes gelezen is 8388608 8 MiB; correct gelezen is het 8 GiB. Een parser die hier verkeerd gokt gooit geen fout, hij keurt gewoon elke krimp goed die hem ooit wordt voorgelegd.
Negen redenen om te weigeren
Voordat er iets onomkeerbaars gebeurt zijn er negen benoemde blokkades: de container is weg, protection=1 staat aan, Proxmox houdt een lock op de gast, een van onze eigen backup- of hersteltaken loopt, er bestaat een beschermd herstelpunt, het rootfilesystem zou krimpen, de huidige grootte ervan kon niet gelezen worden, een mount point kon niet geclassificeerd worden, of er loopt al een herinstallatie.
Elk daarvan is een getypeerde waarde met een eigen vertaalde zin in negen talen, zodat een reden die niet op de lijst staat er geen kan krijgen. Ze worden aan onze kant twee keer gecontroleerd, één keer wanneer de voorvertoning wordt getekend en nog eens wanneer op de bevestigknop wordt gedrukt, en een derde keer door de agent tegen de configuratie die hij zelf leest. Drie sluitingen, want dit is de onomkeerbare stap van het hoofdstuk.
Het rootwachtwoord wordt in de tweede fase gevraagd en niet in de eerste, en het reist als kortstondige data in plaats van te worden opgeslagen. Een geheim dat je vlak voor de onomkeerbare stap intypt brengt geen half uur door in een browsertabblad.
Needs reconcile is de derde uitkomst, en hij houdt de lock dicht
Als er iets misgaat tussen de destroy en de create, wordt de uitvoering niet "mislukt". Ze wordt "needs reconcile", met het container-ID erbij, de stap waarop ze stierf, en of de container nog bestond toen de agent het opgaf. Dat laatste veld kan null zijn, en null is niet false: "ik kon het niet vaststellen" mag niet gelezen worden als "het ID is vrij".
Of een mislukking destructief is, wordt afgeleid uit de positie van de stap in de geordende faselijst, vanaf de destroy. Daarom zijn de fasen een lijst en geen verzameling losse teksten: de vraag "was er al iets onomkeerbaars gebeurd" wordt op precies één plek beantwoord.
Een uitvoering in die toestand houdt de lock op de server dicht. Een tweede vervanging starten op een ID waarvan niemand weet of er een container op zit is precies het ding dat dit ontwerp verbiedt, en een mens moet het afsluiten.
De verliezer van een compare-and-swap moet opnieuw lezen
Er is geen worker die deze rijen bijwerkt. De toestand schuift op wanneer iemand hem leest: de leesactie kijkt naar het commando waar de intentie aan hangt en schuift de rij vooruit, met een compare-and-swap op een generatieteller, zodat twee gelijktijdige lezers samen elke overgang precies één keer uitvoeren.
Dat was ontworpen. Wat niet ontworpen was, en wat de browsertest ving, is wat de verliezer van die swap moet teruggeven.
Het paneel bevraagt de server elke seconde nadat er een voorvertoning is aangevraagd, en die bevraging voert dezelfde overgang uit. Laadt de klant in diezelfde seconde de pagina, dan wint de bevraging de swap, geeft de pagina haar eigen, inmiddels verouderde rij terug, en blijft het scherm zeggen "wachten op de agent" terwijl de voorvertoning al in de database staat. Eén navigatie, één verzoek, en een scherm dat een toestand toont die niet meer bestond. Alleen een tweede herlading loste het op.
De reparatie is één zin lang en de moeite waard om je eigen te maken: een compare-and-swap verliezen betekent per constructie dat iemand anders zojuist precies de overgang heeft gemaakt die je wilde. Het juiste antwoord is dus hun rij, en dat kost één select. Je eigen rij teruggeven is het intuïtieve en het is altijd fout.
Wat wij zouden opschrijven
- Heeft datgene wat je vervangt geen image, noem het dan een vervanging. Het woord verandert wat je de klant schuldig bent voordat je begint.
- Geef de data en de mount elk hun eigen etiket. Het zijn twee vragen, en een waar antwoord op de ene naast een onwaar antwoord op de andere is de slechtste uitvoer die het systeem kan geven.
- Laat de voorvertoning als voorwaarde terugreizen, lees hem aan de andere kant opnieuw, en weiger bij elk verschil. Er heeft een mens tussendoor geklikt.
- Rond behoudend af als een afrondingsfout alleen kan vernietigen.
- Geef een half afgemaakte destructieve operatie een eigen toestand, houd de lock dicht, en laat hem nooit in geslaagd of mislukt vallen.
- Lees opnieuw na een verloren compare-and-swap, altijd.
Containerondersteuning op Proxmox wordt in stappen uitgerold, en opnieuw installeren zit laat in die volgorde, om de redenen hierboven. Zit je eerder in die reeks, dan staan de afwegingen tussen de twee soorten gasten in VM of container op Proxmox, en de reden dat een geslaagde API-aanroep geen afgeronde taak is staat in waarom een Proxmox-200 geen uitkomst is.
Uit het Proxmox-werk bij Provibr.