在 PowerShell 中创建网络场
Creating a web farm in PowerShell
我正在尝试在 PowerShell 中自动创建服务器场。通过手动创建,我得到了以下 XML:
<webFarms>
<webFarm name="alwaysup" enabled="true">
<server address="alwaysup-blue" enabled="true">
<applicationRequestRouting httpPort="8001" />
</server>
<server address="alwaysup-green" enabled="true">
<applicationRequestRouting httpPort="8002" />
</server>
<applicationRequestRouting>
<healthCheck url="http://alwaysup/up.html" interval="00:00:05" responseMatch="up" />
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
尝试通过 PS 执行此操作证明很麻烦:据我所知,没有专门的 API 来执行此操作(WebFarmSnapin 用于旧版本).
我已将注意力转移到 IIS Administration Cmdlets,但只完成了一半。
我拥有的代码:
#####
# Overwriting the server farm
#####
Write-Host "Overwriting the server farm $($webFarmName)"
$webFarm = @{};
$webFarm["name"] = 'siteFarm'
Set-WebConfiguration "/webFarms" -Value $webFarm
#####
# Adding the servers
#####
Write-Host "Adding the servers"
$blueServer = @{}
$blueServer["address"] = 'site-blue'
$blueServer["applicationRequestRouting"] = @{}
$greenServer = @{}
$greenServer["address"] = 'site-green'
$greenServer["applicationRequestRouting"] = @{}
$servers = @($blueServer, $greenServer)
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']" -Value $servers
#####
# Adding routing
#####
Write-Host "Adding the routing configurations"
$blueServerRouting = @{}
$blueServerRouting["httpPort"] = "8001"
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']/server[@address='site-blue']" -Value $blueServerRouting
这会生成
<webFarms>
<webFarm name="siteFarm">
<server address="site-blue" />
<server address="site-green" />
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
如您所见,它缺少与路由相关的端口。此时我什至还没有开始尝试添加健康检查。
我做错了什么?是否有一些我没有找到的 Cmdlet 让这更容易?
Related 但没有太多有用的答案(带有生成代码的 PowerShell 选项卡保持为空)。
鉴于没有人提出解决方案:我采用了非常简单的解决方法,直接编辑 XML 而不是通过 API.
实际的 webfarm 功能我还没有开始工作,但看起来至少所有的部分都在那里。
一个操纵 XML 的例子可以是这样的:
$configPath = "C:\Windows\System32\inetsrv\config\applicationHost.config"
$configXml = [xml] (type $configPath)
[xml] $webFarmXml = @"
<webFarms>
<webFarm name="siteFarm" enabled="true">
<server address="site-blue" enabled="true">
<applicationRequestRouting httpPort="$bluePort" />
</server>
<server address="site-green" enabled="true">
<applicationRequestRouting httpPort="$greenPort" />
</server>
<applicationRequestRouting>
<healthCheck url="http://mySite/up.html" interval="00:00:05" responseMatch="up" />
<protocol reverseRewriteHostInResponseHeaders="true">
<cache enabled="false" queryStringHandling="NoCaching" />
</protocol>
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
"@
$configXml.configuration.AppendChild($configXml.ImportNode($webFarmXml.webFarms, $true))
$configXml.Save($configPath)
看来您已经通过修改 XML 配置文件本身找到了执行此操作的方法。尽管感觉这不太像 "right way",但直接更改配置文件是一个完全有效的解决方案。本质上,您创建了一个 模板 ,配置模板提供了一种快速且可读的方法来生成可维护和可重复的配置。
我们可以通过将模板文本从脚本中提取到单独的文本文件中来改进这种方法。然后,脚本可以读取(或获取)模板并根据需要换出任何占位符值。这类似于我们通过将 HTML 模板与代码分离来分离 Web 应用程序中的关注点。
为了更直接地回答这个问题,让我们看一下如何使用 PowerShell 和 IIS 管理来做到这一点 API(你是对的——如果 IIS,最新版本不再支持 Web Farm Framework和 Windows)。问题中的原始代码是一个好的开始。我们只需要区分操作配置 collections(使用 *-WebConfiguration
cmdlet)和配置 items(使用 *-WebConfigurationProperty
cmdlet) .这是一个脚本,它将根据问题中的示例设置配置值:
$farmName = 'siteFarm'
Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter 'webFarms' `
-Name '.' `
-Value @{ name = $farmName; enabled = $true }
Add-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']" `
-Value @(
@{ address = 'site-blue'; enabled = $true },
@{ address = 'site-green'; enabled = $true }
)
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-blue']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8001 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-green']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8002 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'healthCheck' `
-Value @{
url = 'http://mySite/up.html'
interval = '00:00:05'
responseMatch = 'up'
}
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'protocol' `
-Value @{ reverseRewriteHostInResponseHeaders = $true }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting/protocol" `
-Name 'cache' `
-Value @{ enabled = $false; queryStringHandling = 'NoCaching' }
这可能有点冗长,但我想清楚地说明每一步的意图。此实现使用 XPath 查询 -Filter
参数到 select 适当的 XML 节点。我们或许可以重构脚本以减少重复性任务,例如通过定义一个 Add-FarmServer
函数来获取服务器名称和端口,然后添加适当的指令。如果遇到锁定配置问题,我们可能还需要 Remove-WebConfigurationLock
。
我们是选择使用模板还是程序化方法取决于项目和团队的偏好。当我们有许多类似的项目要配置时,API 变得更有吸引力,例如,如果我们有数百台服务器要添加到网络场。另一方面,模板易于理解,不需要其他团队成员学习新的(可能有些混乱)API。
我一直在研究这个,我发现你可以在 Powershell 中使用 Microsoft.Web.Administration
来完成这个。
Add-Type -AssemblyName "Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
$iisManager = New-Object Microsoft.Web.Administration.ServerManager
$applicationHostConfig = $IISManager.GetApplicationHostConfiguration()
# Get the WebFarms section. Section names are case-sensitive.
$webFarmSection = $ApplicationHostConfig.GetSection("webFarms")
$webFarms = $WebFarmSection.GetCollection()
# Create a new webfarm and assign a name.
$webFarm = $WebFarms.CreateElement("webFarm")
$webFarm.SetAttributeValue("name", "MyNewWebFarmName")
# Add it to the parent element
$webFarms.Add($webFarm)
# get Webfarm server collection
$servers = $webFarm.GetCollection()
# add server
$serverBlue = $servers.CreateElement("server")
$routingBlue = $serverBlue.GetChildElement("applicationRequestRouting")
$routingBlue.SetAttributeValue("httpPort", "8001")
$serverBlue.SetAttributeValue("address", "MyNewWebFarmName-blue")
$servers.Add($serverBlue)
# Save changes
$iisManager.CommitChanges()
有两件事值得一提:
调用 $iisManager.CommitChanges()
后对象进入只读模式。在进行任何新更改之前,您需要重新实例化所有对象。
如果您决定在您的网络场中创建一个新服务器,如果您为该服务器分配一个名称然后尝试访问其端口设置,您将 运行 遇到问题。您需要做的是先分配端口,然后分配名称以及是否为enabled/disabled。否则,它会抛出 System.AccessViolationException: Attempted to read or write protected memory.
错误。
让我知道进展如何!
我正在尝试在 PowerShell 中自动创建服务器场。通过手动创建,我得到了以下 XML:
<webFarms>
<webFarm name="alwaysup" enabled="true">
<server address="alwaysup-blue" enabled="true">
<applicationRequestRouting httpPort="8001" />
</server>
<server address="alwaysup-green" enabled="true">
<applicationRequestRouting httpPort="8002" />
</server>
<applicationRequestRouting>
<healthCheck url="http://alwaysup/up.html" interval="00:00:05" responseMatch="up" />
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
尝试通过 PS 执行此操作证明很麻烦:据我所知,没有专门的 API 来执行此操作(WebFarmSnapin 用于旧版本).
我已将注意力转移到 IIS Administration Cmdlets,但只完成了一半。
我拥有的代码:
#####
# Overwriting the server farm
#####
Write-Host "Overwriting the server farm $($webFarmName)"
$webFarm = @{};
$webFarm["name"] = 'siteFarm'
Set-WebConfiguration "/webFarms" -Value $webFarm
#####
# Adding the servers
#####
Write-Host "Adding the servers"
$blueServer = @{}
$blueServer["address"] = 'site-blue'
$blueServer["applicationRequestRouting"] = @{}
$greenServer = @{}
$greenServer["address"] = 'site-green'
$greenServer["applicationRequestRouting"] = @{}
$servers = @($blueServer, $greenServer)
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']" -Value $servers
#####
# Adding routing
#####
Write-Host "Adding the routing configurations"
$blueServerRouting = @{}
$blueServerRouting["httpPort"] = "8001"
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']/server[@address='site-blue']" -Value $blueServerRouting
这会生成
<webFarms>
<webFarm name="siteFarm">
<server address="site-blue" />
<server address="site-green" />
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
如您所见,它缺少与路由相关的端口。此时我什至还没有开始尝试添加健康检查。
我做错了什么?是否有一些我没有找到的 Cmdlet 让这更容易?
Related 但没有太多有用的答案(带有生成代码的 PowerShell 选项卡保持为空)。
鉴于没有人提出解决方案:我采用了非常简单的解决方法,直接编辑 XML 而不是通过 API.
实际的 webfarm 功能我还没有开始工作,但看起来至少所有的部分都在那里。
一个操纵 XML 的例子可以是这样的:
$configPath = "C:\Windows\System32\inetsrv\config\applicationHost.config"
$configXml = [xml] (type $configPath)
[xml] $webFarmXml = @"
<webFarms>
<webFarm name="siteFarm" enabled="true">
<server address="site-blue" enabled="true">
<applicationRequestRouting httpPort="$bluePort" />
</server>
<server address="site-green" enabled="true">
<applicationRequestRouting httpPort="$greenPort" />
</server>
<applicationRequestRouting>
<healthCheck url="http://mySite/up.html" interval="00:00:05" responseMatch="up" />
<protocol reverseRewriteHostInResponseHeaders="true">
<cache enabled="false" queryStringHandling="NoCaching" />
</protocol>
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
"@
$configXml.configuration.AppendChild($configXml.ImportNode($webFarmXml.webFarms, $true))
$configXml.Save($configPath)
看来您已经通过修改 XML 配置文件本身找到了执行此操作的方法。尽管感觉这不太像 "right way",但直接更改配置文件是一个完全有效的解决方案。本质上,您创建了一个 模板 ,配置模板提供了一种快速且可读的方法来生成可维护和可重复的配置。
我们可以通过将模板文本从脚本中提取到单独的文本文件中来改进这种方法。然后,脚本可以读取(或获取)模板并根据需要换出任何占位符值。这类似于我们通过将 HTML 模板与代码分离来分离 Web 应用程序中的关注点。
为了更直接地回答这个问题,让我们看一下如何使用 PowerShell 和 IIS 管理来做到这一点 API(你是对的——如果 IIS,最新版本不再支持 Web Farm Framework和 Windows)。问题中的原始代码是一个好的开始。我们只需要区分操作配置 collections(使用 *-WebConfiguration
cmdlet)和配置 items(使用 *-WebConfigurationProperty
cmdlet) .这是一个脚本,它将根据问题中的示例设置配置值:
$farmName = 'siteFarm'
Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter 'webFarms' `
-Name '.' `
-Value @{ name = $farmName; enabled = $true }
Add-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']" `
-Value @(
@{ address = 'site-blue'; enabled = $true },
@{ address = 'site-green'; enabled = $true }
)
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-blue']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8001 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-green']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8002 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'healthCheck' `
-Value @{
url = 'http://mySite/up.html'
interval = '00:00:05'
responseMatch = 'up'
}
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'protocol' `
-Value @{ reverseRewriteHostInResponseHeaders = $true }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting/protocol" `
-Name 'cache' `
-Value @{ enabled = $false; queryStringHandling = 'NoCaching' }
这可能有点冗长,但我想清楚地说明每一步的意图。此实现使用 XPath 查询 -Filter
参数到 select 适当的 XML 节点。我们或许可以重构脚本以减少重复性任务,例如通过定义一个 Add-FarmServer
函数来获取服务器名称和端口,然后添加适当的指令。如果遇到锁定配置问题,我们可能还需要 Remove-WebConfigurationLock
。
我们是选择使用模板还是程序化方法取决于项目和团队的偏好。当我们有许多类似的项目要配置时,API 变得更有吸引力,例如,如果我们有数百台服务器要添加到网络场。另一方面,模板易于理解,不需要其他团队成员学习新的(可能有些混乱)API。
我一直在研究这个,我发现你可以在 Powershell 中使用 Microsoft.Web.Administration
来完成这个。
Add-Type -AssemblyName "Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
$iisManager = New-Object Microsoft.Web.Administration.ServerManager
$applicationHostConfig = $IISManager.GetApplicationHostConfiguration()
# Get the WebFarms section. Section names are case-sensitive.
$webFarmSection = $ApplicationHostConfig.GetSection("webFarms")
$webFarms = $WebFarmSection.GetCollection()
# Create a new webfarm and assign a name.
$webFarm = $WebFarms.CreateElement("webFarm")
$webFarm.SetAttributeValue("name", "MyNewWebFarmName")
# Add it to the parent element
$webFarms.Add($webFarm)
# get Webfarm server collection
$servers = $webFarm.GetCollection()
# add server
$serverBlue = $servers.CreateElement("server")
$routingBlue = $serverBlue.GetChildElement("applicationRequestRouting")
$routingBlue.SetAttributeValue("httpPort", "8001")
$serverBlue.SetAttributeValue("address", "MyNewWebFarmName-blue")
$servers.Add($serverBlue)
# Save changes
$iisManager.CommitChanges()
有两件事值得一提:
调用
$iisManager.CommitChanges()
后对象进入只读模式。在进行任何新更改之前,您需要重新实例化所有对象。如果您决定在您的网络场中创建一个新服务器,如果您为该服务器分配一个名称然后尝试访问其端口设置,您将 运行 遇到问题。您需要做的是先分配端口,然后分配名称以及是否为enabled/disabled。否则,它会抛出
System.AccessViolationException: Attempted to read or write protected memory.
错误。
让我知道进展如何!