كيفية بناء مسرع صاروخي لنصوص PowerCLI 

عاجلاً أم آجلاً ، يتمكن أي مسؤول نظام VMware من أتمتة المهام الروتينية. يبدأ كل شيء من سطر الأوامر ، ثم يأتي PowerShell أو VMware PowerCLI.

افترض أنك أتقنت PowerShell أبعد قليلاً من بدء تشغيل ISE واستخدام cmdlets القياسية من الوحدات النمطية التي تعمل مع نوع من السحر. عندما تبدأ في عد المئات من الأجهزة الافتراضية ، ستجد أن البرامج النصية التي ساعدت على النطاقات الصغيرة تعمل بشكل أبطأ بشكل ملحوظ على الأجهزة الكبيرة. 

في هذه الحالة ، ستساعد أداتان:

  • PowerShell Runspaces - نهج يسمح لك بموازاة تنفيذ العمليات في سلاسل عمليات منفصلة ؛ 
  • Get-View هي إحدى وظائف PowerCLI الأساسية ، وهي نظير لـ Get-WMIObject على Windows. لا يقوم cmdlet هذا بسحب كائنات مرتبطة به ، ولكنه يتلقى معلومات في شكل كائن بسيط مع أنواع بيانات بسيطة. في كثير من الحالات ، يخرج بشكل أسرع.

بعد ذلك ، سأتحدث باختصار عن كل أداة وأعرض أمثلة على الاستخدام. سنقوم بتحليل نصوص محددة ونرى متى يعمل أحدها بشكل أفضل ، عندما يكون الثاني. اذهب!



المرحلة الأولى: Runspace


لذلك ، تم تصميم Runspace للمعالجة المتوازية للمهام خارج الوحدة الرئيسية. بالطبع ، يمكنك بدء عملية أخرى تستهلك بعض الذاكرة والمعالج ، وما إلى ذلك. إذا تم تشغيل النص البرمجي الخاص بك في غضون دقيقتين وقضاء جيجابايت من الذاكرة ، فربما لن تحتاج إلى Runspace. ولكن بالنسبة للنصوص الموجودة على عشرات الآلاف من الأشياء ، هناك حاجة إليها.
يمكنك البدء في التطوير من هنا: 
بدء استخدام PowerShell Runspaces: الجزء 1

ما الذي يستخدم Runspace:

  • السرعة بتحديد قائمة الأوامر القابلة للتنفيذ ،
  • المهام الموازية
  • سلامة.

هنا مثال من الإنترنت عندما يساعد Runspace:
« – , vSphere. vCenter , . , PowerShell.
, VMware vCenter .  
PowerShell runspaces, ESXi Runspace . PowerShell , , ».

: How to Show Virtual Machine I/O on an ESXi Dashboard

في الحالة أدناه ، لم يعد Runspace في العمل:
"أحاول كتابة نص برمجي يجمع الكثير من البيانات من الجهاز الظاهري ، وإذا لزم الأمر ، يكتب بيانات جديدة. المشكلة هي أن هناك الكثير من VMs ، ويستغرق الأمر 5-8 ثوانٍ لآلة واحدة. " 

المصدر: Multithreading PowerCLI مع RunspacePool

Get-View مطلوب هنا ، دعنا ننتقل إليه. 

المرحلة الثانية: 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 تناظريًا مباشرًا - الفكرة هي نفسها تمامًا.

Get-View غير مريح في العمليات الروتينية على ميزات النقاط. ولكن عندما يتعلق الأمر بالآلاف وعشرات الآلاف من الأشياء ، ليس لديه سعر.
اقرأ المزيد على مدونة VMware: مقدمة في Get-View

الآن سأعرض كل شيء في حالة حقيقية. 

نكتب سيناريو لتفريغ VM


ذات مرة طلب مني زميلي تحسين نصه. تعتبر المهمة روتينًا عاديًا: ابحث عن جميع الأجهزة الافتراضية ذات معلمة cloud.uuid مكررة (نعم ، هذا ممكن عند استنساخ VM في vCloud Director). 

الحل الواضح الذي يتبادر إلى الذهن:

  1. احصل على قائمة بجميع الأجهزة الافتراضية.
  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
}
#     

كل شيء بسيط للغاية وواضح. هو مكتوب في بضع دقائق مع استراحة قهوة. المسمار على المرشح ، ويتم ذلك.

ولكن قياس الوقت:





دقيقتان و 47 ثانية عند معالجة ما يقرب من 10 كيلو VM. المكافأة هي نقص الفلاتر والحاجة إلى فرز النتيجة يدويًا. من الواضح أن النص يطلب التحسين.

يأتي Ranspace لأول مرة عند الإنقاذ عندما تحتاج إلى الحصول على مقاييس المضيف باستخدام 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 key):

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

الخوارزمية هي على النحو التالي:

  1. احصل على قائمة بجميع الأجهزة الافتراضية.
  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 والمعلمات التي سيتم نقلها إلى الدفق. استغلالها!

نقيس الوقت:



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 ، ولكنه يعمل بشكل أسرع عدة مرات. 

الآن احصل على VM. لا يتم تنفيذها بسرعة ، لأنها تتعامل مع الأشياء المعقدة. ينشأ سؤال منطقي: لماذا في هذه الحالة نحتاج إلى معلومات إضافية و PSObject وحشية عندما نحتاج فقط إلى اسم VM وحالته وقيمة السمة الصعبة؟  

بالإضافة إلى ذلك ، تركت الفرامل في وجه Get-AdvancedOptions النص. يبدو أن استخدام Runspace Pools مبالغة الآن ، حيث لم تعد هناك حاجة لموازاة مهمة بطيئة في التدفقات مع القرفصاء عند نقل الجلسة. الأداة جيدة ، ولكن ليس لهذه الحالة. 

نحن ننظر إلى إخراج 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

نقيس الوقت:



9 ثوانٍ لما يقرب من 10 كائنات مع التصفية وفقًا للحالة المطلوبة. غرامة!

بدلاً من الاستنتاج

تعتمد النتيجة المقبولة بشكل مباشر على اختيار الأداة. غالبًا ما يكون من الصعب القول على وجه اليقين ما الذي يجب اختياره بالضبط لتحقيقه. كل طريقة من طرق تسريع البرنامج النصي جيدة في حدود قابلية تطبيقها. آمل أن تساعدك هذه المقالة في المهمة الصعبة المتمثلة في فهم أساسيات أتمتة العمليات وتحسينها في البنية التحتية الخاصة بك.

ملاحظة: المؤلف يشكر جميع أعضاء الكومونة لمساعدتهم ودعمهم في إعداد المقال. حتى مع الكفوف. وحتى الذين ليس لديهم أرجل ، مثل الأصلة العاصرة.

All Articles