我正在做一个重启大量计算机的项目。其中一个重要要求是分阶段重启,这样所有机器就不会同时重启(太快会导致 SAN 出现问题)。
我尝试在工作流中通过限制 50 个并行操作并添加 15 秒延迟(每分钟 200 次重启)来执行此操作。
workflow Bounce-Computer {
param(
[string[]]$Computers
)
foreach -parallel -throttlelimit 50 ($computer in $Computers) {
Restart-Computer -PSComputerName $computer -Force
Start-Sleep -Seconds 15
}
}
但我遇到了一个问题:如果目标计算机上的 WMI 出现故障,工作流程就会挂起。
除了修复所有目标机器(有几千台)上的 WMI 之外,我该如何以可控的方式做这样的事情?工作?
答案1
我认为解决您的问题的方法不止一种,至少从我所看到的根源来看是这样。以下是我的做法:
Workflow Invoke-MassRestart{
Param
(
[parameter(mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]
$ComputerName,
[int]
$Throttle = 5,
[int]
$Delay = 5
)
Foreach -parallel -ThrottleLimit $Throttle ($Computer in $ComputerName){
Sequence {
InlineScript{
[void](Restart-Computer -PSComputerName $using:Computer)
}
Start-Sleep -Seconds $Delay
}
}
}
如果您希望我解释其中任何部分,请告诉我。我想调用我Restart-Computer
对 的强制[void]
转换。这基本上告诉系统发出命令并继续。我认为您在等待 50 个操作报告某种状态时遇到了麻烦。还请注意块内所需的唯一作用域InlineScript{}
。我还决定让它能够接受来自 的管道输入Get-ADComputer
。
答案2
无需过多修改脚本,您可以让脚本捕获 WMI 超时错误(如果它在挂起期间抛出可捕获的错误),然后重新启动下一台计算机。catch 语句可以输出问题计算机和错误,供您稍后查看。
Workflow Bounce-Computer {
param([string[]]$Computers)
foreach -parallel -throttlelimit 50 ($computer in $Computers) {
try{
Restart-Computer -PSComputerName $computer -Force -ErrorAction stop
Start-Sleep -Seconds 15
}
catch [AppropriateWMIExceptionError]
{
echo 'WMI timeout error' #Or another action to note the problem computer
}
}
答案3
我会改用作业引擎并执行如下操作。这尚未经过全面测试,但它应该可以工作,可能在某个地方遗漏了某些东西。
这样,您还可以让它们并行运行,并且如果一次重新启动挂起,则只有该作业需要时间,如果某项作业花费的时间太长,我们就会删除该作业。
代码如下:
function restart-myComputer (
[Parameter(ParameterSetName="restart", Mandatory=".")]
[Parameter(ParameterSetName="bg", Mandatory=".")]
[string]$computer,
[Parameter(ParameterSetName="restart")]
[int]$wait,
[Parameter(ParameterSetName="bg")]
[switch]$bg)
{
if ($bg) {
Start-Job -name $computer -ScriptBlock ([scriptblock]::create("Restart-Computer -PSComputerName $computer -Force -ErrorAction stop")) > $null
} else {
Restart-Computer -PSComputerName $computer -Force -ErrorAction stop
Start-Sleep -Seconds $wait
}
}
function remove-myJobs (
[Parameter(ParameterSetName="remove")]
[string[]]$names,
[Parameter(ParameterSetName="remove")]
[int]$maxRunTime = 30
{
$d = get-date
[string[]]$stillRunningJobArr = @()
foreach ($name in $names) {
$job = get-job -name $name
$diffTime = $d - $job.PSBeginTime
if($diffTime.TotalSeconds -gt $maxRunTime) {
remove-job -name $name -Force
write-host "Removed job $name that ran longer then $maxRunTime seconds"
} else {
write-host "Job $name has still not been running for more then $maxRunTime"
$stillRunningJobArr += $name
}
}
return $stillRunningJobArr
}
function remove-allMyJobs (
[Parameter(ParameterSetName="remove")]
[int]$maxRunTime = 30)
{
$d = get-date
foreach ($job in get-job) {
$diffTime = $d - $job.PSBeginTime
$name = $job.name
if($diffTime.TotalSeconds -gt $maxRunTime) {
remove-job -name $name -Force
write-host "Removed job $name that ran longer then $maxRunTime seconds"
} else {
write-host "Job $name has still not been running for more then $maxRunTime"
}
}
}
function restart-myComputers (
[Parameter(ParameterSetName="restart", Mandatory=".")]
[Parameter(ParameterSetName="bg", Mandatory=".")]
[string[]]$computers,
[Parameter(ParameterSetName="bg", Mandatory=".")]
[int]$maxConcurrentJobs,
[Parameter(ParameterSetName="bg", Mandatory=".")]
[Parameter(ParameterSetName="restart")]
[int]$wait,
[Parameter(ParameterSetName="bg")]
[switch]$bg)
{
[string[]]$restartedComputerArr = @()
if ($bg) {
foreach ($computer in $computers) {
if((get-job -state 'Running').Count -gt $maxConcurrentJobs) {
$restartedComputerArr = stop-myJobs -$restartedComputerArr -maxRunTime $maxRunTime
sleep $wait # Wait to get as many jobs to complete as possible.
}
Restart-myComputer -computer $computer -bg
$restartedComputerArr += $computer
}
} else {
Restart-myComputer -computer $computer -wait $wait
}
remove-allMyJobs
}
答案4
我最终重写了脚本以使用 VIC。此环境中的 WMI 太不稳定,无法使用。