ProHoster > Blog > Bestjoer > Hoe kinne jo in raketbooster bouwe foar PowerCLI-skripts
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!
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.
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.
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.
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.
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:
Krij in list fan alle VM's.
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:
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:
Wy krije in list fan alle VM's.
Yn parallelle streamen krije wy cloud.uuid.
Wy sammelje gegevens fan streamen yn ien objekt.
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:
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}}
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:
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.