Bagaimana untuk membina penggalak roket untuk skrip PowerCLI 

Lambat laun, mana-mana pentadbir sistem VMware datang untuk mengautomasikan tugas rutin. Semuanya bermula dengan baris arahan, kemudian datang PowerShell atau VMware PowerCLI.

Katakan anda telah menguasai PowerShell sedikit lebih jauh daripada melancarkan ISE dan menggunakan cmdlet standard daripada modul yang berfungsi disebabkan oleh "semacam keajaiban". Apabila anda mula mengira mesin maya dalam ratusan, anda akan mendapati bahawa skrip yang membantu dalam skala kecil berjalan dengan ketara lebih perlahan pada skala besar. 

Dalam situasi ini, 2 alat akan membantu:

  • PowerShell Runspaces – pendekatan yang membolehkan anda menyelaraskan pelaksanaan proses dalam benang berasingan; 
  • Dapatkan-View – fungsi asas PowerCLI, analog Get-WMIObject dalam Windows. Cmdlet ini tidak menarik objek yang mengiringi entiti, tetapi menerima maklumat dalam bentuk objek ringkas dengan jenis data ringkas. Dalam banyak kes ia keluar lebih cepat.

Seterusnya, saya akan bercakap secara ringkas tentang setiap alat dan menunjukkan contoh penggunaan. Mari analisa skrip tertentu dan lihat apabila satu skrip berfungsi lebih baik daripada yang lain. Pergi!

Bagaimana untuk membina penggalak roket untuk skrip PowerCLI

Peringkat pertama: Runspace

Jadi, Runspace direka untuk pemprosesan selari tugas di luar modul utama. Sudah tentu, anda boleh melancarkan proses lain yang akan memakan sedikit memori, pemproses, dll. Jika skrip anda berjalan dalam beberapa minit dan menggunakan memori gigabait, kemungkinan besar anda tidak memerlukan Runspace. Tetapi untuk skrip untuk puluhan ribu objek ia diperlukan.

Anda boleh mula belajar di sini: 
Permulaan Penggunaan PowerShell Runspaces: Bahagian 1

Apakah yang diberikan oleh Runspace:

  • kelajuan dengan mengehadkan senarai arahan yang dilaksanakan,
  • pelaksanaan tugas selari,
  • keselamatan.

Berikut ialah contoh daripada Internet apabila Runspace membantu:

β€œPertikaian storan adalah salah satu metrik paling sukar untuk dijejaki dalam vSphere. Di dalam vCenter, anda tidak boleh pergi dan melihat VM mana yang menggunakan lebih banyak sumber storan. Nasib baik, anda boleh mengumpul data ini dalam beberapa minit terima kasih kepada PowerShell.
Saya akan berkongsi skrip yang akan membolehkan pentadbir sistem VMware mencari dengan pantas di seluruh vCenter dan menerima senarai VM dengan data pada penggunaan purata mereka.  
Skrip menggunakan ruang larian PowerShell untuk membenarkan setiap hos ESXi mengumpul maklumat penggunaan daripada VMnya sendiri dalam Runspace yang berasingan dan segera melaporkan penyiapan. Ini membolehkan PowerShell menutup kerja dengan serta-merta, dan bukannya berulang melalui hos dan menunggu setiap satu untuk menyelesaikan permintaannya."

Sumber: Cara Menunjukkan I/O Mesin Maya pada Papan Pemuka ESXi

Dalam kes di bawah, Runspace tidak lagi berguna:

β€œSaya cuba menulis skrip yang mengumpul banyak data daripada VM dan menulis data baharu apabila perlu. Masalahnya ialah terdapat banyak VM, dan 5-8 saat dihabiskan untuk satu mesin.” 

Sumber: Multithreading PowerCLI dengan RunspacePool

Di sini anda memerlukan Get-View, mari teruskan kepadanya. 

Peringkat kedua: Get-View

Untuk memahami sebab Get-View berguna, anda perlu mengingati cara cmdlet berfungsi secara umum. 

Cmdlet diperlukan untuk mendapatkan maklumat dengan mudah tanpa perlu mengkaji buku rujukan API dan mencipta semula roda seterusnya. Apa yang pada zaman dahulu mengambil seratus atau dua baris kod, PowerShell membolehkan anda lakukan dengan satu arahan. Kami membayar untuk kemudahan ini dengan pantas. Tiada keajaiban di dalam cmdlet itu sendiri: skrip yang sama, tetapi pada tahap yang lebih rendah, ditulis oleh tangan mahir seorang tuan dari India yang cerah.

Sekarang, untuk perbandingan dengan Get-View, mari ambil cmdlet Get-VM: ia mengakses mesin maya dan mengembalikan objek komposit, iaitu, ia melampirkan objek lain yang berkaitan dengannya: VMHost, Datastore, dsb.  

Get-View di tempatnya tidak menambah apa-apa yang tidak perlu pada objek yang dikembalikan. Lebih-lebih lagi, ia membolehkan kami untuk menentukan maklumat yang kami perlukan dengan tegas, yang akan menjadikan objek output lebih mudah. Dalam Pelayan Windows secara umum dan dalam Hyper-V khususnya, cmdlet Get-WMIObject ialah analog langsung - ideanya adalah sama.

Get-View menyusahkan untuk operasi rutin pada objek titik. Tetapi apabila ia datang kepada beribu-ribu dan berpuluh-puluh ribu objek, ia tidak mempunyai harga.

Anda boleh membaca lebih lanjut di blog VMware: Pengenalan kepada Get-View

Sekarang saya akan menunjukkan kepada anda segala-galanya menggunakan kes sebenar. 

Menulis skrip untuk memunggah VM

Pada suatu hari rakan sekerja saya meminta saya mengoptimumkan skripnya. Tugas itu adalah rutin biasa: cari semua VM dengan parameter cloud.uuid pendua (ya, ini mungkin apabila mengklon VM dalam Pengarah vCloud). 

Penyelesaian jelas yang terlintas di fikiran ialah:

  1. Dapatkan senarai semua VM.
  2. Menghuraikan senarai itu entah bagaimana.

Versi asal ialah skrip mudah 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 mudah dan jelas. Ia boleh ditulis dalam beberapa minit dengan rehat kopi. Skru pada penapisan dan ia selesai.

Tetapi mari kita ukur masa:

Bagaimana untuk membina penggalak roket untuk skrip PowerCLI

Bagaimana untuk membina penggalak roket untuk skrip PowerCLI

2 minit 47 saat apabila memproses hampir 10k VM. Bonus ialah ketiadaan penapis dan keperluan untuk mengisih hasil secara manual. Jelas sekali, skrip memerlukan pengoptimuman.

Runspaces adalah yang pertama datang untuk menyelamatkan apabila anda perlu mendapatkan metrik hos secara serentak daripada vCenter atau perlu memproses puluhan ribu objek. Mari lihat apa yang dibawa oleh pendekatan ini.

Hidupkan kelajuan pertama: PowerShell Runspaces

Perkara pertama yang terlintas di fikiran untuk skrip ini adalah untuk melaksanakan gelung bukan secara berurutan, tetapi dalam benang selari, kumpulkan semua data ke dalam satu objek dan tapisnya. 

Tetapi terdapat masalah: PowerCLI tidak akan membenarkan kami membuka banyak sesi bebas ke vCenter dan akan menimbulkan ralat 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 menyelesaikannya, anda mesti menghantar maklumat sesi ke dalam strim terlebih dahulu. Mari kita ingat bahawa PowerShell berfungsi dengan objek yang boleh dihantar sebagai parameter sama ada kepada fungsi atau kepada ScriptBlock. Mari lulus sesi dalam bentuk objek sedemikian, memintas $global:DefaultVIServers (Connect-VIServer dengan kekunci -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 laksanakan multithreading melalui Runspace Pools.  

Algoritma adalah seperti berikut:

  1. Kami mendapat senarai semua VM.
  2. Dalam aliran selari kita mendapat cloud.uuid.
  3. Kami mengumpul data daripada aliran ke dalam satu objek.
  4. Kami menapis objek dengan mengumpulkannya mengikut nilai medan CloudUUID: mereka yang bilangan nilai unik lebih besar daripada 1 ialah VM yang kami cari.

Akibatnya, kami mendapat 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
}

Keindahan skrip ini ialah ia boleh digunakan dalam kes lain yang serupa dengan hanya menggantikan ScriptBlock dan parameter yang akan dihantar ke strim. Eksploitasi!

Kami mengukur masa:

Bagaimana untuk membina penggalak roket untuk skrip PowerCLI

55 saat. Ia lebih baik, tetapi ia masih boleh menjadi lebih pantas. 

Mari beralih ke kelajuan kedua: GetView

Mari kita cari apa yang salah.
Pertama sekali, cmdlet Get-VM mengambil masa yang lama untuk dilaksanakan.
Kedua, cmdlet Get-AdvancedOptions mengambil masa lebih lama untuk disiapkan.
Mari kita berurusan dengan yang kedua dahulu. 

Get-AdvancedOptions mudah untuk objek VM individu, tetapi sangat kekok apabila bekerja dengan banyak objek. Kita boleh mendapatkan maklumat yang sama daripada objek mesin maya itu sendiri (Get-VM). Ia hanya terkubur dengan baik dalam objek ExtensionData. Berbekalkan penapisan, kami mempercepatkan proses mendapatkan data yang diperlukan.

Dengan pergerakan tangan yang sedikit ini 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}}

Output adalah sama seperti Get-AdvancedOptions, tetapi ia berfungsi berkali-kali lebih pantas. 

Sekarang untuk Dapatkan-VM. Ia tidak pantas kerana ia berkaitan dengan objek yang kompleks. Timbul persoalan logik: mengapa kita memerlukan maklumat tambahan dan PSObject yang besar dalam kes ini, sedangkan kita hanya memerlukan nama VM, keadaannya dan nilai atribut rumit?  

Selain itu, halangan dalam bentuk Get-AdvancedOptions telah dialih keluar daripada skrip. Menggunakan Runspace Pools kini kelihatan seperti berlebihan kerana tidak ada lagi keperluan untuk menyelaraskan tugas perlahan merentasi benang jongkong apabila menyerahkan sesi. Alat ini bagus, tetapi bukan untuk kes ini. 

Mari kita lihat output ExtensionData: ia tidak lebih daripada objek Get-View. 

Mari kita bincangkan teknik purba ahli PowerShell: satu baris menggunakan penapis, pengisihan dan pengelompokan. Semua kengerian sebelumnya diruntuhkan dengan elegan menjadi satu baris dan dilaksanakan 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 masa:

Bagaimana untuk membina penggalak roket untuk skrip PowerCLI

9 saat untuk hampir 10k objek dengan penapisan mengikut keadaan yang dikehendaki. Hebat!

Daripada kesimpulan

Keputusan yang boleh diterima secara langsung bergantung pada pilihan alat. Selalunya sukar untuk mengatakan dengan pasti apa sebenarnya yang harus dipilih untuk mencapainya. Setiap kaedah yang disenaraikan untuk mempercepatkan skrip adalah baik dalam had kebolehgunaannya. Saya harap artikel ini akan membantu anda dalam tugas sukar untuk memahami asas automasi proses dan pengoptimuman dalam infrastruktur anda.

PS: Penulis mengucapkan terima kasih kepada semua ahli komuniti atas bantuan dan sokongan mereka dalam menyediakan artikel. Malah mereka yang mempunyai kaki. Dan juga mereka yang tidak mempunyai kaki, seperti boa constrictor.

Sumber: www.habr.com

Tambah komen