วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI 

ไม่ช้าก็เร็วผู้ดูแลระบบ VMware จะมาทำงานประจำโดยอัตโนมัติ ทุกอย่างเริ่มต้นด้วยบรรทัดคำสั่ง จากนั้นมาด้วย PowerShell หรือ VMware PowerCLI

สมมติว่าคุณเชี่ยวชาญ PowerShell เกินกว่าการเปิดตัว ISE เล็กน้อย และใช้ cmdlets มาตรฐานจากโมดูลที่ทำงานเนื่องจาก "เวทย์มนตร์บางอย่าง" เมื่อคุณเริ่มนับเครื่องเสมือนเป็นร้อย คุณจะพบว่าสคริปต์ที่ช่วยงานขนาดเล็กจะทำงานช้าลงอย่างเห็นได้ชัดในขนาดใหญ่ 

ในสถานการณ์เช่นนี้ 2 เครื่องมือจะช่วยได้:

  • PowerShell Runspaces – แนวทางที่ช่วยให้คุณขนานการดำเนินการของกระบวนการในเธรดที่แยกจากกัน 
  • รับ-ดู – ฟังก์ชัน PowerCLI พื้นฐาน ซึ่งเป็นอะนาล็อกของ Get-WMIObject ใน Windows cmdlet นี้ไม่ได้ดึงวัตถุที่มาพร้อมกับเอนทิตี แต่รับข้อมูลในรูปแบบของวัตถุอย่างง่ายที่มีชนิดข้อมูลอย่างง่าย ในหลายกรณีจะออกเร็วกว่า

ต่อไป ฉันจะพูดคุยสั้นๆ เกี่ยวกับเครื่องมือแต่ละอย่างและแสดงตัวอย่างการใช้งาน มาวิเคราะห์สคริปต์ที่เฉพาะเจาะจงและดูว่าสคริปต์ใดทำงานได้ดีกว่าสคริปต์อื่น ไป!

วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI

ขั้นแรก: Runspace

ดังนั้น Runspace ได้รับการออกแบบมาเพื่อการประมวลผลงานนอกโมดูลหลักแบบขนาน แน่นอน คุณสามารถเปิดกระบวนการอื่นที่จะกินหน่วยความจำ ตัวประมวลผล ฯลฯ บางส่วนได้ หากสคริปต์ของคุณรันภายในสองสามนาทีและใช้หน่วยความจำหนึ่งกิกะไบต์ เป็นไปได้มากว่าคุณจะไม่ต้องใช้ Runspace แต่สำหรับสคริปต์สำหรับอ็อบเจ็กต์นับหมื่นรายการนั้นจำเป็น

คุณสามารถเริ่มเรียนรู้ได้ที่นี่: 
การเริ่มต้นใช้งาน PowerShell Runspaces: ตอนที่ 1

การใช้ Runspace ให้อะไร:

  • ความเร็วโดยการจำกัดรายการคำสั่งที่ดำเนินการ
  • การปฏิบัติงานแบบขนาน
  • ความปลอดภัย.

นี่คือตัวอย่างจากอินเทอร์เน็ตเมื่อ Runspace ช่วย:

“ความขัดแย้งด้านพื้นที่จัดเก็บข้อมูลเป็นหนึ่งในตัวชี้วัดที่ยากที่สุดในการติดตามใน vSphere ภายใน vCenter คุณไม่สามารถไปดูได้ว่า VM ตัวใดใช้ทรัพยากรพื้นที่จัดเก็บข้อมูลมากกว่า โชคดีที่คุณสามารถรวบรวมข้อมูลนี้ได้ภายในไม่กี่นาทีด้วย PowerShell
ฉันจะแชร์สคริปต์ที่จะช่วยให้ผู้ดูแลระบบ VMware ค้นหาทั่วทั้ง vCenter ได้อย่างรวดเร็วและรับรายการ VM พร้อมข้อมูลการใช้งานโดยเฉลี่ย  
สคริปต์ใช้รันสเปซ PowerShell เพื่ออนุญาตให้แต่ละโฮสต์ ESXi รวบรวมข้อมูลการใช้งานจาก VM ของตัวเองใน Runspace ที่แยกต่างหาก และรายงานการดำเนินการเสร็จสิ้นทันที ซึ่งช่วยให้ PowerShell สามารถปิดงานได้ทันที แทนที่จะวนซ้ำผ่านโฮสต์และรอให้แต่ละงานดำเนินการตามคำขอของตนให้เสร็จสิ้น”

ที่มา: วิธีแสดง Virtual Machine I/O บนแดชบอร์ด ESXi

ในกรณีด้านล่าง Runspace จะไม่มีประโยชน์อีกต่อไป:

“ฉันกำลังพยายามเขียนสคริปต์ที่รวบรวมข้อมูลจำนวนมากจาก VM และเขียนข้อมูลใหม่เมื่อจำเป็น ปัญหาคือมี VM ค่อนข้างมาก และใช้เวลา 5-8 วินาทีในเครื่องเดียว” 

ที่มา: PowerCLI แบบมัลติเธรดพร้อม RunspacePool

ที่นี่คุณจะต้องมี 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) 

ทางออกที่ชัดเจนที่อยู่ในใจคือ:

  1. รับรายการ VM ทั้งหมด
  2. แยกวิเคราะห์รายการอย่างใด

เวอร์ชันดั้งเดิมคือสคริปต์ง่ายๆ นี้:

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

ทุกอย่างเรียบง่ายและชัดเจนมาก สามารถเขียนได้ภายในไม่กี่นาทีด้วยช่วงพักดื่มกาแฟ ขันเกลียวกรองก็เสร็จแล้ว

แต่มาวัดเวลากัน:

วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI

วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI

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  

อัลกอริทึมมีดังนี้:

  1. เราได้รับรายการ VM ทั้งหมด
  2. ในสตรีมแบบขนานเราได้รับ cloud.uuid
  3. เรารวบรวมข้อมูลจากสตรีมเป็นวัตถุเดียว
  4. เรากรองออบเจ็กต์โดยการจัดกลุ่มตามค่าของฟิลด์ 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 และพารามิเตอร์ที่จะถูกส่งไปยังสตรีม ใช้ประโยชน์จากมัน!

เราวัดเวลา:

วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI

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

เราวัดเวลา:

วิธีสร้างจรวดสนับสนุนสำหรับสคริปต์ PowerCLI

9 วินาที สำหรับวัตถุเกือบ 10 ชิ้นที่มีการกรองตามเงื่อนไขที่ต้องการ ยอดเยี่ยม!

แทนการสรุป

ผลลัพธ์ที่ยอมรับได้โดยตรงขึ้นอยู่กับการเลือกเครื่องมือ มักจะเป็นเรื่องยากที่จะบอกว่าอะไรควรเลือกเพื่อให้บรรลุเป้าหมาย แต่ละวิธีที่ระบุไว้ในการเร่งความเร็วสคริปต์นั้นดีภายในขอบเขตการใช้งาน ฉันหวังว่าบทความนี้จะช่วยคุณในงานที่ยากลำบากในการทำความเข้าใจพื้นฐานของกระบวนการอัตโนมัติและการเพิ่มประสิทธิภาพในโครงสร้างพื้นฐานของคุณ

PS: ผู้เขียนขอขอบคุณสมาชิกชุมชนทุกท่านสำหรับความช่วยเหลือและสนับสนุนในการจัดทำบทความ แม้กระทั่งผู้ที่มีอุ้งเท้า และแม้แต่คนไม่มีขาก็เหมือนงูเหลือมบีบรัด

ที่มา: will.com

เพิ่มความคิดเห็น