Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts 

Ier of letter komt elke VMware-systeembehearder om routinetaken te automatisearjen. It begjint allegear mei de kommandorigel, dan komt PowerShell of VMware PowerCLI.

Litte wy sizze dat jo PowerShell in bytsje fierder behearske hawwe dan it lansearjen fan ISE en it brûken fan standert cmdlets fan modules dy't wurkje fanwegen "wat soarte fan magy". As jo ​​​​begjinne mei it tellen fan firtuele masines yn 'e hûnderten, sille jo fine dat skripts dy't op lytse skaal helpe, merkber stadiger rinne op grutte skaal. 

Yn dizze situaasje sille 2 ark helpe:

  • PowerShell Runspaces - in oanpak wêrmei jo de útfiering fan prosessen yn aparte threaden kinne parallelisearje; 
  • Get-View - in basis PowerCLI-funksje, in analoog fan Get-WMIObject yn Windows. Dizze cmdlet lûkt gjin objekten dy't begeliede entiteiten, mar ûntfangt ynformaasje yn 'e foarm fan in ienfâldich objekt mei ienfâldige gegevenstypen. Yn in protte gefallen komt it flugger út.

Dêrnei sil ik koart oer elk ark prate en foarbylden fan gebrûk sjen litte. Litte wy spesifike skripts analysearje en sjen wannear't de iene better wurket as de oare. Go!

Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts

Earste etappe: Runspace

Dat, Runspace is ûntworpen foar parallelle ferwurking fan taken bûten de haadmodule. Fansels kinne jo in oar proses starte dat wat ûnthâld, prosessor, ensfh. As jo ​​skript yn in pear minuten rint en in gigabyte oan ûnthâld ferbrûkt, sille jo nei alle gedachten gjin Runspace nedich hawwe. Mar foar skripts foar tsientûzenen objekten is it nedich.

Jo kinne hjir begjinne te learen: 
Begjin gebrûk fan PowerShell Runspaces: Diel 1

Wat jout it brûken fan Runspace:

  • snelheid troch de list mei útfierde kommando's te beheinen,
  • parallelle útfiering fan taken,
  • feilichheid.

Hjir is in foarbyld fan it ynternet as Runspace helpt:

"Opslachkonflikt is ien fan 'e hurdste metriken om te folgjen yn vSphere. Binnen vCenter kinne jo net gewoan gean en sjen hokker VM mear opslachboarnen konsumearret. Gelokkich kinne jo dizze gegevens yn minuten sammelje troch PowerShell.
Ik sil in skript diele wêrmei VMware-systeembehearders fluch troch vCenter kinne sykje en in list mei VM's ûntfange mei gegevens oer har gemiddelde konsumpsje.  
It skript brûkt PowerShell runspaces foar in tastean eltse ESXi host in sammelje konsumpsje ynformaasje fan syn eigen VMs yn in aparte Runspace en fuortendaliks rapportearje foltôging. Dit lit PowerShell banen fuortendaliks slute, ynstee fan iterearjen troch hosts en wachtsje op elk om syn fersyk te foltôgjen.

Boarne: Hoe te sjen Virtual Machine I / O op in ESXi Dashboard

Yn it gefal hjirûnder is Runspace net langer brûkber:

"Ik besykje in skript te skriuwen dat in protte gegevens fan in VM sammelt en nije gegevens skriuwt as it nedich is. It probleem is dat d'r nochal in protte VM's binne, en 5-8 sekonden wurde bestege oan ien masine. 

Boarne: Multithreading PowerCLI mei RunspacePool

Hjir sille jo Get-View nedich wêze, litte wy der nei gean. 

Twadde etappe: Get-View

Om te begripen wêrom't Get-View nuttich is, is it wurdich te ûnthâlden hoe't cmdlets yn 't algemien wurkje. 

Cmdlets binne nedich om maklik ynformaasje te krijen sûnder de needsaak om API-referinsjeboeken te studearjen en it folgjende tsjil opnij út te finen. Wat yn 'e âlde dagen naam hûndert of twa rigels fan koade, PowerShell kinne jo dwaan mei ien kommando. Wy betelje foar dit gemak mei snelheid. Der is gjin magy binnen de cmdlets sels: itselde skript, mar op in leger nivo, skreaun troch de betûfte hannen fan in master út sinnich Yndia.

No, foar ferliking mei Get-View, litte wy de Get-VM cmdlet nimme: it makket tagong ta de firtuele masine en jout in gearstald objekt werom, dat wol sizze, it hechtet oare relatearre objekten oan: VMHost, Datastore, ensfh.  

Get-View op syn plak foeget neat net nedich ta oan it weromjûne objekt. Boppedat lit it ús strikt oanjaan hokker ynformaasje wy nedich binne, wat it útfierobjekt makliker makket. Yn Windows Server yn it algemien en yn Hyper-V yn it bysûnder, de Get-WMIObject cmdlet is in direkte analoog - it idee is krekt itselde.

Get-View is ûngemaklik foar routine operaasjes op puntobjekten. Mar as it giet om tûzenen en tsientûzenen objekten, it hat gjin priis.

Jo kinne mear lêze op it VMware-blog: Yntroduksje ta Get-View

No sil ik jo alles sjen litte mei in echte saak. 

In skript skriuwe om in VM te ûntladen

Op in dei frege myn kollega my om syn skript te optimalisearjen. De taak is in mienskiplike routine: fine alle VM's mei in duplikaat cloud.uuid parameter (ja, dit is mooglik by it klonen fan in VM yn vCloud Director). 

De foar de hân lizzende oplossing dy't yn 't sin komt is:

  1. Krij in list fan alle VM's.
  2. Parse de list op ien of oare manier.

De oarspronklike ferzje wie dit ienfâldige skript:

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 heul ienfâldich en dúdlik. It kin yn in pear minuten skreaun wurde mei in kofjestop. Skroef de filtraasje op en it is dien.

Mar litte wy de tiid mjitte:

Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts

Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts

2 minuten 47 sekonden by it ferwurkjen fan hast 10k VM's. In bonus is it ûntbrekken fan filters en de needsaak om de resultaten manuell te sortearjen. Fansels fereasket it skript optimisaasje.

Runspaces binne de earste dy't ta de rêding komme as jo tagelyk hostmetriken fan vCenter moatte krije of tsientûzenen objekten moatte ferwurkje. Litte wy sjen wat dizze oanpak bringt.

Skeakelje de earste snelheid oan: PowerShell Runspaces

It earste ding dat yn 't sin komt foar dit skript is om de loop net sequentieel út te fieren, mar yn parallelle threaden, sammelje alle gegevens yn ien objekt en filterje it. 

Mar d'r is in probleem: PowerCLI sil ús net tastean om in protte ûnôfhinklike sesjes te iepenjen nei vCenter en sil in grappige flater smyt:

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 lossen, moatte jo earst sesje-ynformaasje binnen de stream trochjaan. Lit ús ûnthâlde dat PowerShell wurket mei objekten dy't as parameter kinne wurde trochjûn oan in funksje of nei in ScriptBlock. Litte wy de sesje trochjaan yn 'e foarm fan sa'n objekt, troch $global:DefaultVIServers (Ferbine-VIServer mei de -NotDefault-kaai):

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

Litte wy no multithreading ymplementearje fia Runspace Pools.  

It algoritme is as folgjend:

  1. Wy krije in list fan alle VM's.
  2. Yn parallelle streamen krije wy cloud.uuid.
  3. Wy sammelje gegevens fan streamen yn ien objekt.
  4. Wy filterje it objekt troch it te groepearjen troch de wearde fan it CloudUUID-fjild: dyjingen wêr't it oantal unike wearden grutter is dan 1 binne de VM's wêr't wy nei sykje.

As gefolch krije wy it skript:


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
}

De skientme fan dit skript is dat it kin wurde brûkt yn oare ferlykbere gefallen troch gewoan it ScriptBlock te ferfangen en de parameters dy't nei de stream sille wurde trochjûn. Exploitearje it!

Wy mjitte tiid:

Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts

55 sekonden. It is better, mar it kin noch flugger. 

Litte wy nei de twadde snelheid gean: GetView

Litte wy útfine wat der mis is.
Earst en foaral duorret de Get-VM cmdlet in lange tiid om út te fieren.
Twadder nimt de Get-AdvancedOptions cmdlet noch langer om te foltôgjen.
Litte wy earst mei de twadde omgean. 

Get-AdvancedOptions is handich foar yndividuele VM-objekten, mar heul onhandig as jo wurkje mei in protte objekten. Wy kinne deselde ynformaasje krije fan it firtuele masine-objekt sels (Get-VM). It is gewoan goed begroeven yn it ExtensionData-objekt. Bewapene mei filterjen fersnelle wy it proses fan it krijen fan de nedige gegevens.

Mei in lichte beweging fan 'e hân 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}}

It feroaret yn dit:


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

De útfier is itselde as Get-AdvancedOptions, mar it wurket in protte kearen rapper. 

No nei Get-VM. It is net fluch, om't it omgiet mei komplekse objekten. In logyske fraach ûntstiet: wêrom moatte wy ekstra ynformaasje en in meunsterlike PSObject yn dit gefal, as wy krekt nedich de namme fan de VM, syn steat en de wearde fan in lestich attribút?  

Derneist is it obstakel yn 'e foarm fan Get-AdvancedOptions út it skript fuortsmiten. It brûken fan Runspace Pools liket no te overkill, om't d'r net langer in ferlet is om in trage taak te parallelisearjen oer squat-threads by it oerjaan fan in sesje. It ark is goed, mar net foar dit gefal. 

Litte wy nei de útfier fan ExtensionData sjen: it is neat mear as in Get-View-objekt. 

Litte wy de âlde technyk fan 'e PowerShell-masters oanroppe: ien rigel mei filters, sortearjen en groepearjen. Alle foarige horror is elegant yn ien rigel ynstoarten en útfierd yn ien sesje:


$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

Wy mjitte tiid:

Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts

9 sekonden foar hast 10k foarwerpen mei filterjen troch de winske betingst. Grut!

Yn stee fan in konklúzje

In akseptabel resultaat hinget direkt ôf fan 'e kar fan ark. It is faak lestich om wis te sizzen wat der krekt keazen wurde moat om it te berikken. Elk fan 'e neamde metoaden foar it fersnellen fan skripts is goed binnen de grinzen fan har tapasberens. Ik hoopje dat dit artikel jo sil helpe yn 'e drege taak om de basis fan prosesautomatisaasje en optimisaasje yn jo ynfrastruktuer te begripen.

PS: De skriuwer betanket alle leden fan 'e mienskip foar har help en stipe by it tarieden fan it artikel. Ek dy mei poaten. En sels dyjingen dy't gjin skonken hawwe, lykas in boaconstrictor.

Boarne: www.habr.com

Add a comment