Bereitstellung eines zweiten Websites-Controller

 

Gilt für: Windows Azure Pack

Windows Azure Pack: Websites können maximal zwei Websitecontroller haben. Bei hoher Verfügbarkeit wird dringend empfohlen, dass Sie das Maximum verwenden und einen zweiten Websitecontroller bereitstellen. Dies können Sie auf zwei Arten ausführen:

  • Wenn Sie eine neue Farm erstellen
  • Hinzufügen eines Controllers zu einer vorhandenen Farm

Hinzufügen eines zweiten Websitecontrollers zu einer neuen Farm (mit Update Release 6 und höher)

Mithilfe der Windows Azure Pack-Verwaltungskonsole für Websites können Sie einen zweiten Websitecontroller hinzufügen, wenn Sie eine neue Farm konfigurieren.

  1. Wenn Sie den Controllertyp auswählen, wählen Sie den Server als sekundärer Controller an einem vorhandenen Websites-Cloudserver aus.
  2. Klicken Sie in der Liste der Systemschlüssel und der Datenbankverbindungszeichenfolge auf "Konfigurieren".
  3. Geben Sie im Popupfenster Werte für diese Felder ein:
    • Primärer Controller
    • Benutzername
    • Kennwort
  4. Klicken Sie auf "OK" , um den zweiten Controller zu konfigurieren.

Hinzufügen eines zweiten Websitecontrollers zu einer vorhandenen Farm

Verwenden Sie die folgenden Skripts, um einem vorhandenen Farm einen Controller hinzuzufügen:

  • OnStartSecondaryController.cmd – Installiert WebPlatform-Installer (Web PI) auf dem Windows 2012-Server oder dem Virtuellen Computer, den Sie vorbereitet haben. Dann wird nacheinander Skripts HostingBootstrapperBootstrapper und OnStartSecondaryController aufgerufen, um die Bereitstellung des zweiten Controllers abzuschließen. Erforderliche Parameter schließen die SQL Server- und Controller-Administratorrechte ein, die im Setup verwendet werden.

  • HostingBootstrapperBootstrapper.ps1 – Ruft Programme\Microsoft\Web Platform Installer\WebpiCmd.exe auf, um den zweiten Websitecontroller zu installieren. Er wird auf dieselbe Weise wie der primäre Controller installiert, verwirft aber das Konfigurationsportal durch Angeben von Befehlszeilenschalter /SuppressPostFinish.

  • OnStartSecondaryController.ps1 – Kopiert die Konfigurationsdaten vom primären Controller zum sekundären Controller, einschließlich SystemCore und SiteRuntime-Schlüssel sowie die Verbindungszeichenfolgen für Hosting und Ressourcenmessung. Wenn dies abgeschlossen ist, wird der WebFarmService gestartet.

  • Common.ps1 – Stellt Unterstützungsfunktionen für das Skript OnStartSecondaryController.ps1 bereit.

Wichtig

Diese Skripts erfordern, dass die Windows-Remoteverwaltung (WinRM) auf dem primären Controller aktiviert ist.

Schritte zum Ausführen der Skripts

  1. Kopieren Sie die Skriptdateien in einen Ordner auf dem Server, welcher der sekundäre Controller sein wird.

  2. Kopieren Sie die WebPlatformInstaller.msi-Datei in den gleichen Ordner wie die Skripts. Dies ist notwendig, da das OnStartSecondaryController.cmd-Skript die Installation von Web PI automatisiert.

  3. Führen Sie OnStartSecondaryController.cmd mit Administratorrechten aus und geben Sie die Parameter an, die in der folgenden Tabelle beschrieben werden. Administratorrechte sind erforderlich, damit die entsprechenden Produkte nach Web PI-Automatisierung installiert werden können und auf den primären Controller durch WinRM zugegriffen werden kann.

    Hinweis

    In einem WORKGROUP-Szenario müssen Sie möglicherweise WinRM auf dem primären Controller aktivieren, oder die Befehle im OnStartSecondaryController.cmd-Skript manuell ausführen.

OnStartSecondaryController.cmd

Syntax

OnStartSecondaryController.cmd -feed %Feed% -webSitesInstanceName %WebSitesInstanceName% -sqlservername %DatabaseServerName% -sqlsysadmin %DatabaseSysAdminAccount% -sqlsysadminpwd %DatabaseSysAdminPassword% -controllerAdminUserName %ControllerAdminUserName% -controllerAdminPassword %ControllerAdminPassword%  

Parameter

Parametername Beschreibung Hinweise
feed Gibt optional den Web PI-Feed an, der verwendet wird, um den Controller zu installieren. Wenn dieser Parameter nicht angegeben wird, wird der primäre Standard-Web PI-Feed verwendet.
webSitesInstanceName Wird als Präfix für Datenbankobjekte verwendet Muss mit dem Präfixnamen der Datenbank in der Installation übereinstimmen.
sqlservername Name der SQL Server-Instanz
sqlsysadmin Benutzerkontoname für SQL Server-Sysadmin Muss Mitglied von Sysadmin-Serverrollen sein.
sqlsysadminpwd Benutzerkontokennwort für SQL Server-Sysadmin
controllerAdminUserName Kontoname für bereitzustellenden Web Farm Framework (WFF)-Administrator Wenn dies ein Domänenkonto ist, wird es zur lokalen Administratorgruppe hinzugefügt.

Wenn der Server in der WORKGROUP ist, wird versucht, den Benutzer zu erstellen, wenn der Benutzer nicht vorhanden ist, und ihn dann zur lokalen Administratorgruppe hinzufügen.
controllerAdminPassword Bereitzustellendes WFF-Administratorkontokennwort

OnStartSecondaryController.cmd-Skript

@echo off  
echo Starting OnStartSecondaryController.cmd  
  
rem ---------------------------------------------  
rem Initialize Variables  
rem ---------------------------------------------  
    set POWERSHELL=%windir%\System32\WindowsPowerShell\v1.0\powershell.exe  
  
    set FEED=  
    set INSTANCE_NAME=  
    set SQL_SERVERNAME=  
    set SQL_SYSADMIN=  
    set SQL_SYSADMINPWD=  
    set CONTROLLER_ADMIN_USERNAME=  
    set CONTROLLER_ADMIN_PASSWORD=  
  
rem ---------------------------------------------  
rem Parse command line parameters  
rem ---------------------------------------------  
:parse_param  
    set PARAM_MATCHED=0  
    if "%1"=="" (  
        goto :parse_param_completed  
    )  
  
    if /I "%1"=="-feed" (  
        set FEED=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-webSitesInstanceName" (  
        set INSTANCE_NAME=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-sqlservername" (  
        set SQL_SERVERNAME=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-sqlsysadmin" (  
        set SQL_SYSADMIN=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-sqlsysadminpwd" (  
        set SQL_SYSADMINPWD=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-controllerAdminUserName" (  
        set CONTROLLER_ADMIN_USERNAME=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if /I "%1"=="-controllerAdminPassword" (  
        set CONTROLLER_ADMIN_PASSWORD=%2  
        shift  
        set PARAM_MATCHED=1  
    )  
  
    if "%PARAM_MATCHED%"=="0" (  
       echo Parameter %1 was not matched  
       exit 1  
    )  
  
    shift  
    goto :parse_param  
:parse_param_completed  
  
rem -----------------------------------------------------------------------  
rem Provision Controller  
rem ------------------------------------------------------------------------  
  
    echo Installing WebPlatformInstaller.  
    start /wait %windir%\system32\msiexec.exe /qn /i WebPlatformInstaller.msi /l %SystemDrive%\WebPlatformInstaller.log  
    if errorlevel 1 (  
        echo WebPlatform Installer installation failed. See log at %SystemDrive%\WebPlatformInstaller.log for more details.  
        exit 1  
    )  
  
    echo WebPlatformInstaller setup completed successfully.  
  
    echo Enabling remote desktop access.  
    start /wait cscript %windir%\system32\scregedit.wsf /ar 0  
    if errorlevel 1 (  
        echo Enabling remote desktop failed.  
        exit 1  
    )  
  
    echo Remote desktop access has been enabled successfully.  
  
    if exist StrongNameHijack.msi (  
  
        echo Installing StrongNameHijack...  
        start /wait %windir%\system32\msiexec.exe /qn /i StrongNameHijack.msi /l %SystemDrive%\StrongNameHijack.log  
        if errorlevel 1 (  
            echo "StrongNameHijack installation failed. See log at %SystemDrive%\StrongNameHijack.log for more details."  
            exit 1  
        )  
  
        echo StrongNameHijack setup completed successfully.  
  
        net stop msiserver & net start msiserver  
    )  
  
    echo Starting HostingBootstrapperBootstrapper.ps1  
  
    if "%FEED%"=="" (  
        %POWERSHELL% -ExecutionPolicy Unrestricted -File HostingBootstrapperBootstrapper.ps1  
    ) else (  
        %POWERSHELL% -ExecutionPolicy Unrestricted -File HostingBootstrapperBootstrapper.ps1 -mainFeed "%FEED%"  
    )  
  
    if errorlevel 1 (  
        echo HostingBootstrapperBootstrapper.ps1 failed.  
        exit 1  
    )  
  
    echo HostingBootstrapperBootstrapper.ps1 completed successfully.  
  
    echo Starting OnStartSecondaryController.ps1  
  
    %POWERSHELL% -ExecutionPolicy Unrestricted -File OnStartSecondaryController.ps1 -webSitesInstanceName "%INSTANCE_NAME%" -sqlservername "%SQL_SERVERNAME%" -sqlsysadmin "%SQL_SYSADMIN%" -sqlsysadminpwd "%SQL_SYSADMINPWD%" -controllerAdminUserName "%CONTROLLER_ADMIN_USERNAME%" -controllerAdminPassword "%CONTROLLER_ADMIN_PASSWORD%"  
    if errorlevel 1 (  
        echo OnStartSecondaryController.ps1 failed.  
        exit 1  
    )  
  
    echo OnStartSecondaryController.ps1 completed successfully.  
  
echo OnStartSecondaryController.cmd completed successfully.  
  
exit 0  

HostingBootstrapperBootstrapper.ps1

# PowerShell script to setup Web Sites Controller using WebPI.  
# Copyright (c) Microsoft Corporation. All rights reserved.  
  
Param  
(  
    [string] $boostrapperProductId = "HostingPrimaryControllerBootstrapper_v2",  
    [string] $mainFeed = "",  
    [string] $customFeed = ""  
)  
  
# Change Error Action to Stop  
$ErrorActionPreference="Stop"  
  
Function BootstrapBootstrapper ()  
{  
    $WebPiCmd = [System.Environment]::ExpandEnvironmentVariables("%ProgramW6432%\Microsoft\Web Platform Installer\WebpiCmd.exe")  
    $WebPiLog = [System.Environment]::ExpandEnvironmentVariables("%SystemDrive%\HostingPrimaryControllerBootstrapper.log")  
  
    If ($mainFeed -eq "")  
    {  
        Invoke-Command -ScriptBlock { & $WebPiCmd /Install /Products:$boostrapperProductId /AcceptEula /SuppressReboot /SuppressPostFinish /Log:$WebPiLog }  
    }  
    Else  
    {  
        If ($customFeed -eq "")  
        {  
            Invoke-Command -ScriptBlock { & $WebPiCmd /Install /Products:$boostrapperProductId /AcceptEula /SuppressReboot /SuppressPostFinish /XML:$mainFeed /Log:$WebPiLog }  
        }  
        Else  
        {  
            Invoke-Command -ScriptBlock { & $WebPiCmd /Install /Products:$boostrapperProductId /AcceptEula /SuppressReboot /SuppressPostFinish /XML:$mainFeed /Feeds:$customFeed /Log:$WebPiLog }  
        }  
    }  
  
    If ($lastexitcode -ne $Null -And $lastexitcode -ne 0)  
    {  
        Exit $lastexitcode  
    }  
}  
  
# Entry Point  
BootstrapBootstrapper  

OnStartSecondaryController.ps1

# PowerShell script to setup a Web Sites secondary controller.  
# Copyright (c) Microsoft Corporation. All rights reserved.  
  
Param  
(  
    [string] $webSitesInstanceName,  
    [string] $sqlservername,  
    [string] $sqlsysadmin,  
    [string] $sqlsysadminpwd,  
    [string] $controllerAdminUserName,  
    [string] $controllerAdminPassword  
)  
  
# Init Global Variables  
$cnstr = "server=$($sqlservername);database=Hosting;uid=$($sqlsysadmin);pwd=$($sqlsysadminpwd);"  
  
# Load common script file  
$startDir = "."  
$common = [System.IO.Path]::Combine($startDir, "Common.ps1")  
. $common  
  
Function Get-PrimaryController()  
{  
    Try  
    {  
        $siteManager = New-Object Microsoft.Web.Hosting.SiteManager $cnstr  
        $primaryController = $siteManager.Controllers.GetPrimaryController([Microsoft.Web.Hosting.PlatformOptions]::VirtualMachineManager)  
  
        Return $primaryController.MachineName;  
    }  
    Finally  
    {  
        If($siteManager -ne $Null)  
        {  
            $siteManager.Dispose()  
        }  
    }  
}  
  
Function Test-PrimaryController()  
{  
    Try  
    {  
        $PrimaryController = Get-PrimaryController  
  
        Return $PrimaryController -ne $Null  
    }  
    Catch [Microsoft.Web.Hosting.WebHostingObjectNotFoundException]  
    {  
        Write-Host "$(Get-Date): Primary Controller NOT Ready"  
  
        Return $False;  
    }  
}  
  
Function WaitForPrimaryControllerToBeReady()  
{  
    $WaitIndex=0;  
    $MaxWait=15  
    $WaitInterval=60000  
  
    Write-Host "$(Get-Date): Waiting for Primary Controller to be ready"  
  
    $valid = Test-PrimaryController  
    If ($valid -eq $False)  
    {  
        While ($WaitIndex -lt $MaxWait -and $valid -eq $False)  
        {  
            [System.Threading.Thread]::Sleep($WaitInterval);  
            $WaitIndex = $WaitIndex + 1  
            $valid = Test-PrimaryController  
        }  
    }  
  
    If ($valid -eq $False)  
    {  
        Throw New-Object [System.Exception] "Primary Controller NOT ready. Timed out waiting."  
    }  
  
    Write-Host "$(Get-Date): Primary Controller is Ready"  
}  
  
Function Copy-SystemCoreKeyFromPrimaryController([string] $PrimaryController)  
{  
    Write-Host "$(Get-Date): Copy SystemCore key"  
  
    $remoteCommand = {  
        Add-PSSnapIn WebHostingSnapIn  
        Get-WebSitesConfig -Type SecurityKey -SymmetricKeyName SystemCore  
    }  
  
    $SystemCoreKey = Invoke-Command -ComputerName $PrimaryController -ScriptBlock $remoteCommand  
    Set-WebSitesConfig -Type SecurityKey -SymmetricKeyName SystemCore -SymmetricKey $SystemCoreKey -Force  
}  
  
Function Copy-SiteRuntimeKeyFromPrimaryController([string] $PrimaryController)  
{  
    Write-Host "$(Get-Date): Copy SiteRuntime key"  
  
    $remoteCommand = {  
        Add-PSSnapIn WebHostingSnapIn  
        Get-WebSitesConfig -Type SecurityKey -SymmetricKeyName SiteRuntime  
    }  
  
    $SiteRuntimeKey  = Invoke-Command -ComputerName $PrimaryController -ScriptBlock $remoteCommand  
    Set-WebSitesConfig -Type SecurityKey -SymmetricKeyName SiteRuntime -SymmetricKey $SiteRuntimeKey -Force  
}  
  
Function Copy-HostingConnectionStringFromPrimaryController([string] $PrimaryController)  
{  
    Write-Host "$(Get-Date): Copy hosting connection string"  
  
    $remoteCommand = {  
        Add-PSSnapIn WebHostingSnapIn  
        [Microsoft.Web.Hosting.SiteManager]::GetDefaultConnectionString()  
    }  
  
    $HostingCnStr  = Invoke-Command -ComputerName $PrimaryController -ScriptBlock $remoteCommand  
    Set-WebSitesConnectionString -Type Hosting -ConnectionString $HostingCnStr  
}  
  
Function Copy-MeteringConnectionStringFromPrimaryController([string] $PrimaryController)  
{  
    Write-Host "$(Get-Date): Copy metering connection string"  
  
    $remoteCommand = {  
        Add-PSSnapIn WebHostingSnapIn  
        [Microsoft.Web.Hosting.SiteManager]::GetMeteringConnectionString()  
    }  
  
    $computerName = [Microsoft.Web.Hosting.Common.NetworkHelper]::GetComputerName([Microsoft.Web.Hosting.Common.ComputerNameFormat]::DnsFullyQualified)  
  
    $MeteringCnStr  = Invoke-Command -ComputerName $PrimaryController -ScriptBlock $remoteCommand      
    Set-WebSitesConnectionString -Type Metering -ConnectionString $MeteringCnStr -ServerName $computerName  
}  
  
Function OnStartSecondaryController()  
{  
    Try  
    {  
        Write-Host "$(Get-Date): Starting OnStartSecondaryController()" -ForegroundColor Green  
  
        Add-PSSnapin WebHostingSnapin  
  
        ConfigureRoleAdministrator $controllerAdminUserName $controllerAdminPassword  
  
        WaitForHostingDatabaseToBeReady $cnstr  
        WaitForPrimaryControllerToBeReady  
  
        $PrimaryController = Get-PrimaryController  
        Copy-SystemCoreKeyFromPrimaryController $PrimaryController  
        Copy-SiteRuntimeKeyFromPrimaryController $PrimaryController  
        Copy-HostingConnectionStringFromPrimaryController $PrimaryController  
        Copy-MeteringConnectionStringFromPrimaryController $PrimaryController  
  
        Start-Service WebFarmService  
  
        Write-Host "$(Get-Date): OnStartSecondaryController completed successfully" -ForegroundColor Green  
    }  
    Catch [System.Exception]  
    {  
        Write-Host "$(Get-Date): Exception encountered while executing OnStartSecondaryController:" -ForegroundColor Red  
        Write-Host $_ -ForegroundColor Red  
  
        Write-Host "$(Get-Date): Exiting OnStartSecondaryController.ps1" -ForegroundColor Red  
        Exit -1  
    }  
}  
  
# Entry Point  
OnStartSecondaryController  

Common.ps1

# PowerShell Web Sites common script.  
# Copyright (c) Microsoft Corporation. All rights reserved.  
  
Function LoadHostingFramework()  
{  
    $mwhc = Get-Item ".\Microsoft.Web.Hosting.Common.dll"  
    [void] [System.Reflection.Assembly]::LoadFrom($mwhc.FullName)  
    Write-Host "$(Get-Date): Microsoft.Web.Hosting.Common assembly was successfully loaded from: $mwhc"  
  
    $mwh  = Get-Item ".\Microsoft.Web.Hosting.dll"  
    [void] [System.Reflection.Assembly]::LoadFrom($mwh.FullName)  
    Write-Host "$(Get-Date): Microsoft.Web.Hosting assembly was successfully loaded from: $mwh"  
}  
  
Function IsHostingDatabaseReady([string] $cnstr)  
{  
    Try  
    {  
        $siteManager = New-Object Microsoft.Web.Hosting.SiteManager $cnstr  
        $siteManager.TestConnection([Microsoft.Web.Hosting.PlatformOptions]::VirtualMachineManager)  
  
        Return $true;  
    }  
    Catch [Exception]  
    {  
        Write-Host "$(Get-Date): Hosting Database NOT Ready"  
  
        Return $false;  
    }  
    Finally  
    {  
        If($siteManager -ne $Null)  
        {  
            $siteManager.Dispose()  
        }  
    }  
}  
  
Function WaitForHostingDatabaseToBeReady ([string] $cnstr)  
{  
    $WaitIndex=0;  
    $MaxWait=120  
    $WaitInterval=60000  
  
    Write-Host "$(Get-Date): Waiting for Hosting Database to be ready"  
  
    $ready = IsHostingDatabaseReady $cnstr  
    If($ready -ne $true)  
    {  
    While ($WaitIndex -lt $MaxWait -and $ready -ne $true)  
        {  
            [System.Threading.Thread]::Sleep($WaitInterval);  
            $WaitIndex = $WaitIndex + 1  
            $ready = IsHostingDatabaseReady $cnstr  
        }  
    }  
  
    If ($ready -ne $true)  
    {  
        Throw New-Object [System.Exception] "Hosting Database NOT ready. Timed out waiting."  
    }  
  
    Write-Host "$(Get-Date): Hosting Database is Ready"  
}  
  
Function ConfigureRoleAdministrator([string] $roleadminusr, [string] $roleadminpwd)  
{  
    $isDomain = $false  
  
    # Identify if user is a domain user account  
    $values = $roleadminusr.Split('\');  
    If ($values.Length -eq 1)  
    {  
        $domain = $env:COMPUTERNAME  
        $username = $values[0]  
    }  
    ElseIf ($values.Length -eq 2)  
    {  
        If ([String]::Equals($values[0], ".") -Or  
            [String]::Equals($values[0], [Environment]::MachineName, [StringComparison]::OrdinalIgnoreCase))  
        {  
            $isDomain = $false  
            $domain = $env:COMPUTERNAME  
            $username = $values[1]  
        }  
        Else  
        {  
            $isDomain = $true  
            $domain = $values[0]  
            $username = $values[1]  
        }  
    }  
    Else  
    {  
        Throw New-Object ArgumentException "Invalid user name" "roleadminusr"  
    }  
  
    # Create user if specified user is not a domain account  
    if ($isDomain -eq $false)  
    {  
        Try  
        {  
            $computer = [ADSI]"WinNT://$env:COMPUTERNAME"  
            $user = $computer.Create("User", $username)  
            $user.setpassword($roleadminpwd)  
            $user.SetInfo()  
        }  
        Catch [System.Runtime.InteropServices.COMException]  
        {  
            # User already exists  
            If ($_.Exception.ErrorCode -eq -2147022672)  
            {  
                Write-Host "$(Get-Date): User $domain\$username already exits."  
                Write-Host "$(Get-Date): Updating password for User $domain\$username."  
                $user = [ADSI]("WinNT://$env:COMPUTERNAME/$username,user")  
                $user.setpassword($roleadminpwd)  
                $user.SetInfo()  
            }  
            Else  
            {  
                Write-Host "$(Get-Date): Error creating user $domain\$username." -ForegroundColor Red  
  
                Throw  
            }  
        }  
    }  
  
    # Add user to local administrators group  
  
    #first translate the "Administrators" name to the name based on locale (make well known SID -> Name translation)  
  
    $administratorsSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")  
    $administratorsGroupName = $administratorsSID.Translate([System.Security.Principal.NTAccount]).Value  
  
    # retrieve the short name eg in german translate VORDEFINIERT\Administratoren ->Administratoren  
    # the translation should always return fully qualified name equivalent to BUILTIN\Administrators  
  
    $localizedAdministratorsGroupName=$administratorsGroupName.Split("\\")[1]  
  
    $adminGroup = [ADSI]("WinNT://$env:COMPUTERNAME/$localizedAdministratorsGroupName,group")  
  
    Try  
    {  
        If ($isDomain -eq $true)  
        {  
            $adminGroup.add("WinNT://$domain/$username,user")  
        }  
        Else  
        {  
            $adminGroup.add("WinNT://$env:COMPUTERNAME/$username,user")  
        }  
    }  
    Catch [System.Runtime.InteropServices.COMException]  
    {  
        If ($_.Exception.ErrorCode -eq -2147023518) # ERROR_MEMBER_IN_ALIAS 1378 (0x562)  
        {  
            Write-Host "$(Get-Date): User $domain\$username is already member of Administrators group."  
        }  
        Else  
        {  
            Write-Host "$(Get-Date): Error adding user $domain\$username to Administrators group." -ForegroundColor Red  
  
            Throw  
        }  
    }  
}  

Weitere Informationen

Bereitstellen von Windows Azure Pack: Websites