佈建第二部 Web Sites 控制器

 

適用于:Windows Azure Pack

Windows Azure Pack: Web Sites 最多可有兩個 Web Sites 控制站。 如果要使用高可用性,極力建議您採用此上限值並佈建第二部 Web Sites 控制器。 您可以使用下列兩種方式之一來執行此動作:

  • 當您建立新的伺服器陣列時
  • 將控制器新增至現有的伺服器陣列

使用更新版本 6 和更新版本將第二個網站控制器新增至新的伺服器陣列 ()

使用 Windows Azure Pack Web Sites 管理主控台,您可以在設定新的伺服器陣列時新增第二個 Web Sites 控制器。

  1. 選取控制器類型時,請選取 [ 將伺服器加入現有的網站雲端] 作為次要控制器
  2. 在系統金鑰和資料庫 ConnectionStrings 清單中,按一下 [ 設定]
  3. 在快顯視窗中,輸入這些欄位的值:
    • 主要控制器
    • 使用者名稱
    • 密碼
  4. 按一下 [確定 ] 來設定第二個控制器。

將第二個網站控制器新增至現有的伺服器陣列

使用下列腳本將控制器新增至現有的伺服器陣列:

  • OnStartSecondaryController.cmd :在您所準備的 Windows 2012 伺服器或虛擬機器上,安裝 WebPlatform Installer (Web PI)。 接下來,其會依序呼叫 HostingBootstrapperBootstrapper 及 OnStartSecondaryController 指令碼,完成次要控制器的佈建工作。 必要項目包括 SQL Server 及控制器的系統管理認證,供設定時使用。

  • HostingBootstrapperBootstrapper.ps1:呼叫 Program Files\Microsoft\Web Platform Installer\WebpiCmd.exe,以安裝第二部 Web Sites 控制器。 其程序和安裝主要控制站相同,但會指定命令列參數 /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 進行存取。

    注意

    在 WORKGROUP 案例中,您可能需要啟用主要控制站的 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 系統管理使用者帳戶的名稱 必須是系統管理伺服器角色的成員。
sqlsysadminpwd SQL Server 系統管理使用者帳戶的密碼
controllerAdminUserName 要佈建之 Web Farm Framework (WFF) 系統管理員帳戶的名稱 這如果是網域帳戶,將會加入本機的 Administrators 群組。

如果伺服器位於 WORKGROUP,而使用者不存在,其會嘗試建立該使用者,並將其加入本機的 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 Pack:Web Sites