Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou 

Vroeër of later kom enige VMware-stelseladministrateur om roetinetake te outomatiseer. Dit begin alles met die opdragreël, dan kom PowerShell of VMware PowerCLI.

Kom ons sê jy het PowerShell 'n bietjie verder bemeester as om ISE te begin en standaard cmdlets te gebruik van modules wat werk as gevolg van "een of ander soort magie". Wanneer jy virtuele masjiene in die honderde begin tel, sal jy vind dat skrifte wat op klein skaal help, merkbaar stadiger op groot skaal loop. 

In hierdie situasie sal 2 gereedskap help:

  • PowerShell Runspaces - 'n benadering wat jou toelaat om die uitvoering van prosesse in aparte drade te paralleliseer; 
  • Kry-Besigtig – 'n basiese PowerCLI-funksie, 'n analoog van Get-WMIObject in Windows. Hierdie cmdlet trek nie voorwerpe wat entiteite vergesel nie, maar ontvang inligting in die vorm van 'n eenvoudige voorwerp met eenvoudige datatipes. In baie gevalle kom dit vinniger uit.

Vervolgens sal ek kortliks oor elke instrument praat en voorbeelde van gebruik wys. Kom ons ontleed spesifieke skrifte en kyk wanneer die een beter werk as die ander. Gaan!

Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou

Eerste fase: Runspace

Dus, Runspace is ontwerp vir parallelle verwerking van take buite die hoofmodule. Natuurlik kan jy 'n ander proses begin wat 'n bietjie geheue, verwerker, ens. sal opvreet. As jou skrif binne 'n paar minute loop en 'n gigagreep geheue verbruik, sal jy heel waarskynlik nie Runspace nodig hê nie. Maar vir skrifte vir tienduisende voorwerpe is dit nodig.

Jy kan hier begin leer: 
Begin gebruik van PowerShell Runspaces: Deel 1

Wat gee die gebruik van Runspace:

  • spoed deur die lys van uitgevoerde opdragte te beperk,
  • parallelle uitvoering van take,
  • sekuriteit.

Hier is 'n voorbeeld van die internet wanneer Runspace help:

“Stoorgeskil is een van die moeilikste maatstawwe om in vSphere op te spoor. Binne vCenter kan jy nie net gaan kyk watter VM meer bergingsbronne verbruik nie. Gelukkig kan jy hierdie data binne minute insamel danksy PowerShell.
Ek sal 'n skrif deel wat VMware-stelseladministrateurs in staat sal stel om vinnig deur vCenter te soek en 'n lys van VM's te ontvang met data oor hul gemiddelde verbruik.  
Die skrif gebruik PowerShell-loopruimtes om elke ESXi-gasheer toe te laat om verbruiksinligting van sy eie VM's in 'n aparte Runspace in te samel en voltooiing onmiddellik aan te meld. Dit stel PowerShell in staat om take onmiddellik te sluit, eerder as om deur gashere te herhaal en te wag vir elkeen om sy versoek te voltooi.”

Bron: Hoe om virtuele masjien I/O op 'n ESXi Dashboard te wys

In die geval hieronder is Runspace nie meer bruikbaar nie:

“Ek probeer 'n draaiboek skryf wat baie data van 'n VM versamel en nuwe data skryf wanneer nodig. Die probleem is dat daar nogal baie VM's is, en 5-8 sekondes word op een masjien spandeer. 

Bron: Multithreading PowerCLI met RunspacePool

Hier sal jy Get-View nodig hê, kom ons gaan verder daarna. 

Tweede fase: Get-View

Om te verstaan ​​waarom Get-View nuttig is, is dit die moeite werd om te onthou hoe cmdlets in die algemeen werk. 

Cmdlets is nodig om inligting gerieflik te bekom sonder om API-naslaanboeke te bestudeer en die volgende wiel te herontdek. Wat in die ou dae honderd of twee reëls kode geneem het, laat PowerShell jou toe om met een opdrag te doen. Ons betaal met spoed vir hierdie gerief. Daar is geen towerkrag binne die cmdlets self nie: dieselfde skrif, maar op 'n laer vlak, geskryf deur die vaardige hande van 'n meester uit sonnige Indië.

Nou, vir vergelyking met Get-View, kom ons neem die Get-VM cmdlet: dit kry toegang tot die virtuele masjien en gee 'n saamgestelde voorwerp terug, dit wil sê, dit heg ander verwante voorwerpe daaraan: VMHost, Datastore, ens.  

Get-View in sy plek voeg niks onnodig by die teruggekeerde voorwerp nie. Boonop stel dit ons in staat om streng te spesifiseer watter inligting ons benodig, wat die uitsetvoorwerp makliker sal maak. In Windows Server in die algemeen en in Hyper-V in die besonder, is die Get-WMIObject-cmdlet 'n direkte analoog - die idee is presies dieselfde.

Get-View is ongerieflik vir roetine-operasies op puntvoorwerpe. Maar wanneer dit by duisende en tienduisende voorwerpe kom, het dit geen prys nie.

Jy kan meer lees op die VMware blog: Inleiding tot Get-View

Nou sal ek jou alles wys deur 'n regte saak te gebruik. 

Skryf 'n skrip om 'n VM af te laai

Eendag het my kollega my gevra om sy draaiboek te optimaliseer. Die taak is 'n algemene roetine: vind alle VM's met 'n duplikaat cloud.uuid-parameter (ja, dit is moontlik wanneer 'n VM in vCloud Director gekloon word). 

Die voor die hand liggende oplossing wat by my opkom is:

  1. Kry 'n lys van alle VM's.
  2. Ontleed die lys op een of ander manier.

Die oorspronklike weergawe was hierdie eenvoudige skrif:

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
}
# Далее РУКАМИ парсим полученный результат

Alles is uiters eenvoudig en duidelik. Dit kan binne 'n paar minute met 'n koffiepouse geskryf word. Skroef die filtrasie aan en dit is klaar.

Maar kom ons meet die tyd:

Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou

Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou

2 minute en 47 sekondes wanneer byna 10k VM's verwerk word. 'n Bonus is die afwesigheid van filters en die behoefte om die resultate handmatig te sorteer. Dit is duidelik dat die skrif optimalisering vereis.

Runspaces is die eerste wat tot die redding kom wanneer jy gelyktydig gasheerstatistieke van vCenter moet verkry of tienduisende voorwerpe moet verwerk. Kom ons kyk wat hierdie benadering bring.

Skakel die eerste spoed aan: PowerShell Runspaces

Die eerste ding wat in gedagte kom vir hierdie skrif is om die lus nie opeenvolgend uit te voer nie, maar in parallelle drade, versamel al die data in een voorwerp en filtreer dit. 

Maar daar is 'n probleem: PowerCLI sal ons nie toelaat om baie onafhanklike sessies na vCenter oop te maak nie en sal 'n snaakse fout gooi:

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.

Om dit op te los, moet jy eers sessie-inligting binne die stroom deurgee. Laat ons onthou dat PowerShell werk met voorwerpe wat as 'n parameter óf na 'n funksie óf na 'n ScriptBlock deurgegee kan word. Kom ons slaag die sessie in die vorm van so 'n voorwerp, omseil $global:DefaultVIServers (Verbind-VIServer met die -NotDefault sleutel):

$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 $_
           }
       }
   }

Kom ons implementeer nou multithreading deur Runspace Pools.  

Die algoritme is soos volg:

  1. Ons kry 'n lys van alle VM's.
  2. In parallelle strome kry ons wolk.uuid.
  3. Ons versamel data van strome in een voorwerp.
  4. Ons filtreer die voorwerp deur dit te groepeer volgens die waarde van die CloudUUID-veld: dié waar die aantal unieke waardes groter as 1 is, is die VM's waarna ons soek.

As gevolg hiervan kry ons die skrif:


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
}

Die skoonheid van hierdie skrif is dat dit in ander soortgelyke gevalle gebruik kan word deur bloot die ScriptBlock en die parameters wat na die stroom deurgegee sal word, te vervang. Ontgin dit!

Ons meet tyd:

Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou

55 sekondes. Dit is beter, maar dit kan steeds vinniger wees. 

Kom ons beweeg na die tweede spoed: GetView

Kom ons vind uit wat fout is.
In die eerste plek neem die Get-VM-cmdlet lank om uit te voer.
Tweedens neem die Get-AdvancedOptions-cmdlet selfs langer om te voltooi.
Kom ons handel eers met die tweede een. 

Get-AdvancedOptions is gerieflik vir individuele VM-voorwerpe, maar baie lomp wanneer met baie voorwerpe gewerk word. Ons kan dieselfde inligting van die virtuele masjienvoorwerp self (Get-VM) kry. Dit is net goed begrawe in die ExtensionData-voorwerp. Gewapen met filter, versnel ons die proses om die nodige data te bekom.

Met 'n effense beweging van die hand is dit:


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

Verander in hierdie:


$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}}

Die uitset is dieselfde as Get-AdvancedOptions, maar dit werk baie keer vinniger. 

Nou na Get-VM. Dit is nie vinnig nie, want dit handel oor komplekse voorwerpe. 'n Logiese vraag ontstaan: hoekom het ons ekstra inligting en 'n monsteragtige PSO-objek nodig in hierdie geval, wanneer ons net die naam van die VM, sy toestand en die waarde van 'n moeilike eienskap nodig het?  

Daarbenewens is die struikelblok in die vorm van Get-AdvancedOptions uit die skrif verwyder. Die gebruik van Runspace Pools lyk nou na oordrewe, aangesien dit nie meer nodig is om 'n stadige taak oor hurkdrade te paralleliseer wanneer 'n sessie oorhandig word nie. Die instrument is goed, maar nie vir hierdie geval nie. 

Kom ons kyk na die uitvoer van ExtensionData: dit is niks meer as 'n Get-View-objek nie. 

Kom ons gebruik die antieke tegniek van die PowerShell-meesters: een reël wat filters, sortering en groepering gebruik. Al die vorige gruwel word elegant in een reël saamgevat en in een sessie uitgevoer:


$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

Ons meet tyd:

Hoe om 'n vuurpylversterker vir PowerCLI-skrifte te bou

9 sekondes vir byna 10k voorwerpe met filtering volgens die verlangde toestand. Puik!

In plaas daarvan om 'n gevolgtrekking

'N Aanvaarbare resultaat hang direk af van die keuse van gereedskap. Dit is dikwels moeilik om met sekerheid te sê wat presies gekies moet word om dit te bereik. Elkeen van die gelyste metodes om skrifte te bespoedig is goed binne die perke van die toepaslikheid daarvan. Ek hoop dat hierdie artikel u sal help met die moeilike taak om die basiese beginsels van prosesoutomatisering en optimalisering in u infrastruktuur te verstaan.

PS: Die skrywer bedank alle gemeenskapslede vir hul hulp en ondersteuning met die voorbereiding van die artikel. Selfs dié met pote. En selfs diegene wat nie bene het nie, soos 'n boa-konstrictor.

Bron: will.com

Voeg 'n opmerking