天天看點

PowerCLI 腳本性能優化

還是決定寫一下這個話題,Powershell 非我的強項,本文大部分内容也來自于其他部落格,就當是知識收藏。

大概10月份左右去做一個項目,看到以前同僚留下的資料中包含很多

PowerCLI

的腳本,有去批量做配置的,有批量收集資訊的,于是花時間整理了一套适合自己的,但總有一個問題不能很好地解決:Powershell

腳本是順序執行的,面對大批量的資料收集速度很慢,通常會耗費 1 小時以上才能收集完資訊,于是花了些時間嘗試性能調優,将優化經驗整理發出來。

0x00. 關于 PowerCLI 及 Powershell

Powercli

是 VMware 出品的一系列 Microsoft Powershell 子產品,通過這些子產品,用于可以非常快捷的利用 Powershell 對

vCenter、NSX、SRM 等資源進行批量操作,例如批量修改 ESXI 主機的配置、列舉虛拟機清單等等。國外的 vSphere 管理者用

PowerCLI 非常多,社群中積累了大量的 PowerCLI 腳本,隻要能想到的操作都能找到相關資源。

0x01. 優化原則

目前我使用過的優化方式有三種:

1、優化代碼,提升單行單碼的運作效率

2、異步執行指令

3、多線程

以下章節逐一講解每個部分

0x02. 優化代碼

檢視瓶頸在哪裡

Powershell 有個指令叫 Measure-Command ,通過此指令可以檢視一段代碼的運作時間,其使用方法如下:

Measure-Command{ 指令内容 }      

例如,測試 Get-Process 的運作時間:

PowerCLI 腳本性能優化

将過濾左移

一個通用的優化方式如下:假如你要先收集大量的資料,應當盡早對資料進行過濾,以縮減這部分資料的大小,再進行其他二次過濾。或者按照原作者的話,将過濾往代碼的左邊移動。

例如:

想要通過 get-process 擷取 svchost 這個程序的 CPU 資訊,可以通過以下兩組代碼實作:

Get-Process -Name 'Dock' | Where-Object {$_.CPU -ne $null} | Select-Object CPU      
Get-Process | Where-Object {$_.ProcessName -eq 'Dock'} | Select-Object CPU | Where-Object {$_.CPU -ne $null}      

性能對比如下:

PowerCLI 腳本性能優化

可以看到前者運作速度比後者要快一倍多,如果是執行大量的任務,這一點點差距累計起來的差距是很大的。

PowerCLI 代碼性能優化

筆者對 PowerCLI 的腳本進行了類似測試發現随着寫法不同,代碼執行速度差異很大,如下例:擷取主機所在叢集資訊:

Get-VMHost $vmHost | Get-cluster      
Get-Cluster -vmhost $vmHost      
PowerCLI 腳本性能優化

又如對 ESXi 本地盤數量的統計:

雖然 esxcli 看似步驟更多,代碼更多,但使用 esxcli 的效率明顯高于 Get-scsilun 的效率:

$ESXCLI = Get-EsxCli -VMHost $VMHost $luncount=$ESXCLI.storage.core.device.list() | select DeviceType | where DeviceType -eq "Direct-Access"write-host "Count is" $luncount.Count -ForegroundColor Green      
$luncount=get-vmhost $VMhost| Get-ScsiLun -LunType disk write-host "Count is" $luncount.Count -ForegroundColor Green      
PowerCLI 腳本性能優化

遇到大量代碼時,需要反複測試不同寫法帶來的速度差異,選擇最優的。

指派的效率遠遠高于執行一個查詢

另外測試發現,所有指派的操作速度都低于 1ms,是以這類操作并不影響性能。具體到代碼優化上,應該盡可能減少與 vCenter 的互動。例如擷取主機所在叢集:

Get-VMHost $vmHost|Get-cluster      
Get-Cluster -vmhost $vmHost      
$info.cluster=$vmhost.Parent.name      

直接的指派遠比執行一條查詢快。三條指令執行速度分别如下:

PowerCLI 腳本性能優化

0x03. 異步任務

PowerCLI 有相當一部分操作可以使用 ​

​-RunAsync​

​ 參數,添加此參數後,此指令在執行後隻通過 vCenter 建立任務,并不監控任務執行情況。

在執行某些批量任務的時候,例如虛拟機開關機、虛拟機建立會非常有用。

0x04. 使用多線程

Powershell 有多種方式來建立多線程任務,時間原因我隻測試使用了 PSJob ,除此之外還有 Runspace 和 RSJob ,有興趣的可以自行研究。

PSJob 使用條件

如果要使用 PSJob 建立多線程任務,前提條件有:

  • Powershell 3.0 以上版本
  • 主控端的記憶體和CPU足夠多
  • 確定程式可以多線程運作

注意:在進行多線程改造前,需要確定多線程運作時,多個線程間不會沖突。例如多個程序同時寫入檔案便會沖突,但是同時讀取一個檔案就沒問題。

PSJobs 的 cmdlet 内置于 Microsoft.PowerShell.Core 中,可以使用下列指令檢視此 cmdlet 下的指令:

Get-Command *-Job      
PowerCLI 腳本性能優化

PSJobs 共有 8 種狀态,最常用的如下:

  • Completed :Job 已經完成,可以擷取 job 輸出的資訊,或者 job 可以被安全移除。
  • Running:Job 正在運作,無法停止(除非強制停止),無法擷取輸出資訊。
  • Blocked:Job 正在運作,但是系統提示要求輸入資訊才能繼續。
  • Failed:執行 Job 時出錯。

start-job 開始任務

使用 ​

​start-job​

​可以建立一個新的任務,如果使用 measure-command 檢視任務執行時間,可以看到遠遠小于指令真實的執行時間。

PowerCLI 腳本性能優化

get-job 擷取 job 狀态

通過 ​

​get-job​

​指令可以檢視 Job 的運作情況。

PS51> Start-Job -Scriptblock {Start-Sleep 5}
PS51> Get-Job      
PowerCLI 腳本性能優化
PowerCLI 腳本性能優化

上例中,如果任務還在運作,則 State 顯示 Running,如果運作完畢,State 為 Completed。

另外在運作完後 HasMoreData 值為 False,表示此 Job 沒有輸出。

下圖是其他狀态的截圖,使用的指令見 Command 列。

PowerCLI 腳本性能優化

receive-job 擷取任務輸出

通過 ​

receive-job

​ 指令可以檢視 Job 的輸出資訊(使用  ​

write-output

​ 輸出),注意一旦輸出後 Job 中就不會再有這些資訊了,是以建議等 Job 完成後再完整收集資訊。

$Job = Start-Job -ScriptBlock {Write-Output 'Hello World'}
Receive-Job $Job      
PowerCLI 腳本性能優化

參數傳遞

預設 start-job 生産的任務無法使用全局定義的變量,需要 ArgumentList 進行變量的傳遞。

Start-Job -Scriptblock {param ($Text) Write-Output $Text} -ArgumentList "Hello world!"      

任務執行完畢後的操作

在做一些批量收集的時候,我們通過 ​

​start-job​

​ 來建立了大量的 Job,但這隻是下發了任務,我們還需要在任務執行完畢後讀取并處理資料,在此我使用 for 循環定期檢查 Job 是不是已經完成,隻有等待完成後才批量讀取資料,具體操作如下:

$tasklist=@(
 "task1"
 "task2"
 "task3"
 )
 
 $jobs=@()
  Foreach ($task in $tasklist) {
  write-host "Starting Job..."
  $jobs += start-job -ScriptBlock {
  param ($taskname)
    sleep 5
    Write-Output "Task Finished : "$taskname
    } -ArgumentList $task}while($jobs.state -contains "Running")
  {write-host "still running!" -ForegroundColor Yellow
  sleep 1
  }  
 
write-host "Finished!" -ForegroundColor Greenreceive-job $jobs      

輸出優化

上面提到可以在 job 内使用 ​

​Write-Output ​

​輸出資訊 ,job 外使用​

​receive-job​

​ 來接受資訊,

在實際測試時發現 ​

​Write-Output​

​對于一個比較大的變量效率非常低(例如我嘗試輸出 Get-vmhost 的結果,幾十分鐘沒有反應),低到懷疑人生,是以最佳方法是在 Job 内完成該有的過濾。

例如:

Measure-Command{
  write-host "Starting Job..."
  $job = start-job -ScriptBlock { $debuginfo=Connect-VIServer -Server "192.168.1.1" -User '[email protected]' -Password 'VMware1!' -WarningAction Ignore $vmHost =get-vmhost "esx06.vsphere.local" Write-Output $vmHost Disconnect-VIServer -Server "192.168.1.1" -Confirm:$false}while($job.state -contains "Running") {  write-host "still running!" -ForegroundColor Yellow  sleep 1
 }  
write-host "Finished!" -ForegroundColor Greenreceive-job $job}      

Measure-Command{  $job = start-job -ScriptBlock  {  $debuginfo=Connect-VIServer -Server "192.168.1.1" -User '[email protected]' -Password 'VMware1!' -WarningAction Ignore  $vmHost =get-vmhost "esx06.vsphere.local"| select name,Version  Write-Output $vmHost  Disconnect-VIServer -Server "192.168.1.1" -Confirm:$false } while($job.state -contains "Running")  {   write-host "still running!" -ForegroundColor Yellow   sleep 1  }  write-host "Finished!" -ForegroundColor Greenreceive-job $job}      

後面的指令隻是在輸出時增加了 ​

​select name,Version​

​ ,執行時間隻有 7 秒,而前面的程式幾十分鐘了還沒反應。

PSJob 應用于 PowerCLI

在應用于 PowerCLI 時,發現每個 job 都必須單獨連接配接 vCenter,好在這個操作可以将密碼寫入代碼,無需互動:

$debuginfo=Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false -Confirm:$false      
$debuginfo=Connect-VIServer -Server "192.168.1.1" -User '[email protected]' -Password 'VMware1!' -WarningAction Ignore      

在通過以上各種手段處理後,原本收集100台主機的資訊需要十幾分鐘,現在縮減到了兩分多鐘:

PowerCLI 腳本性能優化

不過對應的,電腦風扇開始狂響。。

PowerCLI 腳本性能優化

參考資料:

繼續閱讀