Ինչպես ստեղծել հրթիռային ուժեղացուցիչ PowerCLI սկրիպտների համար
Վաղ թե ուշ ցանկացած VMware համակարգի ադմինիստրատոր գալիս է ավտոմատացնելու սովորական առաջադրանքները: Ամեն ինչ սկսվում է հրամանի տողից, հետո գալիս է PowerShell կամ VMware PowerCLI:
Ենթադրենք, դուք տիրապետել եք PowerShell-ին մի փոքր ավելի հեռու, քան ISE-ն գործարկելը և ստանդարտ cmdlet-ներ օգտագործել մոդուլներից, որոնք աշխատում են «ինչ-որ մոգության» շնորհիվ: Երբ սկսում եք վիրտուալ մեքենաները հարյուրներով հաշվել, կտեսնեք, որ փոքր մասշտաբով օգնող սկրիպտները մեծ մասշտաբով զգալիորեն դանդաղ են աշխատում:
Այս իրավիճակում 2 գործիքներ կօգնեն.
PowerShell Runspaces – մոտեցում, որը թույլ է տալիս զուգահեռացնել գործընթացների կատարումը առանձին թելերով.
Ստանալ-Դիտել – հիմնական PowerCLI ֆունկցիան, Windows-ում Get-WMIObject-ի անալոգը: Այս cmdlet-ը չի քաշում ուղեկցող օբյեկտները, այլ տեղեկատվություն է ստանում պարզ օբյեկտի տեսքով՝ տվյալների պարզ տեսակներով: Շատ դեպքերում այն ավելի արագ է դուրս գալիս։
Հաջորդը, ես համառոտ կխոսեմ յուրաքանչյուր գործիքի մասին և ցույց կտամ օգտագործման օրինակներ: Եկեք վերլուծենք կոնկրետ սցենարներ և տեսնենք, թե երբ է մեկը մյուսից լավ աշխատում: Գնա՛
Առաջին փուլ. Runspace
Այսպիսով, Runspace-ը նախատեսված է հիմնական մոդուլից դուրս առաջադրանքների զուգահեռ մշակման համար։ Իհարկե, դուք կարող եք գործարկել մեկ այլ պրոցես, որը կխլի որոշակի հիշողություն, պրոցեսոր և այլն: Եթե ձեր սկրիպտը գործարկվի մի քանի րոպեում և սպառի մեկ գիգաբայթ հիշողություն, ամենայն հավանականությամբ ձեզ Runspace-ը պետք չի լինի: Բայց տասնյակ հազարավոր օբյեկտների սցենարների համար դա անհրաժեշտ է։
արագություն՝ սահմանափակելով կատարված հրամանների ցանկը,
առաջադրանքների զուգահեռ կատարում,
անվտանգություն:
Ահա մի օրինակ ինտերնետից, երբ Runspace-ն օգնում է.
«Պահպանման վեճը vSphere-ում հետևելու ամենադժվար ցուցանիշներից մեկն է: vCenter-ի ներսում դուք պարզապես չեք կարող գնալ և տեսնել, թե որ VM-ն է ավելի շատ պահեստավորման ռեսուրսներ սպառում: Բարեբախտաբար, դուք կարող եք հավաքել այս տվյալները րոպեների ընթացքում PowerShell-ի շնորհիվ:
Ես կկիսվեմ մի սկրիպտով, որը VMware համակարգի ադմինիստրատորներին թույլ կտա արագ որոնել vCenter-ում և ստանալ VM-ների ցուցակը՝ դրանց միջին սպառման վերաբերյալ տվյալներով:
Սկրիպտը օգտագործում է PowerShell-ի աշխատատեղերը, որպեսզի յուրաքանչյուր ESXi հոսթին կարողանա հավաքել սպառման մասին տեղեկատվություն իր VM-ներից առանձին Runspace-ում և անմիջապես հաղորդել ավարտի մասին: Սա PowerShell-ին թույլ է տալիս անմիջապես փակել աշխատանքները, այլ ոչ թե կրկնել հոսթինգների միջոցով և սպասել, որ յուրաքանչյուրն ավարտի իր հարցումը»:
«Ես փորձում եմ գրել մի սցենար, որը հավաքում է շատ տվյալներ VM-ից և անհրաժեշտության դեպքում գրում է նոր տվյալներ: Խնդիրն այն է, որ VM-ները բավականին շատ են, իսկ մեկ մեքենայի վրա ծախսվում է 5-8 վայրկյան»։
Այստեղ ձեզ անհրաժեշտ կլինի Get-View, եկեք անցնենք դրան:
Երկրորդ փուլ՝ Get-View
Հասկանալու համար, թե ինչու է Get-View-ն օգտակար, արժե հիշել, թե ինչպես են աշխատում cmdlet-ները ընդհանրապես:
Cmdlet-ներն անհրաժեշտ են տեղեկատվություն ստանալու համար՝ առանց API-ի տեղեկատու գրքերն ուսումնասիրելու և հաջորդ անիվը նորից հայտնագործելու անհրաժեշտության: Այն, ինչ հին ժամանակներում պահանջում էր հարյուր կամ երկու տող կոդ, PowerShell-ը թույլ է տալիս անել մեկ հրամանով: Մենք վճարում ենք այս հարմարության համար արագությամբ: Ինքը՝ cmdlet-ների ներսում ոչ մի կախարդանք չկա. նույն սցենարը, բայց ավելի ցածր մակարդակի վրա, գրված է արևոտ Հնդկաստանից վարպետի հմուտ ձեռքերով:
Այժմ Get-View-ի հետ համեմատելու համար վերցնենք Get-VM cmdlet-ը. այն մուտք է գործում վիրտուալ մեքենա և վերադարձնում կոմպոզիտային օբյեկտ, այսինքն՝ դրան կցում է այլ առնչվող օբյեկտներ՝ VMHost, Datastore և այլն:
Get-View-ն իր տեղում ավելորդ ոչինչ չի ավելացնում վերադարձված օբյեկտին։ Ավելին, դա մեզ թույլ է տալիս խստորեն նշել, թե ինչ տեղեկատվություն է մեզ անհրաժեշտ, ինչը կհեշտացնի ելքային օբյեկտը։ Windows Server-ում ընդհանրապես և Hyper-V-ում, մասնավորապես, Get-WMIObject cmdlet-ը ուղղակի անալոգ է. գաղափարը նույնն է:
Get-View-ը անհարմար է կետային օբյեկտների վրա սովորական գործողությունների համար: Բայց երբ խոսքը գնում է հազարավոր ու տասնյակ հազարավոր օբյեկտների մասին, դա գին չունի։
Այժմ ես ձեզ ցույց կտամ ամեն ինչ՝ օգտագործելով իրական պատյան:
VM-ը բեռնաթափելու համար սցենար գրելը
Մի օր գործընկերս ինձ խնդրեց օպտիմալացնել իր սցենարը: Առաջադրանքը սովորական առօրյա է՝ գտնել բոլոր VM-ները կրկնօրինակ cloud.uuid պարամետրով (այո, դա հնարավոր է vCloud Director-ում VM կլոնավորելիս):
Ակնհայտ լուծումը, որը գալիս է մտքին, հետևյալն է.
Ստացեք բոլոր VM-ների ցուցակը:
Ինչ-որ կերպ վերլուծեք ցուցակը:
Բնօրինակ տարբերակը հետևյալ պարզ սցենարն էր.
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
}
# Далее РУКАМИ парсим полученный результат
Ամեն ինչ չափազանց պարզ է և պարզ: Սուրճի ընդմիջումով մի երկու րոպեից կարելի է գրել։ Պտուտակեք ֆիլտրումը և այն ավարտված է:
Բայց եկեք չափենք ժամանակը.
2 րոպե 47 վայրկյան գրեթե 10 հազար VM-ներ մշակելիս: Բոնուսը զտիչների բացակայությունն է և արդյունքները ձեռքով տեսակավորելու անհրաժեշտությունը: Ակնհայտ է, որ սցենարը պահանջում է օպտիմալացում:
Runspaces-ն առաջինն է օգնության գալիս, երբ դուք պետք է միաժամանակ ձեռք բերեք հյուրընկալող չափումներ vCenter-ից կամ պետք է մշակեք տասնյակ հազարավոր օբյեկտներ: Տեսնենք, թե ինչ է բերում այս մոտեցումը։
Միացրեք առաջին արագությունը՝ PowerShell Runspaces
Առաջին բանը, որ գալիս է մտքում այս սկրիպտի համար, օղակը ոչ թե հաջորդական, այլ զուգահեռ շղթաներով գործարկելն է, հավաքել բոլոր տվյալները մեկ օբյեկտի մեջ և զտել այն:
Բայց կա մի խնդիր. PowerCLI-ն թույլ չի տա մեզ բացել բազմաթիվ անկախ նիստեր vCenter-ի համար և ծիծաղելի սխալ կթողնի.
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.
Դա լուծելու համար նախ պետք է փոխանցեք նիստի տեղեկատվությունը հոսքի ներսում: Հիշենք, որ PowerShell-ը աշխատում է օբյեկտների հետ, որոնք կարող են փոխանցվել որպես պարամետր կամ ֆունկցիայի կամ ScriptBlock-ի: Եկեք նիստն անցկացնենք նման օբյեկտի տեսքով՝ շրջանցելով $global:DefaultVIServers-ը (Connect-VIServer-ը -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 $_
}
}
}
Այժմ եկեք իրականացնենք multithreading-ը Runspace Pools-ի միջոցով:
Ալգորիթմը հետևյալն է.
Մենք ստանում ենք բոլոր VM-ների ցուցակը:
Զուգահեռ հոսքերում մենք ստանում ենք cloud.uuid:
Մենք հոսքերից տվյալներ ենք հավաքում մեկ օբյեկտի մեջ:
Մենք զտում ենք օբյեկտը՝ այն խմբավորելով CloudUUID դաշտի արժեքով. նրանք, որտեղ եզակի արժեքների թիվը 1-ից մեծ է, հենց այն VM-ներն են, որոնք մենք փնտրում ենք:
Արդյունքում մենք ստանում ենք սցենար.
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
}
Այս սցենարի գեղեցկությունն այն է, որ այն կարող է օգտագործվել այլ նմանատիպ դեպքերում՝ պարզապես փոխարինելով ScriptBlock-ը և այն պարամետրերը, որոնք կփոխանցվեն հոսքին։ Շահագործե՛ք այն։
Մենք չափում ենք ժամանակը.
55 վայրկյան: Ավելի լավ է, բայց դեռ կարող է ավելի արագ լինել:
Անցնենք երկրորդ արագությանը` GetView
Եկեք պարզենք, թե ինչն է սխալ:
Նախ և առաջ Get-VM cmdlet-ի գործարկումը երկար ժամանակ է պահանջում:
Երկրորդ, Get-AdvancedOptions cmdlet-ի ավարտը ավելի երկար է տևում:
Նախ անդրադառնանք երկրորդին։
Get-AdvancedOptions-ը հարմար է անհատական VM օբյեկտների համար, բայց շատ անշնորհք է շատ օբյեկտների հետ աշխատելիս: Նույն տեղեկատվությունը մենք կարող ենք ստանալ հենց վիրտուալ մեքենայի օբյեկտից (Get-VM): Այն պարզապես լավ թաղված է ExtensionData օբյեկտում: Զինված զտիչով, մենք արագացնում ենք անհրաժեշտ տվյալների ստացման գործընթացը։
Ձեռքի մի փոքր շարժումով սա հետևյալն է.
VM | Get-AdvancedSetting -Name Cloud.uuid -Server $ConnectionString | Select-Object @{N="VMName";E={$_.Entity.Name}},@{N="CloudUUID";E={$_.Value}},@{N="PowerState";E={$_.Entity.PowerState}}
Արդյունքը նույնն է, ինչ Get-AdvancedOptions-ը, բայց այն աշխատում է մի քանի անգամ ավելի արագ:
Այժմ Get-VM-ին: Այն արագ չէ, քանի որ գործ ունի բարդ օբյեկտների հետ: Տրամաբանական հարց է առաջանում՝ այս դեպքում մեզ ինչի՞ն է պետք հավելյալ տեղեկատվություն և հրեշավոր PSO-օբյեկտ, երբ մեզ պարզապես անհրաժեշտ է VM-ի անունը, նրա վիճակը և խրթին հատկանիշի արժեքը։
Բացի այդ, սցենարից հանվել է Get-AdvancedOptions-ի տեսքով խոչընդոտը։ Runspace Pools-ի օգտագործումն այժմ չափազանցված է թվում, քանի որ նիստը հանձնելիս այլևս կարիք չկա դանդաղ առաջադրանքը զուգահեռեցնել squat threads-ով: Գործիքը լավն է, բայց ոչ այս դեպքի համար:
Եկեք նայենք ExtensionData-ի արդյունքին. այն ոչ այլ ինչ է, քան Get-View օբյեկտ:
Եկեք դիմենք PowerShell-ի վարպետների հնագույն տեխնիկան՝ մեկ տող օգտագործելով զտիչներ, տեսակավորում և խմբավորում: Ամբողջ նախորդ սարսափը նրբագեղ կերպով փլուզվում է մեկ տողի մեջ և կատարվում է մեկ նիստում.
9 վայրկյան գրեթե 10 հազար օբյեկտների համար, որոնք զտվում են ըստ ցանկալի վիճակի: Հիանալի
Փոխարենը մի եզրակացության
Ընդունելի արդյունքն ուղղակիորեն կախված է գործիքի ընտրությունից: Հաճախ դժվար է հստակ ասել, թե կոնկրետ ինչ պետք է ընտրել դրան հասնելու համար։ Սկրիպտների արագացման թվարկված մեթոդներից յուրաքանչյուրը լավ է իր կիրառելիության սահմաններում: Հուսով եմ, որ այս հոդվածը կօգնի ձեզ ձեր ենթակառուցվածքում գործընթացների ավտոմատացման և օպտիմալացման հիմունքները հասկանալու դժվարին գործում:
PS: Հեղինակը շնորհակալություն է հայտնում համայնքի բոլոր անդամներին հոդվածի պատրաստման հարցում ցուցաբերած օգնության և աջակցության համար: Նույնիսկ թաթիկներով: Եվ նույնիսկ նրանք, ովքեր ոտքեր չունեն, ինչպես բոա կոնստրուկտորը: