配置第二个网站控制器
适用于:Windows Azure Pack
Windows Azure 包:网站最多可有两个网站控制器。 为实现高可用性,强烈建议遵照此最大值,另外配置一个网站控制器。 可通过以下两种方式之一执行此操作:
- 创建新场时
- 将控制器添加到现有场
使用 Update Release 6 及更高版本将第二个网站控制器添加到新场 ()
使用 Windows Azure Pack 网站管理控制台,可以在配置新场时添加第二个网站控制器。
- 选择控制器类型时,选择 “将服务器加入到现有网站云”作为辅助控制器。
- 在系统密钥和数据库 ConnectionStrings 列表中,单击“配置”
- 在弹出窗口中,输入以下字段的值:
- 主控制器
- 用户名
- 密码
- 单击 “确定 ”以配置第二个控制器。
将第二个网站控制器添加到现有场
使用以下脚本将控制器添加到现有场:
OnStartSecondaryController.cmd - 在已准备的 Windows 2012 服务器或虚拟机上安装 Web 平台安装程序 (Web PI)。 它在配置辅助控制器完毕之后,接下来调用 HostingBootstrapperBootstrapper 和 OnStartSecondaryController 脚本。 需要的参数包括将在您的设置中使用的 SQL Server 和控制器管理凭据。
HostingBootstrapperBootstrapper.ps1 - 调用 Program Files\Microsoft\Web Platform Installer\WebpiCmd.exe 以安装第二个网站控制器。 它使用与安装主控制器时相同的过程,但通过指定命令行开关 /SuppressPostFinish,放弃配置门户。
OnStartSecondaryController.ps1 - 将配置数据从主控制器复制到辅助控制器,包括
SystemCore
和SiteRuntime
密钥以及托管和资源计量连接字符串。 完成后,它启动 WebFarmService。Common.ps1 - 提供对 OnStartSecondaryController.ps1 脚本的支持功能。
重要
这些脚本要求在主控制器上启用 Windows 远程管理 (WinRM)。
运行脚本的步骤
将脚本文件复制到将成为辅助控制器的服务器上的某个文件夹。
将 WebPlatformInstaller.msi 文件复制到与脚本相同的文件夹。 必须执行此操作,因为 OnStartSecondaryController.cmd 脚本自动安装 Web PI。
以管理员权限运行 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
}
}
}