Cumu custruisce un booster di cohettu per script PowerCLI 

Prima o dopu, ogni amministratore di sistema VMware vene à automatizà e attività di rutina. Tuttu principia cù a linea di cummanda, dopu vene PowerShell o VMware PowerCLI.

Diciamu chì avete maestratu PowerShell un pocu più luntanu da lancià ISE è utilizendu cmdlet standard da moduli chì funzionanu per "qualche tipu di magia". Quandu cuminciate à cuntà e macchine virtuali in centinaie, truverete chì i scripts chì aiutanu à una piccula scala correnu notevolmente più lentu à grande scala. 

In questa situazione, 2 strumenti aiutanu:

  • PowerShell Runspaces - un approcciu chì vi permette di parallelizà l'esekzione di prucessi in fili separati; 
  • Get-View - una funzione PowerCLI di basa, un analogu di Get-WMIObject in Windows. Stu cmdlet ùn tira micca l'uggetti chì accumpagnanu l'entità, ma riceve infurmazioni in forma di un oggettu simplice cù tipi di dati simplici. In parechji casi, esce più veloce.

In seguitu, parleraghju brevemente di ogni strumentu è mostra esempi di usu. Analizemu scripts specifichi è vedemu quandu unu funziona megliu cà l'altru. Vai !

Cumu custruisce un booster di cohettu per script PowerCLI

Prima tappa: Runspace

Dunque, Runspace hè pensatu per u processamentu parallelu di i travaglii fora di u modulu principale. Di sicuru, pudete lancià un altru prucessu chì manghjarà qualchì memoria, prucissuri, etc. Se u vostru script corre in un paru di minuti è cunsuma un gigabyte di memoria, assai prubabilmente ùn avete micca bisognu di Runspace. Ma per scripts per decine di millaie di oggetti hè necessariu.

Pudete principià à amparà quì: 
U principiu di l'usu di PowerShell Runspaces: Parte 1

Chì dà l'usu di Runspace:

  • vitezza limitendu a lista di cumandamenti eseguiti,
  • esecuzione parallela di i travaglii,
  • sicurità.

Eccu un esempiu da Internet quandu Runspace aiuta:

"A disputa di u almacenamentu hè una di e metriche più difficili da seguità in vSphere. Dentru vCenter, ùn pudete micca solu andà à vede quale VM cunsuma più risorse di almacenamiento. Per furtuna, pudete cullà queste dati in minuti grazia à PowerShell.
Aghju da sparte un script chì permetterà à l'amministratori di u sistema VMware di ricerca rapidamente in vCenter è riceve una lista di VM cù dati nantu à u so cunsumu mediu.  
U script usa PowerShell runspaces per permette à ogni host ESXi di cullà l'infurmazioni di cunsumu da e so VM in un Runspace separatu è immediatamente annunzià u cumpletu. Questu permette à PowerShell di chjude i travaglii immediatamente, piuttostu chè di iterà attraversu l'ospiti è aspittendu chì ognunu compie a so dumanda ".

Source: Cumu Mostra l'I/O di a Macchina Virtuale in un Dashboard ESXi

In u casu sottu, Runspace ùn hè più utile:

"Aghju pruvatu à scrive un script chì raccoglie assai dati da una VM è scrive novi dati quandu hè necessariu. U prublema hè chì ci sò assai VMs, è 5-8 seconde sò passati in una macchina ". 

Source: Multithreading PowerCLI cù RunspacePool

Quì avete bisognu di Get-View, andemu à questu. 

Seconda tappa: Get-View

Per capisce perchè Get-View hè utile, vale a pena ricurdà cumu funziona cmdlets in generale. 

I Cmdlets sò necessarii per ottene informazioni convenientemente senza a necessità di studià i libri di riferimentu API è reinventà a prossima rota. Ciò chì in i vechji tempi hà pigliatu un centu o duie linee di codice, PowerShell permette di fà cù un cumandamentu. Paghemu per questa cunvenzione cù rapidità. Ùn ci hè micca magia in i cmdlets stessi: u stessu script, ma à un livellu più bassu, scrittu da e mani abili di un maestru di l'India assulanata.

Avà, per paragunà cù Get-View, pigliemu u cmdlet Get-VM: accede à a macchina virtuale è torna un oggettu cumpostu, vale à dì, attache à ellu altri ogetti cunnessi: VMHost, Datastore, etc.  

Get-View in u so locu ùn aghjunghje nunda innecessariu à l'ughjettu tornatu. Inoltre, ci permette di specificà strettamente quale infurmazione avemu bisognu, chì farà l'ughjettu di output più faciule. In Windows Server in generale è in Hyper-V in particulare, u cmdlet Get-WMIObject hè un analogu direttu - l'idea hè esattamente a stessa.

Get-View hè sconveniente per l'operazioni di rutina nantu à oggetti puntuali. Ma quandu si tratta di millaie è decine di millaie di oggetti, ùn hà micca prezzu.

Pudete leghje più nantu à u blog di VMware: Introduzione à Get-View

Avà vi mustraraghju tuttu cù un casu veru. 

Scrivite un script per scaricà una VM

Un ghjornu u mo cullega m'hà dumandatu di ottimisà u so script. U compitu hè una rutina cumuna: truvate tutte e VM cù un paràmetru cloud.uuid duplicatu (sì, questu hè pussibule quandu clonà una VM in vCloud Director). 

A suluzione ovvia chì vene in mente hè:

  1. Ottene una lista di tutti i VM.
  2. Analizza a lista in qualchì modu.

A versione originale era questu script simplice:

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

Tuttu hè estremamente simplice è chjaru. Pò esse scrittu in un paru di minuti cù una pausa caffè. Avvitate a filtrazione è hè fatta.

Ma misuramu u tempu:

Cumu custruisce un booster di cohettu per script PowerCLI

Cumu custruisce un booster di cohettu per script PowerCLI

2 minuti 47 secondi quandu si tratta quasi 10k VM. Un bonus hè l'assenza di filtri è a necessità di sorte manualmente i risultati. Ovviamente, u script richiede ottimisazione.

I runspaces sò i primi à vene in salvezza quandu avete bisognu à ottene simultaneamente metriche d'ospiti da vCenter o bisognu di processà decine di millaie di oggetti. Videmu ciò chì porta stu approcciu.

Accende a prima velocità: PowerShell Runspaces

A prima cosa chì vene in mente per questu script hè di eseguisce u ciclu micca in sequenza, ma in fili paralleli, recullate tutte e dati in un oggettu è filtrà. 

Ma ci hè un prublema: PowerCLI ùn ci permetterà micca di apre parechje sessioni indipendenti à vCenter è tirà un errore divertente:

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.

Per risolve questu, devi prima passà l'infurmazioni di sessione in u flussu. Ricordemu chì PowerShell travaglia cù l'uggetti chì ponu esse passati cum'è paràmetru sia à una funzione sia à un ScriptBlock. Passemu a sessione in forma di un tali ughjettu, sguassendu $global:DefaultVIServers (Connect-VIServer cù a chjave -NotDefault):

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

Avà implementemu multithreading attraversu Runspace Pools.  

L'algoritmu hè u seguente:

  1. Avemu una lista di tutti i VM.
  2. In flussi paralleli avemu nuvola.uuid.
  3. Cullemu dati da i flussi in un oggettu.
  4. Filtremu l'ughjettu raggruppendulu per u valore di u campu CloudUUID: quelli induve u numeru di valori unichi hè più grande di 1 sò i VM chì circhemu.

In u risultatu, avemu u script:


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
}

A bellezza di stu script hè chì pò esse usatu in altri casi simili per solu rimpiazzà u ScriptBlock è i paràmetri chì saranu passati à u flussu. Sfruttate!

Misuremu u tempu:

Cumu custruisce un booster di cohettu per script PowerCLI

55 seconde. Hè megliu, ma pò ancu esse più veloce. 

Passemu à a seconda velocità: GetView

Scupritemu ciò chì hè sbagliatu.
Prima di tuttu, u cmdlet Get-VM piglia assai tempu per eseguisce.
Siconda, u cmdlet Get-AdvancedOptions dura ancu più tempu per compie.
Facemu prima cù u sicondu. 

Get-AdvancedOptions hè cunvene per l'uggetti VM individuali, ma assai goffa quandu travaglia cù parechji oggetti. Pudemu uttene a stessa infurmazione da l'ughjettu di a macchina virtuale stessu (Get-VM). Hè solu intarratu bè in l'ughjettu ExtensionData. Armatu di filtru, acceleremu u prucessu di ottene e dati necessarii.

Cù un ligeru muvimentu di a manu, questu hè:


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

Si trasforma in questu:


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

L'output hè u listessu cum'è Get-AdvancedOptions, ma travaglia parechje volte più veloce. 

Avà à Get-VM. Ùn hè micca veloce perchè tratta di oggetti cumplessi. Una quistione logica nasce: perchè avemu bisognu di l'infurmazioni extra è un PSObject monstruosu in questu casu, quandu avemu bisognu di u nome di a VM, u so statu è u valore di un attributu complicatu?  

Inoltre, l'ostaculu in a forma di Get-AdvancedOptions hè stata eliminata da u script. L'usu di Runspace Pools pare avà cum'è eccessivu postu chì ùn ci hè più bisognu di parallelizà un compitu lento à traversu i fili squat quandu trasmette una sessione. U strumentu hè bonu, ma micca per stu casu. 

Fighjemu l'output di ExtensionData: ùn hè nunda più cà un ughjettu Get-View. 

Chjamemu l'antica tecnica di i maestri PowerShell: una linea cù filtri, sorte è raggruppamentu. Tuttu l'orrore precedente hè elegantemente colapsatu in una linea è eseguitu in una sessione:


$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

Misuremu u tempu:

Cumu custruisce un booster di cohettu per script PowerCLI

Sicondi 9 per quasi 10k oggetti cù filtrazione da a cundizione desiderata. Perfettu!

Inveci di 'na cunchiusioni

Un risultatu accettabile dipende direttamente da a scelta di l'utillita. Hè spessu difficiuli di dì chì esattamente ciò chì deve esse sceltu per ottene. Ognunu di i metudi elencati per accelerà i scripts hè bonu in i limiti di a so applicabilità. Spergu chì questu articulu vi aiuterà in u difficult compitu di capisce i principii di l'automatizazione di u prucessu è l'ottimisazione in a vostra infrastruttura.

PS: L'autore ringrazia tutti i membri di a cumunità per u so aiutu è u sustegnu in a preparazione di l'articulu. Ancu quelli cù e zampe. E ancu quelli chì ùn anu micca gammi, cum'è una boa constrictor.

Source: www.habr.com

Add a comment