ClusterTriage / Blog / Storage
VMs uitlijnen met hun storage: voortbouwen op het script van Darryl van der Peijl
Jaren geleden schreef een oud-collega van mij, Darryl van der Peijl, een klein, scherp PowerShell-script dat één ding goed deed: het vond elke VM die op de verkeerde node draaide ten opzichte van zijn storage en migreerde die met Live Migration naar de node die zijn Cluster Shared Volume bezat. Het idee staat op zijn site onder align VMs with storage, en het heeft door de jaren heen stilletjes heel wat clusters voor redirected I/O behoed. Darryl heeft sindsdien Splitbrain opgericht, maar het script leeft voort in talloze toolboxen, ook de mijne.
Dit is het verhaal van wat er gebeurt wanneer je dat goede idee meeneemt naar een moderne productieomgeving, op de plekken stuit waar de simpele versie te bot is, en het laat uitgroeien tot een balancer die een klant daadwerkelijk op een zes-node stretch cluster kan draaien zonder de adem in te houden. De eer voor het kerninzicht is van Darryl. Wat volgt is wat wij erbovenop hebben gebouwd.
1. Het oorspronkelijke idee, en waarom het zo goed is
Het inzicht achter Darryl's script is zo'n ding dat overduidelijk is zodra iemand het zegt, en jarenlang makkelijk over het hoofd te zien daarvóór. Een Cluster Shared Volume heeft een eigenaar-node, de coordinator, en alle metadata-operaties op dat volume lopen daardoorheen. Als een VM op de ene node leeft maar de CSV met zijn disks door een andere node wordt bezeten, kruist elke metadata-operatie, en elke I/O die niet direct kan, het clusternetwerk naar de coordinator en terug. Zet de VM op de node die zijn storage bezit en dat verkeer blijft lokaal.
Het oorspronkelijke script automatiseerde de hele lus. Inventariseer de VMs, vind de CSV van elke VM, vind de eigenaar van die CSV, en waar de VM daar nog niet draait, migreer hem met Live Migration naar de eigenaar. Een handvol regels, geen agents, geen producten, alleen clustercmdlets die precies doen wat een zorgvuldige beheerder met de hand zou doen, maar dan over honderd VMs in één keer. Het is het soort hulpmiddel dat je bewaart, want het lost een echt probleem op zonder omhaal.
We hebben het gebruikt, aangeraden en ervan geleerd. Dus toen een recente opdracht ons een flink scheef uitgelijnd stretch cluster voorschotelde, was het script het voor de hand liggende vertrekpunt. En toen leerde het cluster ons waar de simpele versie ophoudt genoeg te zijn.
2. Waar de simpele versie terugbijt
Op het cluster in kwestie meldde de analyse dat zoiets als driekwart van de VMs scheef stond. De naïeve fix was duidelijk: migreer ze allemaal met Live Migration naar hun CSV-eigenaren, precies wat het oorspronkelijke script doet. Dat hebben we niet gedaan, en de reden daarvoor is de hele kern van dit artikel.
Ten eerste was de scheefstand niet willekeurig, maar systematisch. VM-plaatsing en CSV-ownership waren tussen twee nodes verwisseld, in beide sites. De VMs waren verstandig over de hosts verdeeld. Het was de storage-ownership die naar de verkeerde nodes was afgedreven. Als je dat 'oplost' door elke VM naar de storage te verplaatsen, neem je een gebalanceerde VM-verdeling en concentreer je die op de paar nodes die toevallig de CSVs bezitten. Je ruilt een storage-localiteitsprobleem in voor een load- en geheugenprobleem.
Ten tweede sloopte massale Live Migration de rolspreiding. Toen we modelleerden wat het verplaatsen van al die VMs zou doen, ging het aantal gelijkfunctionele VMs dat op één node samenkwam omhoog, niet omlaag. Je zou de storage uitlijnen en het cluster tegelijk minder bestand maken tegen een node-uitval, want nu zitten er meer identieke rollen bij elkaar.
Ten derde was dit een SAN-cluster, Pure FlashArray over iSCSI, waar elke node een direct pad naar de storage heeft. De winst van CSV-eigenaar-uitlijning is reëel, maar kleiner op een SAN dan op Storage Spaces Direct, waar de eigenaar-relatie samenvalt met waar de data fysiek staat. Negentig VMs verplaatsen voor een kleinere winst, terwijl je twee nieuwe problemen creëert, is een slechte ruil.
Niets hiervan is kritiek op het oorspronkelijke script. Dit is wat er gebeurt wanneer een helder idee een specifieke productiewerkelijkheid ontmoet. Het script stelt de juiste vraag. De omgeving vroeg om een ander antwoord.
3. De kern-herformulering: verplaats ownership, niet VMs
Hier is de herformulering die alles op zijn plek liet vallen. Het doel is om elke VM en zijn CSV-eigenaar op dezelfde node te krijgen. Darryl's script bereikt dat door de VM te verplaatsen. Maar er zijn twee verplaatsbare objecten in die zin, en op dit cluster was de goedkopere om te verplaatsen de andere.
Dus de primaire actie van de balancer werd CSV-ownership herbalanceren. Voor elke CSV: vind de node waar de meeste van zijn VMs al draaien, en als dat niet de huidige eigenaar is, verplaats de ownership daarheen met Move-ClusterSharedVolume. Die ene aanroep is vrijwel direct en niet-verstorend. De VMs verplaatsen niet, hun geheugen verplaatst niet, de loadverdeling blijft precies zoals de klant het had ingericht. En in één keer lijnt het tientallen VMs tegelijk uit, want het uitlijnen van de eigenaar op de meerderheid repareert elke VM in die meerderheid tegelijk.
# De herformulering, in één cmdlet, per CSV:
# vind de node die de meeste VMs van deze CSV host,
# en geef die de coordinator-rol.
Move-ClusterSharedVolume -Name $csv -Node $nodeWithMostVMsHere
In de echte run lijnde de CSV-ownership-fase de overgrote meerderheid van de scheef staande VMs uit zonder ook maar één VM te verplaatsen. Slechts een kleine rest had nog een echte Live Migration nodig, en daar komt de oorspronkelijke techniek weer in beeld: VM-verplaatsingen blijven de fallback voor de VMs die ownership-verplaatsingen niet kunnen repareren. Je kunt het klassieke gedrag nog steeds afdwingen met een switch wanneer je dat wilt. De default stopte alleen met voorop te lopen met de dure actie.
De diepere achtergrond van waarom CSV-ownership überhaupt afdrijft, en wat redirected I/O kost, staat in CSV ownership onbalans in Hyper-V clusters: oorzaken en oplossingen.
4. Site-bewuste rolspreiding
Het tweede dat we toevoegden heeft niets met storage te maken en alles met het overleven van een node-uitval. Als je toch al elke VM inventariseert, kun je meteen controleren of VMs met dezelfde functie over nodes verspreid zijn of op één node opgestapeld. Twee domain controllers op dezelfde host is het schoolvoorbeeld dat je nooit wilt.
De naïeve versie van die controle is op een stretch cluster echter fout. Een pool van remote-desktop hosts die bewust aan één site is gepind, is niet 'slecht verspreid', die staat correct geplaatst. Dus de balancer beoordeelt spreiding per site: een rolgroep is spreidbaar alleen wanneer het aantal leden hooguit gelijk is aan het aantal bruikbare nodes in de sites waar de leden daadwerkelijk draaien. Kleinere groepen worden verspreid en krijgen een AntiAffinityClassNames-waarde, zodat het cluster ze bij toekomstige plaatsing en failover uit elkaar houdt. Grotere groepen worden als pool gerapporteerd met hun scheefstand, niet tegen het site-ontwerp in uit elkaar getrokken. Dat onderscheid is wat de tool ervan weerhoudt om dingen te 'repareren' die nooit kapot waren.
Het instellen van de anti-affinity is een paar regels per rolgroep. Geef elk lid van een spreidbare groep dezelfde classnaam, en het cluster houdt ze bij plaatsing en failover op verschillende nodes:
# Gelijkfunctionele VMs die nooit een node mogen delen
$roleClass = 'RDP-Frontend'
$members = 'RDP01','RDP02','RDP03'
foreach ($name in $members) {
$group = Get-ClusterGroup -Name $name
$aac = New-Object System.Collections.Specialized.StringCollection
[void]$aac.Add($roleClass)
$group.AntiAffinityClassNames = $aac
}
# Controleer dat de class op elk lid is gezet
Get-ClusterGroup |
Where-Object { $_.AntiAffinityClassNames -contains $roleClass } |
Select-Object Name, OwnerNode, AntiAffinityClassNames
5. Ingebouwde beschermingen voor productie
Een script dat storage-ownership verplaatst en VMs met Live Migration verschuift op een productiecluster moet betrouwbaar zijn voordat het slim is. De beschermingen die we eromheen hebben gebouwd:
- Read-only by default. Zonder switches analyseert en projecteert het script alleen. Het verandert niets. Remediatie zit achter een expliciete
-Balanceswitch, en je krijgt te horen dat je die eerst met-WhatIfmoet draaien, wat elke ownership-verplaatsing, elke VM-verplaatsing en elke anti-affinity-wijziging afdrukt zonder het cluster aan te raken. - Het schakelt nooit een VM uit. Sommige VMs kunnen niet met Live Migration mee, om bekende redenen. Een draaiende VM op die lijst wordt gerapporteerd als 'handmatig afsluiten vereist', nooit met geweld verplaatst en nooit door het script uitgezet. Een VM wordt alleen uitgeschakeld verplaatst als hij al uit stond.
- Niets verdwijnt stilletjes. Uitgesloten en out-of-scope VMs staan nog steeds in de lijst, met eigenaar, status, uitlijning en de reden waarom ze zijn overgeslagen, gelabeld als geplande uitsluiting. Een rapport dat je kunt vertrouwen laat net zo duidelijk zien wat het niet heeft aangeraakt als wat het wel heeft gedaan.
- Het leest op een donker scherm. De console- en log-output gebruikt wit voor informatie, groen voor uitgelijnd en voor niet-verstorende winst, en rood voor scheefstand, botsingen en alles wat aandacht nodig heeft. Op een typische donkere beheerconsole zie je de status in één oogopslag, zonder te turen naar bijna identieke grijstinten.
6. Voor en na, in één plaatje
Het laatste dat we toevoegden is het deel dat de hele exercitie verdedigbaar maakt: een gemeten voor-en-na. Het script tekent per node een PRE- en een POST-weergave, VM-aantal, geheugen, uitgelijnd versus scheef, en rolbotsingen, als simpele balken in de log en optioneel als HTML-rapport. Daartussen toont het de tussentoestand na alleen de CSV-ownership-fase, zodat je precies ziet hoeveel VMs werden uitgelijnd zonder dat er ook maar één VM verplaatste.
Die projectie is wat je in staat stelt om -Balance -WhatIf te draaien, precies te lezen wat er zou veranderen, en pas dan te beslissen. Het verandert 'vertrouw me, dit gaat helpen' in 'hier is het beeld per node ervoor, het beeld na de ownership-verplaatsingen, en het beeld na de paar resterende VM-verplaatsingen'. Een reviewer die niet bij de sessie was kan de uitkomst uit het rapport lezen. De log sluit af met de aanbevelingen en de exacte commando's om te previewen en toe te passen, zodat niemand de aanroep uit het geheugen hoeft te reconstrueren.
Een preview-run leest zo. De namen zijn generiek, maar de vorm is precies wat de tool afdrukt:
PS> .\Optimize-VMPlacement.ps1 -Balance -WhatIf
Hyper-V VM Placement Optimizer - WhatIf (no changes will be made)
Cluster: PROD-CL Nodes: 4 CSVs: 6 Clustered VMs: 48
PRE -----------------------------------------------------------------
Site A HV-A1 : 14 VMs | 11 aligned | 3 misaligned | roles: 2 collisions
Site A HV-A2 : 10 VMs | 4 aligned | 6 misaligned | roles: 1 collision
Site B HV-B1 : 13 VMs | 9 aligned | 4 misaligned | roles: 1 collision
Site B HV-B2 : 11 VMs | 6 aligned | 5 misaligned | roles: 0 collisions
Total aligned: 30 / 48
PHASE A CSV ownership -> where the VMs already run
What if: Move-ClusterSharedVolume 'CSV02' HV-A2 -> HV-A1 (aligns 6 VMs)
What if: Move-ClusterSharedVolume 'CSV04' HV-B2 -> HV-B1 (aligns 5 VMs)
What if: Move-ClusterSharedVolume 'CSV05' HV-A2 -> HV-A1 (aligns 3 VMs)
Aligned after CSV-owner phase: 44 / 48 (14 VMs aligned, 0 VMs moved)
PHASE B VM moves (running, still misaligned)
What if: Move-ClusterVirtualMachineRole 'APP07' HV-A1 -> HV-B1 (Live)
What if: Move-ClusterVirtualMachineRole 'WEB03' HV-A2 -> HV-B2 (Live)
SKIP (off): BACKUP01 - aligns on a later run under load
MANUAL: MGMT01 - excluded from Live Migration, shut down by hand
PHASE C Anti-affinity (spreadable role groups)
What if: Set AntiAffinityClassNames 'DC' on DC01, DC02
Pool (not forced): RDP01..RDP04 on Site B - 4 members > 2 site nodes, reported only
POST (projected) -----------------------------------------------------
Site A HV-A1 : 13 VMs | 13 aligned | 0 misaligned | roles: 0 collisions
Site A HV-A2 : 9 VMs | 9 aligned | 0 misaligned | roles: 0 collisions
Site B HV-B1 : 14 VMs | 13 aligned | 1 misaligned | roles: 0 collisions
Site B HV-B2 : 12 VMs | 11 aligned | 1 misaligned | roles: 0 collisions
Total aligned: 46 / 48 (2 left: BACKUP01 off, MGMT01 needs manual shutdown)
RECOMMENDATIONS
- 3 CSV ownership moves align 14 VMs with zero downtime. Safe any time.
- 2 Live Migrations (APP07, WEB03). Off-peak preferred.
- MGMT01 is pinned and misaligned; shut it down by hand to align, or leave as-is.
- Preview : .\Optimize-VMPlacement.ps1 -Balance -WhatIf
- Apply : .\Optimize-VMPlacement.ps1 -Balance
Van boven naar beneden lezend zie je het hele betoog van dit artikel zich ontvouwen: de ownership-fase lijnt veertien VMs uit zonder er één te verplaatsen, slechts twee draaiende VMs hebben een Live Migration nodig, de uitgeschakelde en de gepinde VM worden bij naam genoemd en met rust gelaten in plaats van geforceerd, en de te grote rolpool wordt gerapporteerd in plaats van uit elkaar getrokken. Dat is de run die je met vertrouwen een change window in neemt, want je hebt al precies gezien wat hij gaat doen.
7. Wat we van Darryl hebben behouden, en wat we hem verschuldigd zijn
Pel alles weg wat we toevoegden en de kern is nog steeds van hem: vind het verschil tussen waar een VM draait en waar zijn storage wordt bezeten, en dicht het gat. De strikte CSV mount-prefix-matching, de per-VM primaire-CSV-logica, het instinct om de hele lus vanaf één management server te automatiseren met niets dan clustercmdlets, die lijn loopt rechtstreeks terug naar het oorspronkelijke align-VMs-with-storage script. We hebben het niet vervangen. We hebben het laten uitgroeien voor een grotere, gestrekte, SAN-gebaseerde, rolgevoelige omgeving, en er productiewaardige beschermingen omheen gebouwd.
Zo evolueert goede operationele tooling meestal. Iemand ziet het wezenlijke probleem helder en schrijft de strakke versie. Jaren later ontmoet die een omgeving met meer randvoorwaarden, en de strakke versie wordt de kern van iets groters, terwijl het inzicht in het hart precies hetzelfde blijft. Petje af voor Darryl van der Peijl, wiens script een generatie Hyper-V-beheerders heeft geleerd om hun VMs niet langer via de lange weg met hun storage te laten praten. We bouwen er met plezier op voort.
Optimize-VMPlacement.ps1, staat op GitHub op github.com/trippledutch/shared, onder MIT-licentie, met dank aan Darryl van der Peijl voor het oorspronkelijke idee. Het is read-only by default; draai hem eerst met -Balance -WhatIf en lees precies wat hij zou doen. Liever dat wij hem op jouw cluster draaien en je het voor-en-na overhandigen? Dat hoort bij een ClusterTriage Hyper-V cluster assessment.
Heb je nooit gecontroleerd of je VMs draaien waar hun storage wordt bezeten, dan is de kans groot dat sommige dat niet doen, en een SAN verbergt het symptoom tot een node op het verkeerde moment draint.
Een ClusterTriage Hyper-V cluster assessment meet uitlijning, rolspreiding en ownership-drift, en geeft je het voor-en-na.
Plan een kennismaking voor een cluster assessment →Plan een kennismaking voor een Hyper-V cluster assessment →
Veelgestelde vragen
Een VM is uitgelijnd wanneer hij draait op de clusternode die op dat moment het Cluster Shared Volume bezit waar het grootste deel van zijn diskdata op staat. Als VM en CSV-eigenaar op dezelfde node zitten, blijven metadata-operaties lokaal en vermijd je redirected I/O over het clusternetwerk. Het oorspronkelijke script van Darryl van der Peijl automatiseerde het vinden en oplossen van die mismatch door de VM met Live Migration naar de CSV-eigenaar te verplaatsen.
Tientallen VMs met Live Migration verplaatsen concentreert de load op de eigenaar-nodes en kan de rolspreiding slopen, en op SAN-storage is de uitlijningswinst kleiner dan op Storage Spaces Direct. De CSV-ownership verplaatsen naar waar de VMs al draaien is vrijwel direct en niet-verstorend, en lijnt veel VMs tegelijk uit zonder de VM- of geheugenverdeling te veranderen. Wij maakten ownership-verplaatsingen de primaire actie en hielden VM-verplaatsingen als de rest.
De analyse is read-only en altijd veilig. Remediatie gebeurt alleen met een expliciete Balance switch, en je wordt geacht die eerst met WhatIf te draaien, wat elke ownership-verplaatsing, VM-verplaatsing en anti-affinity-wijziging afdrukt zonder iets aan te raken. Het script schakelt nooit een VM uit: een draaiende VM die van Live Migration is uitgesloten wordt gerapporteerd als 'handmatig afsluiten vereist' in plaats van verplaatst.
Op een stretch cluster horen VMs met dezelfde functie over nodes verspreid te zijn voor weerbaarheid, maar alleen binnen de sites waar ze daadwerkelijk draaien. Een rol is spreidbaar wanneer het aantal leden hooguit gelijk is aan het aantal bruikbare nodes in die sites. Grotere groepen worden als pool behandeld en met hun scheefstand gerapporteerd in plaats van uit elkaar getrokken, wat voorkomt dat site-gebonden pools zoals RDP-hosts ten onrechte als volledig spreidbaar worden bestempeld.
Nee, het vult die aan. Automatische CSV-balancing en node-fairness dekken het algemene geval. Dit script richt zich op de specifieke uitlijning tussen VM-plaatsing en CSV-ownership, zet anti-affinity zodat het cluster rolleden bij toekomstige failover uit elkaar houdt, en geeft je een gemeten voor-en-na zodat je kunt aantonen dat de wijziging hielp.