PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

Er ya da geç, herhangi bir VMware sistem yöneticisi rutin görevleri otomatikleştirmeye gelir. Her şey komut satırıyla başlar, ardından PowerShell veya VMware PowerCLI gelir.

Diyelim ki PowerShell'de, ISE'yi başlatmaktan ve "bir tür sihir" nedeniyle çalışan modüllerden standart cmdlet'leri kullanmaktan biraz daha ileri düzeyde uzmanlaştınız. Yüzlerce sanal makineyi saymaya başladığınızda, küçük ölçekte yardımcı olan komut dosyalarının büyük ölçekte gözle görülür şekilde daha yavaş çalıştığını göreceksiniz. 

Bu durumda 2 araç yardımcı olacaktır:

  • PowerShell Çalışma Alanları – süreçlerin ayrı iş parçacıklarında yürütülmesini paralelleştirmenize olanak tanıyan bir yaklaşım; 
  • Get-View – PowerCLI'da Get-WMIObject'e benzer temel bir fonksiyon. WindowsBu cmdlet, ilgili varlıkları içeri aktarmaz, bunun yerine basit veri türlerine sahip basit bir nesne olarak bilgi alır. Bu, birçok durumda daha hızlıdır.

Daha sonra her araçtan kısaca bahsedeceğim ve kullanım örneklerini göstereceğim. Belirli komut dosyalarını analiz edelim ve birinin diğerinden daha iyi çalıştığını görelim. Gitmek!

PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

İlk aşama: Runspace

Runspace, ana modül dışındaki görevlerin paralel işlenmesi için tasarlanmıştır. Tabii ki, bir miktar hafıza, işlemci vb. tüketecek başka bir işlem başlatabilirsiniz. Komut dosyanız birkaç dakika içinde çalışırsa ve bir gigabayt hafıza tüketiyorsa, büyük olasılıkla Runspace'e ihtiyacınız olmayacaktır. Ancak on binlerce nesneye yönelik komut dosyaları için buna ihtiyaç vardır.

Öğrenmeye buradan başlayabilirsiniz: 
PowerShell Çalışma Alanlarını Kullanmaya Başlama: Bölüm 1

Runspace'i kullanmak ne sağlar:

  • yürütülen komutların listesini sınırlayarak hız,
  • görevlerin paralel yürütülmesi,
  • güvenlik.

Runspace'in yardımcı olduğu İnternet'ten bir örnek:

“Depolama çekişmesi vSphere'de izlenmesi en zor ölçümlerden biri. VCenter'ın içinde hangi VM'nin daha fazla depolama kaynağı tükettiğini göremezsiniz. Neyse ki PowerShell sayesinde bu verileri dakikalar içinde toplayabilirsiniz.
VMware sistem yöneticilerinin vCenter'da hızlı bir şekilde arama yapmasına ve ortalama tüketimlerine ilişkin verileri içeren VM'lerin bir listesini almasına olanak sağlayacak bir komut dosyası paylaşacağım.  
Betik, her ESXi ana bilgisayarının kendi VM'lerinden tüketim bilgilerini ayrı bir Çalışma Alanında toplamasına ve tamamlandığını anında raporlamasına olanak sağlamak için PowerShell çalışma alanlarını kullanır. Bu, ana bilgisayarlar arasında yineleme yapmak ve her birinin isteğini tamamlamasını beklemek yerine PowerShell'in işleri hemen kapatmasına olanak tanıyor."

Kaynak: ESXi Kontrol Panelinde Sanal Makine Giriş/Çıkışını Gösterme

Aşağıdaki durumda Runspace artık kullanışlı değildir:

“Bir VM'den çok fazla veri toplayan ve gerektiğinde yeni veriler yazan bir komut dosyası yazmaya çalışıyorum. Sorun şu ki, çok fazla VM var ve bir makinede 5-8 saniye harcanıyor." 

Kaynak: RunspacePool ile çok iş parçacıklı PowerCLI

Burada Get-View'a ihtiyacınız olacak, hadi ona geçelim. 

İkinci aşama: Get-View

Get-View'in neden faydalı olduğunu anlamak için cmdlet'lerin genel olarak nasıl çalıştığını hatırlamakta fayda var. 

API referans kitaplarını incelemeye ve bir sonraki tekerleği yeniden icat etmeye gerek kalmadan bilgileri rahatlıkla elde etmek için cmdlet'lere ihtiyaç vardır. Eskiden yüz veya iki satır kod gerektiren işi PowerShell tek komutla yapmanıza olanak sağlar. Bu rahatlığın bedelini hızla ödüyoruz. Cmdlet'lerin içinde hiçbir sihir yoktur: aynı senaryo, ancak daha düşük düzeyde, güneşli Hindistan'dan bir ustanın yetenekli elleri tarafından yazılmıştır.

Şimdi Get-View ile karşılaştırma yapmak için Get-VM cmdlet'ini ele alalım: sanal makineye erişir ve bileşik bir nesne döndürür, yani ona diğer ilgili nesneleri ekler: VMHost, Datastore, vb.  

Get-View kullanımı, döndürülen nesneye fazladan bir şey eklemez. Dahası, tam olarak hangi bilgilere ihtiyacımız olduğunu kesin olarak belirtmemize olanak tanır, bu da çıktı nesnesini basitleştirir. Windows Server Genel olarak ve özellikle Hyper-V'de, Get-WMIObject cmdlet'i doğrudan bir analogdur; fikir tamamen aynıdır.

Get-View, nokta nesneler üzerindeki rutin işlemler için uygun değildir. Ancak binlerce ve onbinlerce nesne söz konusu olduğunda bunun hiçbir bedeli yoktur.

Daha fazlasını VMware blogunda okuyabilirsiniz: Get-View'a Giriş

Şimdi size her şeyi gerçek bir vaka kullanarak göstereceğim. 

VM'yi boşaltmak için komut dosyası yazma

Bir gün meslektaşım benden senaryosunu optimize etmemi istedi. Görev ortak bir rutindir: yinelenen cloud.uuid parametresine sahip tüm VM'leri bulun (evet, bu, vCloud Director'da bir VM klonlarken mümkündür). 

Aklıma gelen bariz çözüm şu:

  1. Tüm VM'lerin bir listesini alın.
  2. Listeyi bir şekilde ayrıştırın.

Orijinal versiyon şu basit komut dosyasıydı:

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

Her şey son derece basit ve net. Bir kahve molasıyla birkaç dakikada yazılabilir. Filtrelemeyi vidalayın ve bitti.

Ama zamanı ölçelim:

PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

2 dakika 47 saniye neredeyse 10 bin VM'yi işlerken. Bonus, filtrelerin olmaması ve sonuçları manuel olarak sıralama ihtiyacıdır. Açıkçası, komut dosyası optimizasyon gerektiriyor.

vCenter'dan aynı anda ana bilgisayar ölçümlerini almanız veya on binlerce nesneyi işlemeniz gerektiğinde kurtarmaya ilk gelenler çalışma alanlarıdır. Bakalım bu yaklaşım neler getirecek?

İlk hızı açın: PowerShell Runspaces

Bu script için akla gelen ilk şey döngüyü sıralı olarak değil paralel iş parçacıkları halinde yürütmek, tüm verileri tek bir nesnede toplayıp filtrelemektir. 

Ancak bir sorun var: PowerCLI, vCenter'da çok sayıda bağımsız oturum açmamıza izin vermiyor ve komik bir hata veriyor:

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.

Bunu çözmek için öncelikle oturum bilgilerini akışın içine aktarmalısınız. PowerShell'in bir fonksiyona veya bir ScriptBlock'a parametre olarak iletilebilen nesnelerle çalıştığını hatırlayalım. $global:DefaultVIServers'ı (Connect-VIServer'ı -NotDefault anahtarıyla) atlayarak oturumu böyle bir nesne biçiminde geçirelim:

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

Şimdi Runspace Havuzları aracılığıyla çoklu iş parçacıklarını uygulayalım.  

Algoritma aşağıdaki gibidir:

  1. Tüm VM'lerin bir listesini alıyoruz.
  2. Paralel akışlarda cloud.uuid'i elde ederiz.
  3. Akışlardan verileri tek bir nesnede topluyoruz.
  4. Nesneyi CloudUUID alanının değerine göre gruplandırarak filtreliyoruz: benzersiz değer sayısının 1'den büyük olduğu VM'ler aradığımız VM'lerdir.

Sonuç olarak, komut dosyasını alıyoruz:


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
}

Bu betiğin güzelliği, diğer benzer durumlarda sadece ScriptBlock'u ve akışa aktarılacak parametreleri değiştirerek kullanılabilmesidir. İstismar edin!

Zamanı ölçüyoruz:

PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

55 saniye. Daha iyi ama yine de daha hızlı olabilir. 

İkinci hıza geçelim: GetView

Neyin yanlış olduğunu bulalım.
Her şeyden önce Get-VM cmdlet'inin yürütülmesi uzun zaman alır.
İkincisi, Get-AdvancedOptions cmdlet'inin tamamlanması daha da uzun sürüyor.
Önce ikincisini ele alalım. 

Get-AdvancedOptions, bireysel VM nesneleri için uygundur, ancak birçok nesneyle çalışırken çok kullanışsızdır. Aynı bilgiyi sanal makine nesnesinin kendisinden (Get-VM) de alabiliriz. ExtensionData nesnesine iyice gömülmüş durumda. Filtrelemeyle donatılmış olarak gerekli verileri elde etme sürecini hızlandırıyoruz.

Hafif bir el hareketi ile bu:


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

Şuna dönüşür:


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

Çıktı Get-AdvancedOptions ile aynıdır, ancak birçok kez daha hızlı çalışır. 

Şimdi Get-VM'ye geçelim. Karmaşık nesnelerle uğraştığı için hızlı değildir. Mantıksal bir soru ortaya çıkıyor: Sadece VM'nin adına, durumuna ve zor bir özelliğin değerine ihtiyacımız varken, bu durumda neden ekstra bilgiye ve korkunç bir PSObject'e ihtiyacımız var?  

Ayrıca Get-AdvancedOptions şeklindeki engel de scriptten kaldırıldı. Runspace Pools'u kullanmak artık aşırıya kaçıyor gibi görünüyor çünkü artık bir oturumu teslim ederken yavaş bir görevi squat iş parçacıkları arasında paralelleştirmeye gerek yok. Araç iyi, ancak bu durum için değil. 

ExtensionData'nın çıktısına bakalım: Bu bir Get-View nesnesinden başka bir şey değil. 

PowerShell ustalarının eski tekniğine başvuralım: filtreleri kullanan tek satır, sıralama ve gruplama. Önceki tüm korkular zarif bir şekilde tek bir satırda toplanmış ve tek bir oturumda yürütülmüştür:


$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

Zamanı ölçüyoruz:

PowerCLI komut dosyaları için roket güçlendirici nasıl oluşturulur 

9 saniye İstenilen koşula göre filtrelemeyle neredeyse 10 bin nesne için. Harika!

Bunun yerine bir sonuca

Kabul edilebilir bir sonuç doğrudan alet seçimine bağlıdır. Bunu başarmak için tam olarak neyin seçilmesi gerektiğini kesin olarak söylemek genellikle zordur. Komut dosyalarını hızlandırmak için listelenen yöntemlerin her biri, uygulanabilirliği sınırları dahilinde iyidir. Bu makalenin, altyapınızdaki süreç otomasyonu ve optimizasyonunun temellerini anlama gibi zor bir görevde size yardımcı olacağını umuyorum.

Not: Yazar, makalenin hazırlanmasındaki yardımları ve destekleri için tüm topluluk üyelerine teşekkür eder. Pençeleri olanlar bile. Ve boa yılanı gibi bacakları olmayanlar bile.

Kaynak: habr.com

DDoS korumalı siteler, VPS VDS sunucuları için güvenilir hosting satın alın 🔥 DDoS korumalı, güvenilir VPS ve VDS sunucu barındırma hizmeti satın alın | ProHoster