ไม่ช้าก็เร็วผู้ดูแลระบบ VMware จะมาทำงานประจำโดยอัตโนมัติ ทุกอย่างเริ่มต้นด้วยบรรทัดคำสั่ง จากนั้นมาด้วย PowerShell หรือ VMware PowerCLI
สมมติว่าคุณเชี่ยวชาญ PowerShell เกินกว่าการเปิดตัว ISE เล็กน้อย และใช้ cmdlets มาตรฐานจากโมดูลที่ทำงานเนื่องจาก "เวทย์มนตร์บางอย่าง" เมื่อคุณเริ่มนับเครื่องเสมือนเป็นร้อย คุณจะพบว่าสคริปต์ที่ช่วยงานขนาดเล็กจะทำงานช้าลงอย่างเห็นได้ชัดในขนาดใหญ่
ในสถานการณ์เช่นนี้ 2 เครื่องมือจะช่วยได้:
- PowerShell Runspaces – แนวทางที่ช่วยให้คุณขนานการดำเนินการของกระบวนการในเธรดที่แยกจากกัน
- รับ-ดู – ฟังก์ชัน PowerCLI พื้นฐาน ซึ่งเป็นอะนาล็อกของ Get-WMIObject ใน Windows cmdlet นี้ไม่ได้ดึงวัตถุที่มาพร้อมกับเอนทิตี แต่รับข้อมูลในรูปแบบของวัตถุอย่างง่ายที่มีชนิดข้อมูลอย่างง่าย ในหลายกรณีจะออกเร็วกว่า
ต่อไป ฉันจะพูดคุยสั้นๆ เกี่ยวกับเครื่องมือแต่ละอย่างและแสดงตัวอย่างการใช้งาน มาวิเคราะห์สคริปต์ที่เฉพาะเจาะจงและดูว่าสคริปต์ใดทำงานได้ดีกว่าสคริปต์อื่น ไป!
ขั้นแรก: Runspace
ดังนั้น Runspace ได้รับการออกแบบมาเพื่อการประมวลผลงานนอกโมดูลหลักแบบขนาน แน่นอน คุณสามารถเปิดกระบวนการอื่นที่จะกินหน่วยความจำ ตัวประมวลผล ฯลฯ บางส่วนได้ หากสคริปต์ของคุณรันภายในสองสามนาทีและใช้หน่วยความจำหนึ่งกิกะไบต์ เป็นไปได้มากว่าคุณจะไม่ต้องใช้ Runspace แต่สำหรับสคริปต์สำหรับอ็อบเจ็กต์นับหมื่นรายการนั้นจำเป็น
คุณสามารถเริ่มเรียนรู้ได้ที่นี่:
การเริ่มต้นใช้งาน PowerShell Runspaces: ตอนที่ 1
การใช้ Runspace ให้อะไร:
- ความเร็วโดยการจำกัดรายการคำสั่งที่ดำเนินการ
- การปฏิบัติงานแบบขนาน
- ความปลอดภัย.
นี่คือตัวอย่างจากอินเทอร์เน็ตเมื่อ Runspace ช่วย:
“ความขัดแย้งด้านพื้นที่จัดเก็บข้อมูลเป็นหนึ่งในตัวชี้วัดที่ยากที่สุดในการติดตามใน vSphere ภายใน vCenter คุณไม่สามารถไปดูได้ว่า VM ตัวใดใช้ทรัพยากรพื้นที่จัดเก็บข้อมูลมากกว่า โชคดีที่คุณสามารถรวบรวมข้อมูลนี้ได้ภายในไม่กี่นาทีด้วย PowerShell
ฉันจะแชร์สคริปต์ที่จะช่วยให้ผู้ดูแลระบบ VMware ค้นหาทั่วทั้ง vCenter ได้อย่างรวดเร็วและรับรายการ VM พร้อมข้อมูลการใช้งานโดยเฉลี่ย
สคริปต์ใช้รันสเปซ PowerShell เพื่ออนุญาตให้แต่ละโฮสต์ ESXi รวบรวมข้อมูลการใช้งานจาก VM ของตัวเองใน Runspace ที่แยกต่างหาก และรายงานการดำเนินการเสร็จสิ้นทันที ซึ่งช่วยให้ PowerShell สามารถปิดงานได้ทันที แทนที่จะวนซ้ำผ่านโฮสต์และรอให้แต่ละงานดำเนินการตามคำขอของตนให้เสร็จสิ้น”
ในกรณีด้านล่าง Runspace จะไม่มีประโยชน์อีกต่อไป:
“ฉันกำลังพยายามเขียนสคริปต์ที่รวบรวมข้อมูลจำนวนมากจาก VM และเขียนข้อมูลใหม่เมื่อจำเป็น ปัญหาคือมี VM ค่อนข้างมาก และใช้เวลา 5-8 วินาทีในเครื่องเดียว”
ที่นี่คุณจะต้องมี Get-View มาดูกันดีกว่า
ขั้นตอนที่สอง: รับ-ดู
เพื่อให้เข้าใจว่าเหตุใด Get-View จึงมีประโยชน์ จึงควรค่าแก่การจดจำว่า cmdlets ทำงานโดยทั่วไปอย่างไร
จำเป็นต้องใช้ Cmdlets เพื่อรับข้อมูลได้อย่างสะดวกโดยไม่จำเป็นต้องศึกษาหนังสืออ้างอิง API และสร้างวงล้อถัดไปขึ้นมาใหม่ สิ่งที่ในสมัยก่อนใช้โค้ดหนึ่งร้อยหรือสองบรรทัด PowerShell ช่วยให้คุณทำได้ด้วยคำสั่งเดียว เราจ่ายค่าความสะดวกนี้ด้วยความรวดเร็ว ไม่มีเวทย์มนตร์ในตัว cmdlets: สคริปต์เดียวกัน แต่ในระดับที่ต่ำกว่าเขียนโดยมือผู้ชำนาญของปรมาจารย์จากอินเดียที่มีแดดจัด
ตอนนี้เพื่อเปรียบเทียบกับ Get-View ลองใช้ Get-VM cmdlet: มันเข้าถึงเครื่องเสมือนและส่งคืนออบเจ็กต์คอมโพสิตนั่นคือมันแนบออบเจ็กต์อื่น ๆ ที่เกี่ยวข้อง: VMHost, Datastore เป็นต้น
Get-View แทนที่ไม่ได้เพิ่มสิ่งที่ไม่จำเป็นให้กับวัตถุที่ส่งคืน นอกจากนี้ยังช่วยให้เราสามารถระบุข้อมูลที่เราต้องการได้อย่างเคร่งครัด ซึ่งจะทำให้ออบเจ็กต์เอาต์พุตง่ายขึ้น ใน Windows Server โดยทั่วไปและใน Hyper-V โดยเฉพาะ Get-WMIObject cmdlet นั้นเป็นอะนาล็อกโดยตรง - แนวคิดนี้เหมือนกันทุกประการ
รับมุมมองไม่สะดวกสำหรับการดำเนินการตามปกติบนวัตถุจุด แต่เมื่อเป็นวัตถุนับพันนับหมื่นก็ไม่มีราคา
คุณสามารถอ่านเพิ่มเติมได้ในบล็อกของ VMware:
ข้อมูลเบื้องต้นเกี่ยวกับ Get-View
ตอนนี้ฉันจะแสดงให้คุณเห็นทุกอย่างโดยใช้กรณีจริง
การเขียนสคริปต์เพื่อยกเลิกการโหลด VM
วันหนึ่งเพื่อนร่วมงานของฉันขอให้ฉันปรับสคริปต์ของเขาให้เหมาะสม งานนี้เป็นกิจวัตรทั่วไป: ค้นหา VM ทั้งหมดที่มีพารามิเตอร์ cloud.uuid ซ้ำกัน (ใช่ ซึ่งเป็นไปได้เมื่อทำการโคลน VM ใน vCloud Director)
ทางออกที่ชัดเจนที่อยู่ในใจคือ:
- รับรายการ 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 วินาที เมื่อประมวลผล VM เกือบ 10 รายการ โบนัสคือการไม่มีตัวกรองและจำเป็นต้องจัดเรียงผลลัพธ์ด้วยตนเอง แน่นอนว่าสคริปต์ต้องการการปรับให้เหมาะสม
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 (เชื่อมต่อ-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 $_
}
}
}
ตอนนี้เรามาปรับใช้มัลติเธรดผ่าน 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}}
กลายเป็นสิ่งนี้:
$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}}
ผลลัพธ์จะเหมือนกับ Get-AdvancedOptions แต่ทำงานได้เร็วกว่าหลายเท่า
ตอนนี้เพื่อรับ-VM มันไม่เร็วเพราะมันเกี่ยวข้องกับวัตถุที่ซับซ้อน คำถามเชิงตรรกะเกิดขึ้น: เหตุใดเราจึงต้องการข้อมูลเพิ่มเติมและ PSObject ที่น่ากลัวในกรณีนี้ ในเมื่อเราต้องการเพียงชื่อของ VM สถานะและค่าของแอตทริบิวต์ที่ยุ่งยาก
นอกจากนี้อุปสรรคในรูปแบบของ Get-AdvancedOptions ได้ถูกลบออกจากสคริปต์แล้ว การใช้ Runspace Pools ดูเหมือนจะต้องใช้มากเกินไป เนื่องจากไม่จำเป็นต้องทำภารกิจที่ช้าพร้อมกันใน squat threads เมื่อส่งมอบเซสชันอีกต่อไป เครื่องมือนี้ดี แต่ไม่ใช่สำหรับกรณีนี้
ลองดูที่ผลลัพธ์ของ ExtensionData: มันไม่มีอะไรมากไปกว่าวัตถุ Get-View
เรามาเรียกเทคนิคโบราณของปรมาจารย์ PowerShell: หนึ่งบรรทัดโดยใช้ตัวกรอง การเรียงลำดับและการจัดกลุ่ม ความสยองขวัญก่อนหน้านี้ทั้งหมดถูกรวบรวมไว้อย่างหรูหราเป็นบรรทัดเดียวและดำเนินการในเซสชันเดียว:
$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
เราวัดเวลา:
9 วินาที สำหรับวัตถุเกือบ 10 ชิ้นที่มีการกรองตามเงื่อนไขที่ต้องการ ยอดเยี่ยม!
แทนการสรุป
ผลลัพธ์ที่ยอมรับได้โดยตรงขึ้นอยู่กับการเลือกเครื่องมือ มักจะเป็นเรื่องยากที่จะบอกว่าอะไรควรเลือกเพื่อให้บรรลุเป้าหมาย แต่ละวิธีที่ระบุไว้ในการเร่งความเร็วสคริปต์นั้นดีภายในขอบเขตการใช้งาน ฉันหวังว่าบทความนี้จะช่วยคุณในงานที่ยากลำบากในการทำความเข้าใจพื้นฐานของกระบวนการอัตโนมัติและการเพิ่มประสิทธิภาพในโครงสร้างพื้นฐานของคุณ
PS: ผู้เขียนขอขอบคุณสมาชิกชุมชนทุกท่านสำหรับความช่วยเหลือและสนับสนุนในการจัดทำบทความ แม้กระทั่งผู้ที่มีอุ้งเท้า และแม้แต่คนไม่มีขาก็เหมือนงูเหลือมบีบรัด
ที่มา: will.com