Πώς να δημιουργήσετε έναν ενισχυτή πυραύλων για σενάρια PowerCLI 

Αργά ή γρήγορα, οποιοσδήποτε διαχειριστής συστήματος VMware έρχεται να αυτοματοποιήσει τις εργασίες ρουτίνας. Όλα ξεκινούν με τη γραμμή εντολών και μετά έρχεται το PowerShell ή το VMware PowerCLI.

Ας υποθέσουμε ότι έχετε κατακτήσει το PowerShell λίγο περισσότερο από την εκκίνηση του ISE και τη χρήση τυπικών cmdlet από μονάδες που λειτουργούν λόγω «κάποιου είδους μαγείας». Όταν αρχίσετε να μετράτε εικονικές μηχανές σε εκατοντάδες, θα διαπιστώσετε ότι τα σενάρια που βοηθούν σε μικρή κλίμακα τρέχουν αισθητά πιο αργά σε μεγάλη κλίμακα. 

Σε αυτήν την περίπτωση, 2 εργαλεία θα σας βοηθήσουν:

  • PowerShell Runspaces – μια προσέγγιση που σας επιτρέπει να παραλληλίσετε την εκτέλεση διεργασιών σε ξεχωριστά νήματα. 
  • Λήψη-Προβολή – μια βασική λειτουργία PowerCLI, ένα ανάλογο του Get-WMIObject στα Windows. Αυτό το cmdlet δεν τραβάει αντικείμενα που συνοδεύουν οντότητες, αλλά λαμβάνει πληροφορίες με τη μορφή απλού αντικειμένου με απλούς τύπους δεδομένων. Σε πολλές περιπτώσεις βγαίνει πιο γρήγορα.

Στη συνέχεια, θα μιλήσω εν συντομία για κάθε εργαλείο και θα δείξω παραδείγματα χρήσης. Ας αναλύσουμε συγκεκριμένα σενάρια και ας δούμε πότε το ένα λειτουργεί καλύτερα από το άλλο. Πηγαίνω!

Πώς να δημιουργήσετε έναν ενισχυτή πυραύλων για σενάρια PowerCLI

Πρώτο στάδιο: Runspace

Έτσι, το Runspace έχει σχεδιαστεί για παράλληλη επεξεργασία εργασιών εκτός της κύριας ενότητας. Φυσικά, μπορείτε να ξεκινήσετε μια άλλη διαδικασία που θα καταναλώσει λίγη μνήμη, επεξεργαστή κ.λπ. Εάν το σενάριό σας εκτελείται σε λίγα λεπτά και καταναλώνει ένα gigabyte μνήμης, πιθανότατα δεν θα χρειαστείτε το Runspace. Αλλά για σενάρια για δεκάδες χιλιάδες αντικείμενα χρειάζεται.

Μπορείτε να αρχίσετε να μαθαίνετε εδώ: 
Έναρξη χρήσης του PowerShell Runspaces: Μέρος 1

Τι δίνει η χρήση του Runspace:

  • ταχύτητα περιορίζοντας τη λίστα των εκτελεσμένων εντολών,
  • παράλληλη εκτέλεση εργασιών,
  • ασφάλεια.

Ακολουθεί ένα παράδειγμα από το Διαδίκτυο όταν το Runspace βοηθά:

«Η διαμάχη αποθήκευσης είναι μια από τις πιο δύσκολες μετρήσεις για παρακολούθηση στο vSphere. Μέσα στο vCenter, δεν μπορείτε απλώς να πάτε και να δείτε ποιο VM καταναλώνει περισσότερους πόρους αποθήκευσης. Ευτυχώς, μπορείτε να συλλέξετε αυτά τα δεδομένα μέσα σε λίγα λεπτά χάρη στο PowerShell.
Θα μοιραστώ ένα σενάριο που θα επιτρέπει στους διαχειριστές συστήματος VMware να πραγματοποιούν γρήγορη αναζήτηση σε όλο το vCenter και να λαμβάνουν μια λίστα εικονικών μηχανών με δεδομένα για τη μέση κατανάλωσή τους.  
Το σενάριο χρησιμοποιεί χώρους εκτέλεσης του PowerShell για να επιτρέπει σε κάθε κεντρικό υπολογιστή ESXi να συλλέγει πληροφορίες κατανάλωσης από τα δικά του VM σε ξεχωριστό Runspace και να αναφέρει αμέσως την ολοκλήρωση. Αυτό επιτρέπει στο PowerShell να κλείνει τις εργασίες αμέσως, αντί να επαναλαμβάνει μέσω των κεντρικών υπολογιστών και να περιμένει ο καθένας να ολοκληρώσει το αίτημά του."

Πηγή: Τρόπος εμφάνισης Virtual Machine I/O σε έναν πίνακα εργαλείων ESXi

Στην παρακάτω περίπτωση, το Runspace δεν είναι πλέον χρήσιμο:

«Προσπαθώ να γράψω ένα σενάριο που συλλέγει πολλά δεδομένα από ένα VM και γράφει νέα δεδομένα όταν χρειάζεται. Το πρόβλημα είναι ότι υπάρχουν πολλά VM και 5-8 δευτερόλεπτα ξοδεύονται σε ένα μηχάνημα.» 

Πηγή: Multithreading PowerCLI με RunspacePool

Εδώ θα χρειαστείτε Get-View, ας προχωρήσουμε σε αυτό. 

Δεύτερο στάδιο: Λήψη-Προβολή

Για να καταλάβετε γιατί το Get-View είναι χρήσιμο, αξίζει να θυμάστε πώς λειτουργούν τα cmdlet γενικά. 

Τα Cmdlet χρειάζονται για την εύκολη απόκτηση πληροφοριών χωρίς την ανάγκη μελέτης βιβλίων αναφοράς API και επανεφεύρεσης του επόμενου τροχού. Αυτό που παλιά χρειάζονταν εκατό ή δύο γραμμές κώδικα, το PowerShell σας επιτρέπει να το κάνετε με μία εντολή. Πληρώνουμε για αυτήν την ευκολία με ταχύτητα. Δεν υπάρχει καμία μαγεία μέσα στα ίδια τα cmdlet: το ίδιο σενάριο, αλλά σε χαμηλότερο επίπεδο, γραμμένο από τα επιδέξια χέρια ενός δασκάλου από την ηλιόλουστη Ινδία.

Τώρα, για σύγκριση με το Get-View, ας πάρουμε το cmdlet Get-VM: αποκτά πρόσβαση στην εικονική μηχανή και επιστρέφει ένα σύνθετο αντικείμενο, δηλαδή επισυνάπτει άλλα σχετικά αντικείμενα σε αυτό: VMHost, Datastore, κ.λπ.  

Το Get-View στη θέση του δεν προσθέτει τίποτα περιττό στο επιστρεφόμενο αντικείμενο. Επιπλέον, μας επιτρέπει να προσδιορίζουμε αυστηρά ποιες πληροφορίες χρειαζόμαστε, κάτι που θα κάνει το αντικείμενο εξόδου ευκολότερο. Στον Windows Server γενικά και στο Hyper-V ειδικότερα, το cmdlet Get-WMIObject είναι ένα άμεσο ανάλογο - η ιδέα είναι ακριβώς η ίδια.

Το Get-View δεν είναι βολικό για συνήθεις λειτουργίες σε σημειακά αντικείμενα. Αλλά όταν πρόκειται για χιλιάδες και δεκάδες χιλιάδες αντικείμενα, δεν έχει τιμή.

Μπορείτε να διαβάσετε περισσότερα στο ιστολόγιο 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 δευτερόλεπτα κατά την επεξεργασία σχεδόν 10 VM. Ένα μπόνους είναι η απουσία φίλτρων και η ανάγκη χειροκίνητης ταξινόμησης των αποτελεσμάτων. Προφανώς, το σενάριο απαιτεί βελτιστοποίηση.

Το 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 (Connect-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 $_
           }
       }
   }

Τώρα ας εφαρμόσουμε το multithreading μέσω του 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

Ας μάθουμε τι φταίει.
Πρώτα και κύρια, το cmdlet Get-VM χρειάζεται πολύ χρόνο για να εκτελεστεί.
Δεύτερον, το cmdlet Get-AdvancedOptions χρειάζεται ακόμη περισσότερο χρόνο για να ολοκληρωθεί.
Ας ασχοληθούμε πρώτα με το δεύτερο. 

Το 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, αλλά λειτουργεί πολλές φορές πιο γρήγορα. 

Τώρα στο Get-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: Ο συγγραφέας ευχαριστεί όλα τα μέλη της κοινότητας για τη βοήθεια και την υποστήριξή τους στην προετοιμασία του άρθρου. Ακόμα και αυτά με τα πόδια. Και ακόμη και εκείνοι που δεν έχουν πόδια, σαν βόα.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο