在 Powershell 中排队重启

在 Powershell 中排队重启

我正在做一个重启大量计算机的项目。其中一个重要要求是分阶段重启,这样所有机器就不会同时重启(太快会导致 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 太不稳定,无法使用。

相关内容