Kuidas luua PowerCLI skriptide jaoks raketivõimendi 

Varem või hiljem tuleb iga VMware süsteemiadministraator rutiinseid toiminguid automatiseerima. Kõik algab käsurealt, seejärel tuleb PowerShell või VMware PowerCLI.

Oletame, et olete PowerShelli õppinud pisut kaugemale kui ISE käivitamine ja standardsete cmdlet-käskude kasutamine moodulitest, mis töötavad "mingi võlu tõttu". Kui hakkate virtuaalmasinaid sadadesse lugema, avastate, et väikeses mahus abistavad skriptid töötavad suures mahus märgatavalt aeglasemalt. 

Selles olukorras aitavad 2 tööriista:

  • PowerShelli tööruumid – lähenemine, mis võimaldab paralleelstada protsesside täitmist eraldi lõimedes; 
  • Hangi-vaade – PowerCLI põhifunktsioon, mis on Windowsi Get-WMIObjecti analoog. See cmdlet ei tõmba üksustega kaasnevaid objekte, vaid saab teavet lihtsa objekti kujul ja lihtsate andmetüüpidega. Paljudel juhtudel tuleb see kiiremini välja.

Järgmisena räägin lühidalt igast tööriistast ja näitan kasutusnäiteid. Analüüsime konkreetseid skripte ja vaatame, millal üks töötab paremini kui teine. Mine!

Kuidas luua PowerCLI skriptide jaoks raketivõimendi

Esimene etapp: Runspace

Seega on Runspace mõeldud ülesannete paralleelseks töötlemiseks väljaspool põhimoodulit. Muidugi võite käivitada mõne muu protsessi, mis sööb natuke mälu, protsessorit jne. Kui teie skript jookseb paari minutiga ja kulutab gigabaidi mälu, pole teil tõenäoliselt Runspace'i vaja. Kuid seda on vaja kümnete tuhandete objektide skriptide jaoks.

Õppimist saad alustada siit: 
PowerShelli käitamisruumide kasutamise alustamine: 1. osa

Mida Runspace'i kasutamine annab:

  • kiirust, piirates täidetavate käskude loendit,
  • ülesannete paralleelne täitmine,
  • turvalisus.

Siin on näide Internetist, kui Runspace aitab:

„Salvestuskonkurents on vSphere'is üks raskemini jälgitavaid mõõdikuid. VCenteris ei saa te lihtsalt minna ja vaadata, milline VM kulutab rohkem salvestusressursse. Õnneks saate neid andmeid tänu PowerShellile minutitega koguda.
Jagan skripti, mis võimaldab VMware süsteemiadministraatoritel kiiresti otsida kogu vCenterist ja saada VM-ide loendi andmetega nende keskmise tarbimise kohta.  
Skript kasutab PowerShelli käitamisruume, et võimaldada igal ESXi hostil koguda tarbimisteavet oma VM-idest eraldi Runspace'is ja teatada kohe lõpetamisest. See võimaldab PowerShellil töökohad kohe sulgeda, selle asemel et käia läbi hostide ja oodata, kuni igaüks oma taotluse täidab.

Allikas: Virtuaalse masina I/O kuvamine ESXi armatuurlaual

Alloleval juhul pole Runspace enam kasulik:

„Püüan kirjutada skripti, mis kogub VM-ist palju andmeid ja kirjutab vajadusel uusi andmeid. Probleem on selles, et VM-e on üsna palju ja ühe masina peale kulub 5-8 sekundit. 

Allikas: Mitme lõimega PowerCLI koos RunspacePooliga

Siin on vaja Get-View't, liigume selle juurde. 

Teine etapp: Get-View

Et mõista, miks Get-View on kasulik, tasub meeles pidada, kuidas cmdletid üldiselt töötavad. 

Cmdletid on vajalikud teabe mugavaks hankimiseks, ilma et oleks vaja API teatmeid uurida ja uut ratast uuesti leiutada. Seda, mida vanasti kulus sada või kaks koodirida, võimaldab PowerShell teha ühe käsuga. Selle mugavuse eest maksame kiirusega. cmdlet-i enda sees pole maagiat: sama skript, kuid madalamal tasemel, kirjutatud päikeselisest Indiast pärit meistri osavate kätega.

Võrdluseks Get-View'ga võtame Get-VM cmdleti: see pääseb juurde virtuaalmasinale ja tagastab liitobjekti, st lisab sellele muid seotud objekte: VMHost, Datastore jne.  

Get-View omal kohal ei lisa tagastatavale objektile midagi ebavajalikku. Veelgi enam, see võimaldab meil täpselt täpsustada, millist teavet vajame, mis muudab väljundobjekti lihtsamaks. Windows Serveris üldiselt ja eriti Hyper-V-s on Get-WMIObject cmdlet otsene analoog – idee on täpselt sama.

Get-View on punktobjektidega tehtavate rutiinsete toimingute jaoks ebamugav. Aga kui rääkida tuhandetest ja kümnetest tuhandetest objektidest, siis sellel pole hinda.

Rohkem saad lugeda VMware blogist: Get-View tutvustus

Nüüd näitan teile kõike tõelise juhtumi abil. 

Skripti kirjutamine VM-i mahalaadimiseks

Ühel päeval palus mu kolleeg mul oma skripti optimeerida. Ülesanne on tavaline rutiin: otsige üles kõik VM-id, millel on dubleeritud cloud.uuid parameeter (jah, see on võimalik VM-i kloonimisel vCloud Directoris). 

Ilmselge lahendus, mis meelde tuleb, on:

  1. Hankige kõigi VM-ide loend.
  2. Parendada nimekirja kuidagi.

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

Kõik on äärmiselt lihtne ja selge. Selle saab kirjutada paari minutiga koos kohvipausiga. Keerake filter peale ja ongi tehtud.

Aga mõõdame aega:

Kuidas luua PowerCLI skriptide jaoks raketivõimendi

Kuidas luua PowerCLI skriptide jaoks raketivõimendi

2 minutit 47 sekundit peaaegu 10 XNUMX virtuaalse masina töötlemisel. Boonuseks on filtrite puudumine ja vajadus tulemusi käsitsi sortida. Ilmselgelt vajab skript optimeerimist.

Runspaces on esimene, kes appi tuleb, kui teil on vaja vCenterist korraga hankida hostimõõdikud või töödelda kümneid tuhandeid objekte. Vaatame, mida see lähenemine toob.

Lülitage sisse esimene kiirus: PowerShell Runspaces

Esimene asi, mis selle skripti puhul meelde tuleb, on tsükli käivitamine mitte järjestikku, vaid paralleelsete lõimede kaupa, koguda kõik andmed ühte objekti ja filtreerida. 

Kuid on probleem: PowerCLI ei luba meil vCenteris paljusid sõltumatuid seansse avada ja tekitab naljaka vea:

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.

Selle lahendamiseks peate esmalt edastama seansi teabe voo sees. Pidagem meeles, et PowerShell töötab objektidega, mida saab parameetrina edasi anda kas funktsioonile või ScriptBlockile. Edastame seansi sellise objekti kujul, mööda $global:DefaultVIServers (Connect-VIServer võtmega -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 $_
           }
       }
   }

Nüüd rakendame Runspace Poolsi kaudu multithreadingut.  

Algoritm on järgmine:

  1. Saame kõigi VM-ide loendi.
  2. Paralleelsetes voogudes saame cloud.uuid.
  3. Kogume andmeid voogudest ühte objekti.
  4. Filtreerime objekti, rühmitades selle välja CloudUUID väärtuse järgi: need, mille kordumatute väärtuste arv on suurem kui 1, on otsitavad VM-id.

Selle tulemusena saame skripti:


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
}

Selle skripti ilu seisneb selles, et seda saab kasutada ka muudel sarnastel juhtudel, lihtsalt asendades ScriptBlocki ja voogu edastatavad parameetrid. Kasutage seda ära!

Mõõdame aega:

Kuidas luua PowerCLI skriptide jaoks raketivõimendi

55 sekundit. See on parem, kuid see võib siiski olla kiirem. 

Liigume teise kiiruse juurde: GetView

Uurime välja, mis viga.
Eelkõige võtab Get-VM-i cmdleti käivitamine kaua aega.
Teiseks võtab Get-AdvancedOptions cmdleti valmimine veelgi kauem aega.
Esmalt tegeleme teisega. 

Get-AdvancedOptions on mugav üksikute VM-objektide jaoks, kuid paljude objektidega töötamisel väga kohmakas. Sama info saame ka virtuaalmasina objektilt endalt (Get-VM). See on lihtsalt ExtensionData objekti hästi maetud. Filtreerimisega relvastatud kiirendame vajalike andmete hankimise protsessi.

Kerge käeliigutusega on see:


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

Muutub selliseks:


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

Väljund on sama, mis Get-AdvancedOptions, kuid see töötab mitu korda kiiremini. 

Nüüd Get-VM-i juurde. See ei ole kiire, sest see tegeleb keerukate objektidega. Tekib loogiline küsimus: miks me vajame sel juhul lisateavet ja koletu PSO-objekti, kui vajame lihtsalt VM-i nime, selle olekut ja keerulise atribuudi väärtust?  

Lisaks on skriptist eemaldatud takistus Get-AdvancedOptions näol. Runspace Poolsi kasutamine tundub nüüd ülemäärane, kuna seansi üleandmisel pole enam vaja aeglast ülesannet kükitavate lõimede vahel paralleelselt siduda. Tööriist on hea, kuid mitte selle juhtumi jaoks. 

Vaatame ExtensionData väljundit: see pole midagi muud kui Get-View objekt. 

Kutsume appi PowerShelli meistrite iidne tehnika: üks rida filtrite, sorteerimise ja rühmitamise abil. Kogu eelnev õudus on elegantselt kokku pandud üheks reale ja teostatud ühe seansi jooksul:


$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

Mõõdame aega:

Kuidas luua PowerCLI skriptide jaoks raketivõimendi

9 sekundit peaaegu 10 XNUMX objekti jaoks soovitud tingimuse järgi filtreerimisega. Suurepärane!

Selle asemel, et järeldus

Vastuvõetav tulemus sõltub otseselt tööriista valikust. Tihti on raske kindlalt öelda, mida täpselt selle saavutamiseks valida. Kõik loetletud skriptide kiirendamise meetodid on oma rakendatavuse piires head. Loodan, et see artikkel aitab teil oma infrastruktuuri protsesside automatiseerimise ja optimeerimise põhitõdesid mõista.

PS: Autor tänab kõiki kogukonna liikmeid abi ja toetuse eest artikli koostamisel. Isegi need, kellel on käpad. Ja isegi need, kellel pole jalgu, nagu boakonstriktor.

Allikas: www.habr.com

Lisa kommentaar