Cara membuat pendorong roket untuk skrip PowerCLI 

Cepat atau lambat, administrator sistem VMware mana pun akan mengotomatiskan tugas-tugas rutin. Semuanya dimulai dengan baris perintah, lalu muncul PowerShell atau VMware PowerCLI.

Katakanlah Anda telah menguasai PowerShell sedikit lebih jauh daripada meluncurkan ISE dan menggunakan cmdlet standar dari modul yang berfungsi karena "semacam keajaiban". Saat Anda mulai menghitung ratusan mesin virtual, Anda akan menemukan bahwa skrip yang membantu dalam skala kecil berjalan jauh lebih lambat dalam skala besar. 

Dalam situasi ini, 2 alat akan membantu:

  • Ruang Proses PowerShell – pendekatan yang memungkinkan Anda memparalelkan eksekusi proses di thread terpisah; 
  • Dapatkan-Lihat – fungsi dasar PowerCLI, analog dari Get-WMIObject di Windows. Cmdlet ini tidak menarik objek entitas yang menyertainya, tetapi menerima informasi berupa objek sederhana dengan tipe data sederhana. Dalam banyak kasus, hasilnya lebih cepat.

Selanjutnya, saya akan membahas secara singkat setiap alat dan menunjukkan contoh penggunaannya. Mari kita menganalisis skrip tertentu dan melihat mana yang bekerja lebih baik dari yang lain. Pergi!

Cara membuat pendorong roket untuk skrip PowerCLI

Tahap pertama: Runspace

Jadi, Runspace dirancang untuk pemrosesan paralel tugas di luar modul utama. Tentu saja, Anda dapat meluncurkan proses lain yang akan menghabiskan sebagian memori, prosesor, dll. Jika skrip Anda berjalan dalam beberapa menit dan menghabiskan satu gigabyte memori, kemungkinan besar Anda tidak memerlukan Runspace. Namun untuk skrip puluhan ribu objek diperlukan.

Anda dapat mulai belajar di sini: 
Awal Penggunaan PowerShell Runspaces: Bagian 1

Apa yang diberikan oleh penggunaan Runspace:

  • kecepatan dengan membatasi daftar perintah yang dieksekusi,
  • pelaksanaan tugas secara paralel,
  • keamanan.

Berikut adalah contoh dari Internet ketika Runspace membantu:

β€œPertentangan penyimpanan adalah salah satu metrik yang paling sulit dilacak di vSphere. Di dalam vCenter, Anda tidak bisa begitu saja melihat VM mana yang menggunakan lebih banyak sumber daya penyimpanan. Untungnya, Anda dapat mengumpulkan data ini dalam hitungan menit berkat PowerShell.
Saya akan membagikan skrip yang memungkinkan administrator sistem VMware mencari dengan cepat di seluruh vCenter dan menerima daftar VM dengan data konsumsi rata-ratanya.  
Skrip ini menggunakan runspace PowerShell untuk memungkinkan setiap host ESXi mengumpulkan informasi konsumsi dari VMnya sendiri di Runspace terpisah dan segera melaporkan penyelesaiannya. Hal ini memungkinkan PowerShell untuk segera menutup pekerjaan, daripada melakukan iterasi melalui host dan menunggu masing-masing host menyelesaikan permintaannya.”

Sumber: Cara Menampilkan I/O Mesin Virtual di Dasbor ESXi

Dalam kasus di bawah ini, Runspace tidak lagi berguna:

β€œSaya mencoba menulis skrip yang mengumpulkan banyak data dari VM dan menulis data baru bila diperlukan. Masalahnya adalah jumlah VM yang cukup banyak, dan 5-8 detik dihabiskan untuk satu mesin.” 

Sumber: Multithreading PowerCLI dengan RunspacePool

Di sini Anda memerlukan Get-View, mari kita lanjutkan ke sana. 

Tahap kedua: Dapatkan-Lihat

Untuk memahami mengapa Get-View berguna, ada baiknya mengingat cara kerja cmdlet secara umum. 

Cmdlet diperlukan untuk memperoleh informasi dengan mudah tanpa perlu mempelajari buku referensi API dan menemukan kembali roda berikutnya. Apa yang dulu memerlukan seratus atau dua baris kode, PowerShell memungkinkan Anda melakukannya dengan satu perintah. Kami membayar kenyamanan ini dengan kecepatan. Tidak ada keajaiban di dalam cmdlet itu sendiri: skrip yang sama, tetapi pada tingkat yang lebih rendah, ditulis oleh tangan terampil seorang master dari India yang cerah.

Sekarang, untuk perbandingan dengan Get-View, mari kita ambil cmdlet Get-VM: ia mengakses mesin virtual dan mengembalikan objek komposit, yaitu melampirkan objek terkait lainnya ke dalamnya: VMHost, Datastore, dll.  

Get-View sebagai gantinya tidak menambahkan apa pun yang tidak perlu ke objek yang dikembalikan. Selain itu, ini memungkinkan kita untuk secara ketat menentukan informasi apa yang kita perlukan, yang akan membuat objek keluaran lebih mudah. Di Windows Server secara umum dan di Hyper-V khususnya, cmdlet Get-WMIObject adalah analog langsungnya - idenya persis sama.

Get-View tidak nyaman untuk operasi rutin pada objek titik. Namun jika menyangkut ribuan dan puluhan ribu benda, tidak ada harganya.

Anda dapat membaca lebih lanjut di blog VMware: Pengantar Get-View

Sekarang saya akan menunjukkan semuanya menggunakan kasus nyata. 

Menulis skrip untuk membongkar VM

Suatu hari rekan saya meminta saya untuk mengoptimalkan skripnya. Tugasnya adalah rutinitas yang umum: temukan semua VM dengan parameter cloud.uuid duplikat (ya, ini mungkin dilakukan saat mengkloning VM di vCloud Director). 

Solusi jelas yang terlintas dalam pikiran adalah:

  1. Dapatkan daftar semua VM.
  2. Parsing daftarnya entah bagaimana.

Versi aslinya adalah skrip sederhana ini:

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
}
# Π”Π°Π»Π΅Π΅ РУКАМИ парсим ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚

Semuanya sangat sederhana dan jelas. Itu bisa ditulis dalam beberapa menit dengan rehat kopi. Pasang filtrasi dan selesai.

Tapi mari kita ukur waktunya:

Cara membuat pendorong roket untuk skrip PowerCLI

Cara membuat pendorong roket untuk skrip PowerCLI

2 menit 47 detik saat memproses hampir 10 ribu VM. Bonusnya adalah tidak adanya filter dan kebutuhan untuk mengurutkan hasil secara manual. Jelas, skrip memerlukan optimasi.

Runspace adalah solusi pertama saat Anda perlu mendapatkan metrik host secara bersamaan dari vCenter atau perlu memproses puluhan ribu objek. Mari kita lihat apa manfaat dari pendekatan ini.

Nyalakan kecepatan pertama: PowerShell Runspaces

Hal pertama yang terlintas dalam pikiran skrip ini adalah mengeksekusi loop tidak secara berurutan, tetapi dalam thread paralel, mengumpulkan semua data ke dalam satu objek dan memfilternya. 

Namun ada masalah: PowerCLI tidak mengizinkan kami membuka banyak sesi independen ke vCenter dan akan menimbulkan kesalahan lucu:

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.

Untuk mengatasi ini, Anda harus terlebih dahulu meneruskan informasi sesi di dalam aliran. Ingatlah bahwa PowerShell bekerja dengan objek yang dapat diteruskan sebagai parameter ke suatu fungsi atau ke ScriptBlock. Mari kita lewati sesi dalam bentuk objek seperti itu, melewati $global:DefaultVIServers (Connect-VIServer dengan kunci -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 $_
           }
       }
   }

Sekarang mari kita terapkan multithreading melalui Runspace Pools.  

Algoritmanya adalah sebagai berikut:

  1. Kami mendapatkan daftar semua VM.
  2. Dalam aliran paralel kita mendapatkan cloud.uuid.
  3. Kami mengumpulkan data dari aliran ke dalam satu objek.
  4. Kami memfilter objek dengan mengelompokkannya berdasarkan nilai bidang CloudUUID: objek yang jumlah nilai uniknya lebih besar dari 1 adalah VM yang kami cari.

Hasilnya, kami mendapatkan skrip:


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
}

Keunggulan skrip ini adalah dapat digunakan dalam kasus serupa lainnya hanya dengan mengganti ScriptBlock dan parameter yang akan diteruskan ke aliran. Manfaatkan itu!

Kami mengukur waktu:

Cara membuat pendorong roket untuk skrip PowerCLI

55 detik. Itu lebih baik, tapi masih bisa lebih cepat. 

Mari beralih ke kecepatan kedua: GetView

Mari kita cari tahu apa yang salah.
Pertama dan terpenting, cmdlet Get-VM membutuhkan waktu lama untuk dijalankan.
Kedua, cmdlet Get-AdvancedOptions membutuhkan waktu lebih lama untuk diselesaikan.
Mari kita bahas yang kedua dulu. 

Get-AdvancedOptions cocok untuk objek VM individual, namun sangat kikuk saat bekerja dengan banyak objek. Kita bisa mendapatkan informasi yang sama dari objek mesin virtual itu sendiri (Get-VM). Itu hanya terkubur dengan baik di objek ExtensionData. Berbekal filtering, kami mempercepat proses perolehan data yang diperlukan.

Dengan sedikit gerakan tangan, hasilnya adalah:


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

Berubah menjadi ini:


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

Outputnya sama dengan Get-AdvancedOptions, tetapi bekerja berkali-kali lebih cepat. 

Sekarang untuk Mendapatkan-VM. Ini tidak cepat karena berhubungan dengan objek yang kompleks. Sebuah pertanyaan logis muncul: mengapa kita memerlukan informasi tambahan dan PSObject yang mengerikan dalam kasus ini, padahal kita hanya memerlukan nama VM, statusnya, dan nilai atribut yang rumit?  

Selain itu, kendala berupa Get-AdvancedOptions telah dihilangkan dari skrip. Menggunakan Runspace Pools sekarang sepertinya berlebihan karena tidak ada lagi kebutuhan untuk memparalelkan tugas yang lambat di seluruh thread jongkok saat menyerahkan sesi. Alatnya bagus, tapi tidak untuk kasus ini. 

Mari kita lihat keluaran dari ExtensionData: ini tidak lebih dari objek Get-View. 

Mari kita lihat teknik kuno para master PowerShell: satu baris menggunakan filter, pengurutan, dan pengelompokan. Semua horor sebelumnya dengan elegan digabung menjadi satu baris dan dieksekusi dalam satu sesi:


$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

Kami mengukur waktu:

Cara membuat pendorong roket untuk skrip PowerCLI

9 detik untuk hampir 10 ribu objek dengan pemfilteran berdasarkan kondisi yang diinginkan. Besar!

Alih-alih sebuah kesimpulan

Hasil yang dapat diterima secara langsung bergantung pada pilihan alat. Seringkali sulit untuk mengatakan dengan pasti apa sebenarnya yang harus dipilih untuk mencapainya. Masing-masing metode yang tercantum untuk mempercepat skrip bagus dalam batasan penerapannya. Saya harap artikel ini akan membantu Anda dalam tugas sulit dalam memahami dasar-dasar otomatisasi dan pengoptimalan proses di infrastruktur Anda.

PS: Penulis berterima kasih kepada seluruh anggota komunitas atas bantuan dan dukungannya dalam mempersiapkan artikel ini. Bahkan mereka yang mempunyai cakar. Dan bahkan mereka yang tidak memiliki kaki, seperti ular boa.

Sumber: www.habr.com

Tambah komentar