клонирование нескольких виртуальных машин параллельно с многопоточным PowerCLI

Я взял на себя задачу клонирования множества виртуальных машин в VMware. Вместо того, чтобы сотни раз щелкать мастер клонирования, я хотел бы максимально автоматизировать это.

У меня уже настроена и "запечатана" машина-шаблон. После того, как он будет клонирован и включен, вновь клонированный экземпляр загрузится, постоит там некоторое время, пока выполняется системная подготовка и т. д. Это занимает около 20 минут или около того.

Я нашел очень хороший скрипт на MSFT TechNet, который делает все, что мне нужно, чтобы сделать. Я немного изменил его, поэтому мне не нужно менять значение и повторно сохранять сценарий для каждого шага процесса. Вместо счетчика $Step во всем скрипте я просто заменил его некоторыми задержками Start-Sleep. На данный момент он работает нормально и успешно клонирует все машины, которые он считывает из CSV-файла. Каждой машине требуется около 35 минут, прежде чем она будет готова к работе (машина перемещается в другое подразделение AD).

Единственная проблема заключается в том, что он работает последовательно, ожидая завершения всего процесса (клонирование, изменение VLAN, загрузка машины и ожидание присоединения к домену, а также перемещение конечного объекта машины в другое подразделение AD) перед запуском другого клона.

Что бы я действительно хотел, так это многопоточность, чтобы ускорить всю операцию. При тестировании я обнаружил, что клонирование в vSphere начинает замедляться, как только запущено около пяти отдельных заданий клонирования, поэтому я хотел бы изменить этот сценарий, чтобы запускать четыре клонирования (выполняя весь рабочий процесс) одновременно.

Любые идеи? Я могу вставить код, если нужно.


person brian    schedule 03.05.2012    source источник


Ответы (2)


Вы можете максимально клонировать 8 виртуальных машин параллельно из одного шаблона. Если вы запустите new-vm с параметром -RunAsync. С -RunAsync Команда возвращается немедленно, и выходные данные состоят из одного или нескольких объектов Task.

если вы хотите клонировать несколько виртуальных машин. Следующее должно помочь. просто зациклить.

Write-host "Deploying VM " -ForegroundColor Green -NoNewline; Write-Host $vmname -ForegroundColor Yellow
get-OScustomizationspec $cs | get-OScustomizationNicMapping | set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $vm.IP -SubnetMask $vm.subnet -DefaultGateway $vm.gateway -Dns $vm.Dns1, $vm.Dns2

$vms = New-VM -Name $vm.Name -Location $vm.cluster -VMhost $vm.vmhost -Template $vm.template -Datastore $vm.datastore -OSCustomizationSpec $cs -confirm:$false **-RunAsync**

if ($vm1.error) {
    Write-Host "Error in deploying $vmname" -ForegroundColor Red
}
person Amar Varude    schedule 26.06.2012

Ниже приведен сценарий, который я написал за последние пару дней и который развертывает шаблон виртуальной машины примерно на 650 наших серверах. Он развертывает его с 3 разных хостов виртуальных машин в разных центрах обработки данных по всей стране. Он одновременно развертывает 5 из Ирвинга, 15 из Плано и 15 из Атланты. Он работает с 19:00 до 6:00 с понедельника по пятницу и весь день в субботу и воскресенье. Он завершается, если он запускается в любое другое время.

У меня не хватило времени, когда я пытался выяснить, как передать более одного параметра функции при вызове этой функции с помощью Start-Job, поэтому вместо этого я просто создал отдельную функцию для трех местоположений.

С помощью этого скрипта мы смогли развернуть новый образ RDC в более чем 650 местах по ночам и в выходные дни примерно за неделю.

param( [Parameter(ValueFromPipelineByPropertyName=$true)] [string]$InputFile = $null )

Get-Module -ListAvailable VMware* | Import-Module | Out-Null
Import-Module ActiveDirectory

$Global:CompletedHosts = @()

$MAXVMHostCount = 50
$IrvingMaxJobs = 6
$PlanoMaxJobs = 15
$AtlantaMaxJobs = 15

Function Add-VMHosts() {
    param(
        [Parameter(Mandatory=$true)][int]$MAXVMHostCount,
        [Parameter(Mandatory=$false)][string]$InputFile
    )

    $AllVMHosts = @()

    If ($InputFile) {
        $AllVMHosts = Get-Content $InputFile
    }
    Else {
        $Jobs = (Get-Job).Name
        If ($Jobs -ne $null) {
            $Jobs = $Jobs.Trim("-TXINTATL")
        }

        ForEach ($Server in (Get-ADComputer -Server *************** -SearchBase "OU=************************" -Filter { Name -like "**********" })) {
            If ($Server.Name.Substring(10,1) -eq "0") {
                $IP = "10." + $Server.Name.Substring(11,1) + "."
            }
            Else {
                $IP = "10." + $Server.Name.Substring(10,2) + "."
            }
            If ($Server.Name.Substring(12,2) -eq "00") {
                $IP += "100"
            }
            ElseIf ($Server.Name.Substring(12,1) -eq "0") {
                $IP += $Server.Name.Substring(13,1)
            }
            Else {
                $IP += $Server.Name.Substring(12,2)
            }
            $IP += ".252"

            If ($IP -notin $Global:CompletedHosts -and $IP -notin $Jobs) {
                $AllVMHosts = $AllVMHosts + $IP
            }
        }
    }

    Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null

    $CurrentVMHostCount = (Get-VMHost -Location (Get-Datacenter "Profit Centers")).Count

    $HostCount = 0
    ForEach ($VMHost in $AllVMHosts) {
        If ($HostCount -ge ($MaxVMHostCount - $CurrentVMHostCount)) {
            Break
        }
        Else {
            $AddFailed = $false
            $ConnectFailed = $false
            $Test = $null

            $Test = Get-VMHost $VMHost -ErrorAction SilentlyContinue
            If (!$Test -and (Test-Connection $VMHost -Quiet)) {
                Try {
                    Connect-VIServer $VMHost -User "********************" -Password "********************" -WarningAction SilentlyContinue | Out-Null
                }
                Catch {
                    $ConnectFailed = $true
                }

                If (!$ConnectFailed -and (Get-VMHost $VMHost -ErrorAction SilentlyContinue | Get-VM -ErrorAction SilentlyContinue | Where { $_.Name -like "*********************" }) -eq $null -and (Test-Connection $VMHost -Quiet)) {
                    Set-VMHost -VMHost $VMHost -LicenseKey "********************" | Out-Null
                    Disconnect-VIServer -Server $VMHost -Confirm:$false | Out-Null

                    Add-VMHost $VMHost -Location (Get-DataCenter -Name "Profit Centers") -User "********************" -Password "********************" -Force:$true -ErrorAction SilentlyContinue | Out-Null
                    Start-Sleep -Seconds 5
                    Write-Host "$VMHost added to vCenter successfully"

                    $myVMHost = Get-VMHost $VMHost
                    $myVMHost | Get-VirtualPortGroup | Get-NicTeamingPolicy | Set-NicTeamingPolicy -InheritLoadBalancingPolicy $true -InheritNetworkFailoverDetectionPolicy $true -InheritNotifySwitches $true -InheritFailback $true -InheritFailoverOrder $true -WarningAction SilentlyContinue | Out-Null
                    $myVMHost | Get-VirtualPortGroup | Get-SecurityPolicy | Set-SecurityPolicy -AllowPromiscuousInherited $true -ForgedTransmitsInherited $true -MacChangesInherited $true | Out-Null

                    ForEach ($PortGroup in (Get-VirtualPortGroup -VMHost $VMHost)) {
                        $netSys = Get-View (Get-VMHost -Name $VMHost).ExtensionData.ConfigManager.NetworkSystem
                        $spec = (Get-VirtualPortGroup -Name $PortGroup.Name -VMHost $VMHost).ExtensionData.Spec
                        $spec.Policy.ShapingPolicy.Enabled = $null
                        $netSys.UpdatePortgroup($PortGroup.Name,$spec)
                    }

                    $DisconnectedNICs = Get-VMHostNetworkAdapter -VMHost $VMHost -Physical | Where-Object { $_.BitRatePerSec -eq "0" }
                    If (($DisconnectedNICs.Count -gt 0)) {
                        If (($DisconnectedNICs.DeviceName).Contains("vmnic0")) {
                            $myVMHost | Get-VirtualSwitch -Name vSwitch0 | Get-NicTeamingPolicy | Set-NicTeamingPolicy -MakeNicActive vmnic1,vmnic0 | Out-Null
                        }
                    }
                    $HostCount++
                }
                ElseIf ($ConnectFailed) {
                    Write-Host "Failed to connect to $VMHost" -ForegroundColor Yellow
                }
                Else {
                    Write-Host "$VMHost already has RDC Image" -ForegroundColor Yellow
                    If ($VMHost.Name -notin $Global:CompletedHosts) {
                        $Global:CompletedHosts = $Global:CompletedHosts + $VMHost.Name
                    }
                }
            }
            Else {
                Write-Host "$VMHost already exists in vCenter" -ForegroundColor Yellow
            }
        }
    }

    Get-AlarmDefinition "Network uplink redundancy lost" -ErrorAction SilentlyContinue | Set-AlarmDefinition -Enabled $false | Out-Null
    Get-AlarmDefinition "Network uplink redundancy lost" -ErrorAction SilentlyContinue | Set-AlarmDefinition -Enabled $true | Out-Null

    Disconnect-VIServer -Server * -Confirm:$false
}

Function CopyVMToHostIrving([string]$VMHost) {

    Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null

    If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
        $NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)

        New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-TX" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null

        Start-Sleep -Seconds 10
        Start-VM $NewVMName | Out-Null
        Start-Sleep -Seconds 5

        If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
            Exit
        }

        Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
            If ($DCstartedEvent) {
                Break
            }
            Else {
                Start-Sleep -Seconds 5
            }
        }

        Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
            $DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
            If ($DCFailureEvent) {
                Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
                Break
            }
            If ($DCSucceededEvent) {
                Break
            }
            Start-Sleep -Seconds 5
        }

        Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose

        Start-Sleep -Seconds 30
        Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
        Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
        Start-Sleep -Seconds 30

        Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
    }

    Disconnect-VIServer -Server * -Confirm:$false
}

Function CopyVMToHostPlano([string]$VMHost) {

    Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null

    If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
        $NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)

        New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-INT" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null

        Start-Sleep -Seconds 10
        Start-VM $NewVMName | Out-Null
        Start-Sleep -Seconds 5

        If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
            Exit
        }

        Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
            If ($DCstartedEvent) {
                Break
            }
            Else {
                Start-Sleep -Seconds 5
            }
        }

        Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
            $DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
            If ($DCFailureEvent) {
                Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
                Break
            }
            If ($DCSucceededEvent) {
                Break
            }
            Start-Sleep -Seconds 5
        }

        Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose

        Start-Sleep -Seconds 30
        Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
        Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
        Start-Sleep -Seconds 30

        Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
    }

    Disconnect-VIServer -Server * -Confirm:$false
}

Function CopyVMToHostAtlanta([string]$VMHost) {

    Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null

    If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
        $NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)

        New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-ATL" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null

        Start-Sleep -Seconds 10
        Start-VM $NewVMName | Out-Null
        Start-Sleep -Seconds 5

        If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
            Exit
        }

        Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
            If ($DCstartedEvent) {
                Break
            }
            Else {
                Start-Sleep -Seconds 5
            }
        }

        Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
        While($True) {
            $DCvmEvents = Get-VIEvent -Entity $NewVMName
            $DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
            $DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
            If ($DCFailureEvent) {
                Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
                Break
            }
            If ($DCSucceededEvent) {
                Break
            }
            Start-Sleep -Seconds 5
        }

        Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose

        Start-Sleep -Seconds 30
        Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
        Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
        Start-Sleep -Seconds 30

        Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
    }

    Disconnect-VIServer -Server * -Confirm:$false
}

$Functions = [scriptblock]::Create(@"
    Function CopyVMToHostIrving { $Function:CopyVMToHostIrving([string]$myVMHost) }
    Function CopyVMToHostPlano { $Function:CopyVMToHostPlano([string]$myVMHost) }
    Function CopyVMToHostAtlanta { $Function:CopyVMToHostAtlanta([string]$myVMHost) }
"@)

$TotalHostNum = (Get-ADComputer -Server ******************** -SearchBase "OU=********************" -Filter { Name -like "*CED-SQL-PC*" }).Count

While ($Global:CompletedHosts.Count -lt $TotalHostNum) {
    Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null

    Write-Host "Removing completed hosts from vCenter..."
    ForEach ($VMHost in (Get-VMHost -Location (Get-Datacenter "Profit Centers") | Where-Object { $_.Parent -ne "Upgrade Failures" })) {
        If (((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -ne $null -and $VMHost.Name -notin (Get-Job -State Running).Name.Trim("-TXINTATL")) -or (Get-VMHost $VMHost).ConnectionState -ne "Connected") {
            Remove-VMHost $VMHost -Confirm:$false | Out-Null
            If ($VMHost.Name -notin $Global:CompletedHosts) {
                $Global:CompletedHosts = $Global:CompletedHosts + $VMHost.Name
            }
        }
    }

    Write-Host "Adding additional hosts to vCenter..."
    If ($InputFile) {
        Add-VMHosts -MAXVMHostCount $MAXVMHostCount -InputFile $InputFile
    }
    Else {
        Add-VMHosts -MAXVMHostCount $MAXVMHostCount
    }

    $VMHosts = (Get-VMHost -Location (Get-Datacenter "Profit Centers") | Where-Object { $_.Parent -ne "Upgrade Failures" })

    Disconnect-VIServer -Server * -Confirm:$false

    ForEach ($VMHost in $VMHosts) {
        Write-Host "Checking if max job count has been reached..."
        While ((Get-Job -State Running).Count -ge ($IrvingMaxJobs + $PlanoMaxJobs + $AtlantaMaxJobs)) {
            If (((Get-Date).hour -ge 6 -and (Get-Date).hour -lt 18) -and (Get-Date).DayOfWeek -ne "Saturday" -and (Get-Date).DayOfWeek -ne "Sunday") {
                Write-Host "Total count of hosts that new VM was copied to: $Global:CompletedHosts.Count"
                Exit
            }
            Else {
                Start-Sleep -Seconds 60
            }
        }

        Write-Host "Removing completed jobs..."
        ForEach ($Job in (Get-Job -State Completed)) {
            If ($Job.Name.Trim("-TXINTATL") -notin $GlobalCompletedHosts) {
                $Global:CompletedHosts = $Global:CompletedHosts + $Job.Name.Trim("-TXINTATL")
            }
            Remove-Job -Id $Job.Id
        }

        Write-Host "Starting jobs..."
        If ((Get-Job | Where { $_.Name.Trim("-TXINTATL") -eq $VMHost.Name }) -eq $null -and $VMHost.Name -notin $Global:CompletedHosts) {
            If ((Get-Job | Where { $_.Name -like "*-TX" }).Count -lt $IrvingMaxJobs) {
                Write-Host "Starting job for $VMHost using Irving Source" -ForegroundColor Yellow
                Start-Job -InitializationScript $Functions -Script { CopyVMToHostIrving($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-TX") | Out-Null
            }
            ElseIf ((Get-Job | Where { $_.Name -like "*-INT" }).Count -lt $PlanoMaxJobs) {
                Write-Host "Starting job for $VMHost using Plano Source" -ForegroundColor Yellow
                Start-Job -InitializationScript $Functions -Script { CopyVMToHostPlano($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-INT") | Out-Null
            }
            ElseIf ((Get-Job | Where { $_.Name -like "*-ATL" }).Count -lt $AtlantaMaxJobs) {
                Write-Host "Starting job for $VMHost using Atlanta Source" -ForegroundColor Yellow
                Start-Job -InitializationScript $Functions -Script { CopyVMToHostAtlanta($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-ATL") | Out-Null
            }
        }
    }
}
person Evan    schedule 04.05.2019