Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez 

Előbb-utóbb bármely VMware rendszergazda automatizálja a rutinfeladatokat. Minden a parancssorral kezdődik, majd jön a PowerShell vagy a VMware PowerCLI.

Tegyük fel, hogy egy kicsit jobban elsajátította a PowerShellt, mint az ISE elindítása és a szabványos parancsmagok használata olyan modulokból, amelyek „valamilyen varázslat” miatt működnek. Ha elkezdi több száz virtuális gépet számolni, azt fogja tapasztalni, hogy azok a szkriptek, amelyek kis léptékben segítenek, észrevehetően lassabban futnak nagy léptékben. 

Ebben a helyzetben 2 eszköz segít:

  • PowerShell Runspaces – olyan megközelítés, amely lehetővé teszi a folyamatok külön szálon történő végrehajtásának párhuzamosítását; 
  • Get-View – egy alap PowerCLI funkció, a Get-WMIObject analógja a Windows rendszerben. Ez a parancsmag nem húzza le az entitásokat kísérő objektumokat, hanem egyszerű objektumok formájában, egyszerű adattípusokkal fogadja az információkat. Sok esetben gyorsabban jön ki.

Ezután röviden beszélek az egyes eszközökről, és bemutatok használati példákat. Elemezzünk konkrét szkripteket, és nézzük meg, mikor működik jobban az egyik, mint a másik. Megy!

Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez

Első szakasz: Runspace

Tehát a Runspace-t a fő modulon kívüli feladatok párhuzamos feldolgozására tervezték. Természetesen elindíthat egy másik folyamatot, amely felemészti a memóriát, a processzort stb. Ha a szkript néhány percen belül lefut, és egy gigabájt memóriát fogyaszt, valószínűleg nem lesz szüksége Runspace-re. De több tízezer objektum szkriptjéhez szükség van rá.

Itt kezdheti el a tanulást: 
A PowerShell Runspaces használatának kezdete: 1. rész

Mit ad a Runspace használata:

  • sebesség a végrehajtott parancsok listájának korlátozásával,
  • párhuzamos feladatok végrehajtása,
  • biztonság.

Íme egy példa az internetről, amikor a Runspace segít:

„A tárolási versengés az egyik legnehezebben követhető mérőszám a vSphere-ben. A vCenteren belül nem nézheti meg, hogy melyik virtuális gép fogyaszt több tárhelyet. Szerencsére ezeket az adatokat percek alatt összegyűjtheti a PowerShellnek köszönhetően.
Megosztok egy szkriptet, amely lehetővé teszi a VMware rendszergazdák számára, hogy gyorsan kereshessenek a vCenterben, és megkapják a virtuális gépek listáját az átlagos fogyasztásuk adataival.  
A szkript PowerShell-futási tereket használ annak érdekében, hogy minden ESXi-gazdagép fogyasztási információkat gyűjtsön a saját virtuális gépeiről egy külön futási területen, és azonnal jelentse a befejezést. Ez lehetővé teszi, hogy a PowerShell azonnal bezárja a feladatokat, ahelyett, hogy a gazdagépeken keresztül iterálna, és megvárná, amíg mindegyik teljesíti a kérését.

Forrás: Virtuális gép I/O megjelenítése az ESXi irányítópulton

Az alábbi esetben a Runspace már nem hasznos:

„Olyan szkriptet próbálok írni, amely sok adatot gyűjt egy virtuális gépről, és szükség esetén új adatokat ír. A probléma az, hogy elég sok virtuális gép van, és 5-8 másodpercet töltenek egy gépen.” 

Forrás: Többszálú PowerCLI a RunspacePool segítségével

Itt szükség lesz a Get-View-ra, térjünk rá. 

Második szakasz: Get-View

Ahhoz, hogy megértsük, miért hasznos a Get-View, érdemes megjegyezni, hogyan működnek általában a parancsmagok. 

A parancsmagok szükségesek ahhoz, hogy kényelmesen szerezzenek információkat anélkül, hogy API referenciakönyveket kellene tanulmányozniuk és újra fel kellene találniuk a következő kereket. Ami a régi időkben száz-két sornyi kódot igényelt, a PowerShell egyetlen paranccsal megteheti. Gyorsasággal fizetünk ezért a kényelemért. Magukban a cmdletekben nincs varázslat: ugyanaz a szkript, de alacsonyabb szinten, egy napfényes Indiából származó mester ügyes kezei által.

Most a Get-View-val való összehasonlításhoz vegyük a Get-VM parancsmagot: hozzáfér a virtuális géphez, és egy összetett objektumot ad vissza, azaz más kapcsolódó objektumokat csatol hozzá: VMHost, Datastore stb.  

A Get-View a helyén nem ad semmi feleslegeset a visszaadott objektumhoz. Ezenkívül lehetővé teszi számunkra, hogy szigorúan meghatározzuk, milyen információra van szükségünk, ami megkönnyíti a kimeneti objektumot. A Windows Server rendszerben általában, és különösen a Hyper-V-ben a Get-WMIObject parancsmag közvetlen analóg – az ötlet pontosan ugyanaz.

A Get-View kényelmetlen a pontobjektumokkal végzett rutinműveletek során. De ha több ezer és tízezer tárgyról van szó, annak nincs ára.

Bővebben a VMware blogon olvashat: A Get-View bemutatása

Most mindent megmutatok egy valódi tokkal. 

Szkript írása a virtuális gép eltávolításához

Egy nap a kollégám megkért, hogy optimalizáljam a forgatókönyvét. A feladat egy gyakori rutin: keresse meg az összes virtuális gépet, amelyek duplikált cloud.uuid paraméterrel rendelkeznek (igen, ez lehetséges, ha virtuális gépet klónoz a vCloud Directorban). 

A kézenfekvő megoldás, ami eszembe jut:

  1. Szerezze meg az összes virtuális gép listáját.
  2. Elemezze valahogy a listát.

Az eredeti verzió a következő egyszerű szkript volt:

function Get-CloudUUID1 {
   # Получаем список всех ВМ
   $vms = Get-VM
   $report = @()

   # Обрабатываем каждый объект, получая из него только 2 свойства: Имя ВМ и Cloud UUID.
   # Заносим данные в новый PS-объект с полями VM и UUID
   foreach ($vm in $vms)
   {
       $table = "" | select VM,UUID

       $table.VM = $vm.name
       $table.UUID = ($vm | Get-AdvancedSetting -Name cloud.uuid).Value
          
       $report += $table
   }
# Возвращаем все объекты
   $report
}
# Далее РУКАМИ парсим полученный результат

Minden rendkívül egyszerű és világos. Kávészünettel pár perc alatt megírható. Csavarja fel a szűrést és kész.

De mérjük az időt:

Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez

Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez

2 perc 47 másodperc közel 10 XNUMX virtuális gép feldolgozásakor. A bónusz a szűrők hiánya és az eredmények manuális rendezésének szükségessége. Nyilvánvaló, hogy a szkript optimalizálást igényel.

A Runspace-ek az elsők, amelyek segítenek, ha egyidejűleg gazdametrikákat kell beszereznie a vCentertől, vagy több tízezer objektumot kell feldolgoznia. Lássuk, mit hoz ez a megközelítés.

Kapcsolja be az első sebességet: PowerShell Runspaces

Az első dolog, ami eszünkbe jut ennél a szkriptnél, hogy a ciklust nem szekvenciálisan, hanem párhuzamos szálakon kell végrehajtani, az összes adatot egyetlen objektumba gyűjteni, és azt szűrni. 

De van egy probléma: a PowerCLI nem engedi meg, hogy sok független munkamenetet nyissunk meg a vCenter számára, és vicces hibát dob:

You have modified the global:DefaultVIServer and global:DefaultVIServers system variables. This is not allowed. Please reset them to $null and reconnect to the vSphere server.

Ennek megoldásához először át kell adnia a munkamenet-információkat az adatfolyamon belül. Ne felejtsük el, hogy a PowerShell olyan objektumokkal dolgozik, amelyek paraméterként adhatók át akár egy függvénynek, akár egy ScriptBlocknak. Adjuk át a munkamenetet egy ilyen objektum formájában, a $global:DefaultVIServers megkerülésével (Connect-VIServer a -NotDefault kulccsal):

$ConnectionString = @()
foreach ($vCenter in $vCenters)
   {
       try {
           $ConnectionString += Connect-VIServer -Server $vCenter -Credential $Credential -NotDefault -AllLinked -Force -ErrorAction stop -WarningAction SilentlyContinue -ErrorVariable er
       }
       catch {
           if ($er.Message -like "*not part of a linked mode*")
           {
               try {
                   $ConnectionString += Connect-VIServer -Server $vCenter -Credential $Credential -NotDefault -Force -ErrorAction stop -WarningAction SilentlyContinue -ErrorVariable er
               }
               catch {
                   throw $_
               }
              
           }
           else {
               throw $_
           }
       }
   }

Most valósítsuk meg a többszálat a Runspace Pools-on keresztül.  

Az algoritmus a következő:

  1. Kapunk egy listát az összes virtuális gépről.
  2. Párhuzamos folyamokban felhőt kapunk.uuid.
  3. Az adatfolyamokból egyetlen objektumba gyűjtjük az adatokat.
  4. Az objektumot a CloudUUID mező értéke alapján szűrjük: ahol az egyedi értékek száma nagyobb, mint 1, azok a keresett virtuális gépek.

Ennek eredményeként megkapjuk a szkriptet:


function Get-VMCloudUUID {
   param (
       [string[]]
       [ValidateNotNullOrEmpty()]
       $vCenters = @(),
       [int]$MaxThreads,
       [System.Management.Automation.PSCredential]
       [System.Management.Automation.Credential()]
       $Credential
   )

   $ConnectionString = @()

   # Создаем объект с сессионным ключом
   foreach ($vCenter in $vCenters)
   {
       try {
           $ConnectionString += Connect-VIServer -Server $vCenter -Credential $Credential -NotDefault -AllLinked -Force -ErrorAction stop -WarningAction SilentlyContinue -ErrorVariable er
       }
       catch {
           if ($er.Message -like "*not part of a linked mode*")
           {
               try {
                   $ConnectionString += Connect-VIServer -Server $vCenter -Credential $Credential -NotDefault -Force -ErrorAction stop -WarningAction SilentlyContinue -ErrorVariable er
               }
               catch {
                   throw $_
               }
              
           }
           else {
               throw $_
           }
       }
   }

   # Получаем список всех ВМ
   $Global:AllVMs = Get-VM -Server $ConnectionString

   # Поехали!
   $ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
   $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
   $RunspacePool.ApartmentState = "MTA"
   $RunspacePool.Open()
   $Jobs = @()

# ScriptBlock с магией!)))
# Именно он будет выполняться в потоке
   $scriptblock = {
       Param (
       $ConnectionString,
       $VM
       )

       $Data = $VM | Get-AdvancedSetting -Name Cloud.uuid -Server $ConnectionString | Select-Object @{N="VMName";E={$_.Entity.Name}},@{N="CloudUUID";E={$_.Value}},@{N="PowerState";E={$_.Entity.PowerState}}

       return $Data
   }
# Генерируем потоки

   foreach($VM in $AllVMs)
   {
       $PowershellThread = [PowerShell]::Create()
# Добавляем скрипт
       $null = $PowershellThread.AddScript($scriptblock)
# И объекты, которые передадим в качестве параметров скрипту
       $null = $PowershellThread.AddArgument($ConnectionString)
       $null = $PowershellThread.AddArgument($VM)
       $PowershellThread.RunspacePool = $RunspacePool
       $Handle = $PowershellThread.BeginInvoke()
       $Job = "" | Select-Object Handle, Thread, object
       $Job.Handle = $Handle
       $Job.Thread = $PowershellThread
       $Job.Object = $VM.ToString()
       $Jobs += $Job
   }

# Ставим градусник, чтобы наглядно отслеживать выполнение заданий
# И здесь же прибиваем отработавшие задания
   While (@($Jobs | Where-Object {$_.Handle -ne $Null}).count -gt 0)
   {
       $Remaining = "$($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).object)"

       If ($Remaining.Length -gt 60) {
           $Remaining = $Remaining.Substring(0,60) + "..."
       }

       Write-Progress -Activity "Waiting for Jobs - $($MaxThreads - $($RunspacePool.GetAvailableRunspaces())) of $MaxThreads threads running" -PercentComplete (($Jobs.count - $($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).count)) / $Jobs.Count * 100) -Status "$(@($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False})).count) remaining - $remaining"

       ForEach ($Job in $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $True})){
           $Job.Thread.EndInvoke($Job.Handle)     
           $Job.Thread.Dispose()
           $Job.Thread = $Null
           $Job.Handle = $Null
       }
   }

   $RunspacePool.Close() | Out-Null
   $RunspacePool.Dispose() | Out-Null
}


function Get-CloudUUID2
{
   [CmdletBinding()]
   param(
   [string[]]
   [ValidateNotNullOrEmpty()]
   $vCenters = @(),
   [int]$MaxThreads = 50,
   [System.Management.Automation.PSCredential]
   [System.Management.Automation.Credential()]
   $Credential)

   if(!$Credential)
   {
       $Credential = Get-Credential -Message "Please enter vCenter credentials."
   }

   # Вызов функции Get-VMCloudUUID, где мы распараллеливаем операцию
   $AllCloudVMs = Get-VMCloudUUID -vCenters $vCenters -MaxThreads $MaxThreads -Credential $Credential
   $Result = $AllCloudVMs | Sort-Object Value | Group-Object -Property CloudUUID | Where-Object -FilterScript {$_.Count -gt 1} | Select-Object -ExpandProperty Group
   $Result
}

Ennek a szkriptnek az a szépsége, hogy más hasonló esetekben is használható, ha egyszerűen lecseréli a ScriptBlock-ot és a folyamnak átadandó paramétereket. Használd ki!

Időt mérünk:

Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez

55 másodperc. Ez jobb, de még mindig lehet gyorsabb. 

Térjünk át a második sebességre: GetView

Találjuk ki, mi a baj.
Mindenekelőtt a Get-VM parancsmag végrehajtása hosszú ideig tart.
Másodszor, a Get-AdvancedOptions parancsmag elkészítése még tovább tart.
Először foglalkozzunk a másodikkal. 

A Get-AdvancedOptions kényelmes az egyes virtuálisgép-objektumok számára, de nagyon ügyetlen, ha sok objektummal dolgozik. Ugyanezt az információt megkaphatjuk magából a virtuális gép objektumból (Get-VM). Csak jól el van temetve az ExtensionData objektumban. Szűréssel felvértezve felgyorsítjuk a szükséges adatok beszerzésének folyamatát.

Egy enyhe kézmozdulattal ez:


VM | Get-AdvancedSetting -Name Cloud.uuid -Server $ConnectionString | Select-Object @{N="VMName";E={$_.Entity.Name}},@{N="CloudUUID";E={$_.Value}},@{N="PowerState";E={$_.Entity.PowerState}}

Ebből alakul:


$VM | Where-Object {($_.ExtensionData.Config.ExtraConfig | Where-Object {$_.key -eq "cloud.uuid"}).Value -ne $null} | Select-Object @{N="VMName";E={$_.Name}},@{N="CloudUUID";E={($_.ExtensionData.Config.ExtraConfig | Where-Object {$_.key -eq "cloud.uuid"}).Value}},@{N="PowerState";E={$_.summary.runtime.powerstate}}

A kimenet ugyanaz, mint a Get-AdvancedOptions, de sokszor gyorsabban működik. 

Most a Get-VM-hez. Nem gyors, mert összetett tárgyakkal foglalkozik. Felmerül egy logikus kérdés: miért van szükségünk ebben az esetben többletinformációra és egy szörnyű PSObjectre, amikor csak a virtuális gép nevére, állapotára és egy trükkös attribútum értékére van szükségünk?  

Ezenkívül a Get-AdvancedOptions formájában lévő akadályt eltávolították a szkriptből. A Runspace Pools használata most túlzásnak tűnik, mivel a munkamenet átadásakor már nincs szükség a lassú feladatok párhuzamosítására a squat szálak között. Az eszköz jó, de nem erre az esetre. 

Nézzük az ExtensionData kimenetét: ez nem más, mint egy Get-View objektum. 

Hívjuk segítségül a PowerShell mesterek ősi technikáját: egy sor szűrőket, rendezést és csoportosítást. Az összes korábbi horror elegánsan egy sorba van összecsukva és egy munkamenetben végrehajtva:


$AllVMs = Get-View -viewtype VirtualMachine -Property Name,Config.ExtraConfig,summary.runtime.powerstate | Where-Object {($_.Config.ExtraConfig | Where-Object {$_.key -eq "cloud.uuid"}).Value -ne $null} | Select-Object @{N="VMName";E={$_.Name}},@{N="CloudUUID";E={($_.Config.ExtraConfig | Where-Object {$_.key -eq "cloud.uuid"}).Value}},@{N="PowerState";E={$_.summary.runtime.powerstate}} | Sort-Object CloudUUID | Group-Object -Property CloudUUID | Where-Object -FilterScript {$_.Count -gt 1} | Select-Object -ExpandProperty Group

Időt mérünk:

Hogyan készítsünk rakétaerősítőt PowerCLI-szkriptekhez

9 másodperc közel 10 XNUMX objektumhoz a kívánt feltétel szerinti szűréssel. Nagy!

Ahelyett, hogy egy következtetés

Az elfogadható eredmény közvetlenül függ az eszköz megválasztásától. Gyakran nehéz biztosan megmondani, hogy pontosan mit kell választani ennek eléréséhez. A szkriptek felgyorsítására felsorolt ​​módszerek mindegyike jó az alkalmazhatóság határain belül. Remélem, ez a cikk segít abban a nehéz feladatban, hogy megértse az infrastruktúra folyamatautomatizálásának és optimalizálásának alapjait.

PS: A szerző megköszöni a közösség minden tagjának segítségét és támogatását a cikk elkészítésében. Még azok is, akiknek mancsuk van. És még azok is, akiknek nincs lába, mint egy boa-szűkítő.

Forrás: will.com

Hozzászólás