0
点赞
收藏
分享

微信扫一扫

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 脚本性能优化_VMware


将过滤左移

一个通用的优化方式如下:假如你要先收集大量的数据,应当尽早对数据进行过滤,以缩减这部分数据的大小,再进行其他二次过滤。或者按照原作者的话,将过滤往代码的左边移动。

例如:

想要通过 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_02

可以看到前者运行速度比后者要快一倍多,如果是执行大量的任务,这一点点差距累计起来的差距是很大的。

PowerCLI 代码性能优化

笔者对 PowerCLI 的脚本进行了类似测试发现随着写法不同,代码执行速度差异很大,如下例:获取主机所在集群信息:

Get-VMHost $vmHost | Get-cluster

Get-Cluster -vmhost $vmHost

PowerCLI 脚本性能优化_自动化脚本_03

又如对 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 脚本性能优化_PowerCLI_04

遇到大量代码时,需要反复测试不同写法带来的速度差异,选择最优的。

赋值的效率远远高于执行一个查询

另外测试发现,所有赋值的操作速度都低于 1ms,所以这类操作并不影响性能。具体到代码优化上,应该尽可能减少与 vCenter 的交互。例如获取主机所在集群:

Get-VMHost $vmHost|Get-cluster

Get-Cluster -vmhost $vmHost

$info.cluster=$vmhost.Parent.name

直接的赋值远比执行一条查询快。三条命令执行速度分别如下:

PowerCLI 脚本性能优化_虚拟化_05


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 脚本性能优化_VMware_06


PSJobs 共有 8 种状态,最常用的如下:

  • Completed :Job 已经完成,可以获取 job 输出的信息,或者 job 可以被安全移除。
  • Running:Job 正在运行,无法停止(除非强制停止),无法获取输出信息。
  • Blocked:Job 正在运行,但是系统提示要求输入信息才能继续。
  • Failed:执行 Job 时出错。


start-job 开始任务

使用 ​start-job​可以创建一个新的任务,如果使用 measure-command 查看任务执行时间,可以看到远远小于命令真实的执行时间。

PowerCLI 脚本性能优化_自动化脚本_07


get-job 获取 job 状态

通过 ​get-job​命令可以查看 Job 的运行情况。

PS51> Start-Job -Scriptblock {Start-Sleep 5}
PS51> Get-Job

PowerCLI 脚本性能优化_VMware_08

PowerCLI 脚本性能优化_Powershell_09

上例中,如果任务还在运行,则 State 显示 Running,如果运行完毕,State 为 Completed。

另外在运行完后 HasMoreData 值为 False,表示此 Job 没有输出。

下图是其他状态的截图,使用的命令见 Command 列。

PowerCLI 脚本性能优化_Powershell_10


receive-job 获取任务输出

通过 receive-job 命令可以查看 Job 的输出信息(使用  write-output 输出),注意一旦输出后 Job 中就不会再有这些信息了,因此建议等 Job 完成后再完整收集信息。

$Job = Start-Job -ScriptBlock {Write-Output 'Hello World'}
Receive-Job $Job

PowerCLI 脚本性能优化_VMware_11


参数传递

默认 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 'administrator@vsphere.local' -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 'administrator@vsphere.local' -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 'administrator@vsphere.local' -Password 'VMware1!' -WarningAction Ignore

在通过以上各种手段处理后,原本收集100台主机的信息需要十几分钟,现在缩减到了两分多钟:

PowerCLI 脚本性能优化_自动化脚本_12

不过对应的,电脑风扇开始狂响。。

PowerCLI 脚本性能优化_VMware_13


参考资料:

​https://adamtheautomator.com/powershell-multithreading/#runspace-vs-psjobs​

​https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_jobs?view=powershell-7​


举报

相关推荐

0 条评论