配置第二个网站控制器

 

适用于:Windows Azure Pack

Windows Azure 包:网站最多可有两个网站控制器。 为实现高可用性,强烈建议遵照此最大值,另外配置一个网站控制器。 可通过以下两种方式之一执行此操作:

  • 创建新场时
  • 将控制器添加到现有场

使用 Update Release 6 及更高版本将第二个网站控制器添加到新场 ()

使用 Windows Azure Pack 网站管理控制台,可以在配置新场时添加第二个网站控制器。

  1. 选择控制器类型时,选择 “将服务器加入到现有网站云”作为辅助控制器
  2. 在系统密钥和数据库 ConnectionStrings 列表中,单击“配置
  3. 在弹出窗口中,输入以下字段的值:
    • 主控制器
    • 用户名
    • 密码
  4. 单击 “确定 ”以配置第二个控制器。

将第二个网站控制器添加到现有场

使用以下脚本将控制器添加到现有场:

  • OnStartSecondaryController.cmd - 在已准备的 Windows 2012 服务器或虚拟机上安装 Web 平台安装程序 (Web PI)。 它在配置辅助控制器完毕之后,接下来调用 HostingBootstrapperBootstrapper 和 OnStartSecondaryController 脚本。 需要的参数包括将在您的设置中使用的 SQL Server 和控制器管理凭据。

  • HostingBootstrapperBootstrapper.ps1 - 调用 Program Files\Microsoft\Web Platform Installer\WebpiCmd.exe 以安装第二个网站控制器。 它使用与安装主控制器时相同的过程,但通过指定命令行开关 /SuppressPostFinish,放弃配置门户。

  • OnStartSecondaryController.ps1 - 将配置数据从主控制器复制到辅助控制器,包括 SystemCoreSiteRuntime 密钥以及托管和资源计量连接字符串。 完成后,它启动 WebFarmService。

  • Common.ps1 - 提供对 OnStartSecondaryController.ps1 脚本的支持功能。

重要

这些脚本要求在主控制器上启用 Windows 远程管理 (WinRM)。

运行脚本的步骤

  1. 将脚本文件复制到将成为辅助控制器的服务器上的某个文件夹。

  2. 将 WebPlatformInstaller.msi 文件复制到与脚本相同的文件夹。 必须执行此操作,因为 OnStartSecondaryController.cmd 脚本自动安装 Web PI。

  3. 以管理员权限运行 OnStartSecondaryController.cmd ,其中提供下表中所述的参数。 需要管理权限,以便可通过 Web PI 自动化安装相应的产品,并可通过 WinRM 访问主控制器。

    注意

    在工作组方案中,可能需要在主控制器上启用 WinRM,或在 OnStartSecondaryController.cmd 脚本中手动运行命令。

OnStartSecondaryController.cmd

语法

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

参数

参数名称 说明 说明
feed (可选)指定将用于安装控制器的 Web PI 源。 如果未指定此参数,则使用默认的 Web PI 主要源。
webSitesInstanceName 用作数据库对象的前缀 必须与安装中数据库的前缀名称一致。
sqlservername SQL Server 实例的名称
sqlsysadmin SQL Server sysadmin 用户帐户名称 需要是 sysadmin 服务器角色的成员。
sqlsysadminpwd SQL Server sysadmin 用户帐户密码
controllerAdminUserName 要配置的 Web Farm Framework (WFF) 管理员帐户名称 如果这是域帐户,则将其添加到本地 Administrators 组。

如果服务器在工作组中,并且用户不存在,则服务器将尝试创建用户,然后将其添加到本地 Administrators 组。
controllerAdminPassword 要配置的 WFF 管理人员帐户密码

OnStartSecondaryController.cmd 脚本

@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  
        }  
    }  
}  

另请参阅

部署 Windows Azure 包:网站