在 Windows Server 故障转移群集中配置 IIS 万维网发布服务

本文介绍如何在 Windows Server 故障转移群集 (WSFC) 中配置Microsoft Internet Information Services (IIS) 万维网发布服务 (W3SVC) 。

原始产品版本: Windows Server 2008 及更高版本、Internet Information Services 8.0 及更高版本
原始 KB 编号: 970759

简介

本文中的过程仅适用于万维网发布服务。 有关如何在故障转移群集中配置 FTP 发布服务的说明,请参阅 如何在 Windows Server 故障转移群集中为 IIS 配置 FTP

更多信息

在 Internet Information Services 的早期版本中,Microsoft 提供了通用资源监视器组件,以支持使用 Microsoft 群集基础结构的高可用性 Web 服务器实例。 但是,需要自定义代码才能充分实现此类解决方案的潜力。 此外,Microsoft 提供的通用脚本不能满足客户需求。 若要在使用 Windows Server 故障转移聚类分析的群集环境中配置 IIS 7.0 或更高版本,必须使用自定义 (脚本) 代码来启用此类高可用性方案。 执行此操作时,用户可以自定义设置以满足其要求。 这使他们能够完全控制 Web 应用程序的高可用性集成。 此外,IIS 7.0 中引入的用于管理和监视的脚本接口提供了比之前提供的脚本更丰富的环境。

注意

IIS 7.0 安装文件错误地包含 IIS 6.0 中用于 IIS 群集管理任务的 Clusweb.vbsClusftp.vbs 脚本文件。 请勿将这些脚本与 IIS 7.0 或更高版本配合使用。

我们建议管理员仔细评估使用网络负载均衡 (NLB) 作为提高 Web 应用程序的可伸缩性和可用性的主要和首选方法,其中多台服务器运行 IIS 7.0 或更高版本,而不是使用故障转移聚类分析。 NLB 的优势之一是,所有服务器都可以积极参与同时处理传入的超文本传输协议 (HTTP) 请求。 另一个好处是,在 NLB IIS 环境中,支持滚动更新和回滚要容易得多,同时仍提供 Web 应用程序的高可用性。 有关在 NLB 环境中使用 IIS 7.0 或更高版本的详细信息,请参阅以下文章:

请务必考虑,通过聚类分析 IIS 服务聚类分析 IIS 服务并不总是保证 Web 应用程序的高可用性解决方案。 虽然 IIS 服务 (WWW 服务) 可能已启动并运行,但特定应用程序池的托管进程可能已终止,或者应用程序可能引发内部服务器 HTTP 错误。 使用自定义脚本群集化 Web 应用程序并监视其运行状况是使用故障转移聚类分析实现高可用性 IIS 群集的正确建议方法。 下面是一个示例脚本,用于监视应用程序池的状态,以确定它是否已启动。

若要使用故障转移聚类分析为 IIS 7.0 或更高版本的 Web 服务器配置高可用性,请执行以下步骤。 下面更详细地介绍了步骤 3 到 7。 本文后面的示例脚本可用作 IIS 7.0 或更高版本的示例。

  1. 在所有群集节点上安装 Web 服务器角色。 有关详细信息,请参阅 IIS 7 部署指南
  2. 在所有群集节点上安装故障转移聚类分析功能并创建群集。 有关详细信息,请参阅 故障转移群集部署指南
  3. 设置将用于 IIS 共享配置的文件共享。
  4. 在所有群集节点上配置 IIS 共享配置。
  5. 为所有群集节点上的共享配置配置 IIS 脱机文件。
  6. 配置网站 (包括关联的应用程序池) ,并指定其内容在一个群集节点上的位置。
  7. 通过在故障转移聚类分析中创建通用脚本,为网站配置高可用性。

设置将用于 IIS 共享配置的文件共享

  1. 创建将访问将用于 IIS 共享配置的共享的用户。
  2. 创建文件共享。 此共享将用于存储将在所有群集节点上的 IIS 之间共享的 IIS 共享配置。 有多个选项:
  3. 设置在步骤 2 中创建的共享的权限。 为在步骤 1 中创建的用户授予文件共享的“完全控制”权限和 NTFS 权限。
  4. 确认所有群集节点都可以浏览到文件共享。 文件共享的路径为 \\<fileserver>\<share>

在所有群集节点上配置 IIS 共享配置

注意

Windows 2008 Server 上的 IIS 共享配置出现问题,因为 缺少 的 Application Host Helper Service特权。 若要使共享配置正常工作,在 Windows 2008 Server 上设置 IIS 共享配置时,必须执行以下步骤。

  1. 打开管理命令提示符。

  2. 运行以下命令:

    net stop apphostsvc
    
  3. 运行以下命令:

    sc privs apphostsvc SeChangeNotifyPrivilege/SeTcbPrivilege/SeImpersonatePrivilege
    
  4. 运行以下命令:

    net start apphostsvc
    

在群集中的每个 Windows 2008 Server 上完成这些步骤后,继续设置 IIS 共享配置,如本部分所述。

在其中一个群集节点上,将共享配置导出到文件共享:

  1. 导航到 “管理工具”,然后选择“ Internet Information Services (IIS) 管理器”。
  2. 在左窗格中,选择 服务器名称 节点。
  3. 双击“ 共享配置” 图标。
  4. 在“共享配置”页上,在“操作”窗格中选择“导出配置”, (右窗格) 将配置文件从本地计算机导出到另一个位置。
  5. 在“ 导出配置 ”对话框中,在“物理路径”框中键入文件共享 (\\<fileserver>\<share>) 的路径
  6. 选择“ 连接方式”,然后键入有权访问存储共享配置的共享的用户帐户的用户名和密码,然后选择“ 确定”。 此帐户将用于访问共享。 应使用不是域管理员的受限 Active Directory 帐户。
  7. 在“ 导出配置 ”对话框中,键入将用于保护加密密钥的密码,然后选择“ 确定”。
  8. “共享配置”页上,选中“启用共享配置检查”框。
  9. 键入之前输入的物理路径、用户帐户和密码,然后在“操作”窗格中选择“应用”。
  10. 在“ 加密密钥密码 ”对话框中,键入之前设置的加密密钥密码,然后选择“ 确定”。
  11. 在“ 共享配置 ”对话框中,选择“ 确定”。
  12. 选择“确定”。

在每个其他群集节点上,使用刚刚导出到文件共享的共享配置:

  1. 导航到 “管理工具”,然后选择“ Internet Information Services (IIS) 管理器”。
  2. 选择 服务器名称 节点。
  3. 双击“ 共享配置” 图标。
  4. “共享配置”页上,选中“启用共享配置检查”框。
  5. 键入文件共享的物理路径 (\\<fileserver>\<share>) 、用户帐户和之前输入的密码,然后在“操作”窗格中选择“应用”。
  6. 在“ 加密密钥密码 ”对话框中,键入之前设置的加密密钥密码,然后选择“ 确定”。
  7. 在“ 共享配置 ”对话框中,选择“ 确定”。
  8. 选择“确定”。

注意

有关如何在 IIS 中设置共享配置的详细信息,请访问 共享配置

为所有群集节点上的共享配置配置 IIS 脱机文件

在每个群集节点上,启用脱机文件:

  1. 安装桌面体验

    1. 导航到“管理工具”,然后选择“服务器管理器”。
    2. 在左窗格中,选择“ 功能”。
    3. 在右窗格中选择“ 添加功能 ”。
    4. 根据你的 Windows 版本,执行以下操作之一:
      • 有关Windows Server 2016,请访问安装具有桌面体验的服务器
      • 对于 Windows Server 2102 和 2012 R2,选择功能列表中的“用户界面和基础结构”下的“桌面体验”。
      • 对于 Windows Server 2008 和 2008 R2,请选择“ 桌面体验”。
    5. 选择“ 安装” 以安装桌面体验。
    6. 重启计算机。
  2. 执行下列操作之一:

    • 对于Windows Server 2012、2012 R2 和 2016,请在控制面板中选择“同步中心”,然后选择“管理脱机文件”。
    • 对于 Windows Server 2008 和 2008 R2,请在控制面板中选择“脱机文件”。
  3. 选择 “启用脱机文件”。 此时不要重新启动计算机。

  4. 确保缓存设置为只读。 若要执行此操作,请在提升的 cmd 提示符下运行以下命令:

    REG ADD "HKLM\System\CurrentControlSet\Services\CSC\Parameters" /v ReadOnlyCache /t REG_DWORD /d 1 /f
    
  5. 重启计算机。

  6. 从计算机浏览到文件服务器。 右键单击包含 IIS 共享配置的共享,然后选择“ 始终脱机可用”。

    注意

    如果将文件共享设置为在托管 IIS 节点的同一故障转移群集上高度可用,则右键单击共享时,如果所在的群集节点承载高度可用的文件服务器,则不会显示“ 始终可用脱机 ”选项。 需要将高可用文件服务器应用程序移到另一个节点。

  7. 在 控制面板 中,打开“脱机文件”。 选择“ 打开同步中心”,然后选择“ 计划”。

  8. 每天或根据要求计划脱机文件同步。 还可以将脱机同步配置为每隔几分钟运行一次。 即使未设置计划程序,更改 Applicationhost.config 文件中的内容时,更改也会反映在 Web 服务器上。

注意

有关如何在 IIS 中为共享配置配置脱机文件的详细信息,请参阅共享 配置的脱机文件

配置网站,并指定其内容在一个群集节点上的位置

查找拥有群集磁盘资源的群集节点,网站内容文件将保留在其中:

  1. 导航到 “管理工具”,然后选择“ 故障转移群集管理器”。
  2. 连接到群集。 如果位于其中一个群集节点上,群集将自动显示在列表中。
  3. “存储”下,找到网页内容将驻留的磁盘资源。 为此,请展开磁盘资源的存储树。 请确保群集上的任何其他高可用性应用程序不使用存储。 可在“可用存储”下找到 该存储
  4. 此资源处于联机状态的群集节点。 你将在该群集节点上配置 IIS。
  5. 群集磁盘资源名称。

你将将它用于内容文件。 在资源处于联机状态的群集节点上,将 Web 服务器配置为将共享磁盘用于网站内容:

  1. 导航到 “管理工具”,然后选择“ Internet Information Services (IIS) 管理器”。
  2. 在左窗格中,展开服务器名称节点。
  3. 展开 “网站”,然后在“ 网站”下,选择要配置的站点。
  4. 在右窗格中,选择“管理网站”下的“高级设置”。
  5. 在“常规设置”下找到“物理路径”属性,然后键入网站内容文件所在的位置。 它是在上一过程的步骤 5 中记录的群集磁盘资源的位置。
  6. 选择“确定”。

通过在故障转移群集管理器中创建通用脚本来配置网站的高可用性

对于配置 IIS Web 服务器高可用性的最后一步,请设置用于监视网站的网站和应用程序池的通用脚本资源:

  1. 在每个群集节点上,将本文末尾提供的脚本复制到 Windows\System32\inetsrv\Clusweb7.vbs

  2. 默认情况下,脚本监视名为 “默认网站” 的网站和名为 DefaultAppPool 的应用程序池。 如果这些不是正确的网站和应用程序池,请 SITE_NAME and APP_POOL_NAME 更改变量。 请确保脚本中的所有群集节点上都存在相同的网站和应用程序池。

    注意

    名称区分大小写。

  3. 导航到 “管理工具”,然后选择“ 故障转移群集管理器”。

  4. 连接到群集。 如果位于其中一个群集节点上,群集将自动显示在列表中。

  5. 执行下列操作之一:

    • 对于Windows Server 2012 2012 R2 和 2016,右键单击“角色”,然后选择“配置角色”以创建它。
    • 对于 Windows Server 2008 和 2008 R2,右键单击群集,然后选择“ 配置服务或应用程序”。 向导创建高可用性工作负荷。
  6. 选择“ 通用脚本”。

  7. %systemroot%\System32\Inetsrv\clusweb7.vbs中选择脚本文件。

  8. 将客户端接入点 (CAP) 名称设置为客户端将用于连接到高可用性网站的网站名称。 指定要用于网站 CAP 的静态 IP。 如果使用动态主机配置协议 (DHCP) ,则不会显示此选项。

  9. “选择存储” 步骤中,选择网站内容文件所在的群集共享磁盘。 群集上的任何其他高可用性应用程序都应使用存储。

    注意

    如果用于 IIS 共享配置的文件共享托管在同一群集上,则应在此处使用不同的磁盘资源。

  10. 确认设置后,向导将创建群集组、群集资源以及资源之间的依赖关系,然后将资源联机。

    注意

    若要在同一故障转移群集上托管多个高可用性网站,请按照上述步骤进行操作。 但是,请对每个网站和不同的群集共享存储使用不同的脚本文件。 例如,在 中 %systemroot%\System32\Inetsrv,对第一个网站使用 clusweb7.vbs ,对第二个网站 使用clweb7-2.vbs ,对第三个网站使用 clweb7-3.vbs ,依此类比。 每个脚本文件监视不同的网站和应用程序池。

以下脚本仅用于示例,不受 Microsoft 显式支持。 在 IIS 7.0 或更高版本的群集环境中使用此脚本的风险由你自己承担。

'<begin script sample>
'This script provides high availability for IIS websites
'By default, it monitors the "Default Web Site" and "DefaultAppPool"
'To monitor another website, change the SITE_NAME below
'To monitor another application pool, change the APP_POOL_NAME below
'More thorough and application-specific health monitoring logic can be added to the script if needed

Option Explicit

DIM SITE_NAME
DIM APP_POOL_NAME
Dim START_WEB_SITE
Dim START_APP_POOL
Dim SITES_SECTION_NAME
Dim APPLICATION_POOLS_SECTION_NAME
Dim CONFIG_APPHOST_ROOT
Dim STOP_WEB_SITE

'Note:
'Replace this with the site and application pool you want to configure high availability for
'Make sure that the same website and application pool in the script exist on all cluster nodes. Note that the names are case-sensitive.
SITE_NAME = "Default Web Site"
APP_POOL_NAME = "DefaultAppPool"

START_WEB_SITE = 0
START_APP_POOL = 0
STOP_WEB_SITE  = 1
SITES_SECTION_NAME = "system.applicationHost/sites"
APPLICATION_POOLS_SECTION_NAME = "system.applicationHost/applicationPools"
CONFIG_APPHOST_ROOT = "MACHINE/WEBROOT/APPHOST"

'Helper script functions
'Find the index of the website on this node
Function FindSiteIndex(collection, siteName)
    Dim i
    FindSiteIndex = -1

    For i = 0 To (CInt(collection.Count) - 1)
        If collection.Item(i).GetPropertyByName("name").Value = siteName Then
            FindSiteIndex = i
            Exit For
        End If
    Next
End Function

'Find the index of the application pool on this node
Function FindAppPoolIndex(collection, appPoolName)
    Dim i
    FindAppPoolIndex = -1

    For i = 0 To (CInt(collection.Count) - 1)
        If collection.Item(i).GetPropertyByName("name").Value = appPoolName Then
            FindAppPoolIndex = i
            Exit For
        End If
    Next
End Function

'Get the state of the website
Function GetWebSiteState(adminManager, siteName)

    Dim sitesSection, sitesSectionCollection, siteSection, index, siteMethods, startMethod, executeMethod
    Set sitesSection = adminManager.GetAdminSection(SITES_SECTION_NAME, CONFIG_APPHOST_ROOT)
    Set sitesSectionCollection = sitesSection.Collection

    index = FindSiteIndex(sitesSectionCollection, siteName)
    If index = -1 Then
        GetWebSiteState = -1
    End If

    Set siteSection = sitesSectionCollection(index)
    GetWebSiteState = siteSection.GetPropertyByName("state").Value
End Function

'Get the state of the ApplicationPool
Function GetAppPoolState(adminManager, appPool)
    Dim configSection, index, appPoolState

    set configSection = adminManager.GetAdminSection(APPLICATION_POOLS_SECTION_NAME, CONFIG_APPHOST_ROOT)
    index = FindAppPoolIndex(configSection.Collection, appPool)

    If index = -1 Then
        GetAppPoolState = -1
    End If

    GetAppPoolState = configSection.Collection.Item(index).GetPropertyByName("state").Value
End Function

'Start the w3svc service on this node
Function StartW3SVC()
    Dim objWmiProvider
    Dim objService
    Dim strServiceState
    Dim response

    'Check to see if the service is running
    set objWmiProvider = GetObject("winmgmts:/root/cimv2")
    set objService = objWmiProvider.get("win32_service='w3svc'")
    strServiceState = objService.state

    If ucase(strServiceState) = "RUNNING" Then
        StartW3SVC = True
    Else
        'If the service is not running, try to start it
        response = objService.StartService()

        'response = 0  or 10 indicates that the request to start was accepted
        If ( response <> 0 ) and ( response <> 10 ) Then
            StartW3SVC = False
        Else
            StartW3SVC = True
        End If
    End If
End Function

'Start the application pool for the website
Function StartAppPool()
    Dim ahwriter, appPoolsSection, appPoolsCollection, index, appPool, appPoolMethods, startMethod, callStartMethod
    Set ahwriter = CreateObject("Microsoft.ApplicationHost.WritableAdminManager")

    Set appPoolsSection = ahwriter.GetAdminSection(APPLICATION_POOLS_SECTION_NAME, CONFIG_APPHOST_ROOT)
    Set appPoolsCollection = appPoolsSection.Collection
    index = FindAppPoolIndex(appPoolsCollection, APP_POOL_NAME)
    Set appPool = appPoolsCollection.Item(index)

    'See if it is already started
    If appPool.GetPropertyByName("state").Value = 1 Then
        StartAppPool = True
        Exit Function
    End If

    'Try To start the application pool
    Set appPoolMethods = appPool.Methods
    Set startMethod = appPoolMethods.Item(START_APP_POOL)
    Set callStartMethod = startMethod.CreateInstance()
    callStartMethod.Execute()

    'If started return true, otherwise return false
    If appPool.GetPropertyByName("state").Value = 1 Then
        StartAppPool = True
    Else
        StartAppPool = False
    End If
End Function

'Start the website
Function StartWebSite()
    Dim ahwriter, sitesSection, sitesSectionCollection, siteSection, index, siteMethods, startMethod, executeMethod
    Set ahwriter = CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    Set sitesSection = ahwriter.GetAdminSection(SITES_SECTION_NAME, CONFIG_APPHOST_ROOT)
    Set sitesSectionCollection = sitesSection.Collection
    index = FindSiteIndex(sitesSectionCollection, SITE_NAME)
    Set siteSection = sitesSectionCollection(index)

    if siteSection.GetPropertyByName("state").Value = 1 Then
        'Site is already started
        StartWebSite = True
        Exit Function
    End If

    'Try to start site
    Set siteMethods = siteSection.Methods
    Set startMethod = siteMethods.Item(START_WEB_SITE)
    Set executeMethod = startMethod.CreateInstance()
    executeMethod.Execute()

    'Check to see if the site started, if not return false
    If siteSection.GetPropertyByName("state").Value = 1 Then
        StartWebSite = True
    Else
        StartWebSite = False
    End If
End Function

'Stop the website
Function StopWebSite()
    Dim ahwriter, sitesSection, sitesSectionCollection, siteSection, index, siteMethods, startMethod, executeMethod, autoStartProperty
    Set ahwriter = CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    Set sitesSection = ahwriter.GetAdminSection(SITES_SECTION_NAME, CONFIG_APPHOST_ROOT)
    Set sitesSectionCollection = sitesSection.Collection
    index = FindSiteIndex(sitesSectionCollection, SITE_NAME)
    Set siteSection = sitesSectionCollection(index)

    'Stop the site
    Set siteMethods = siteSection.Methods
    Set startMethod = siteMethods.Item(STOP_WEB_SITE)
    Set executeMethod = startMethod.CreateInstance()
    executeMethod.Execute()
End Function

'Cluster resource entry points. More details here:
'http://msdn.microsoft.com/en-us/library/aa372846(VS.85).aspx
'Cluster resource Online entry point
'Make sure the website and the application pool are started
Function Online( )
    Dim bOnline
    'Make sure w3svc is started
    bOnline = StartW3SVC()

    If bOnline <> True Then
        Resource.LogInformation "The resource failed to come online because w3svc could not be started."
        Online = False
        Exit Function
    End If

    'Make sure the application pool is started
    bOnline = StartAppPool()
    If bOnline <> True Then
        Resource.LogInformation "The resource failed to come online because the application pool could not be started."
        Online = False
        Exit Function
    End If

    'Make sure the website is started
    bOnline = StartWebSite()
    If bOnline <> True Then
        Resource.LogInformation "The resource failed to come online because the web site could not be started."
        Online = False
        Exit Function
    End If

    Online = true
End Function

'Cluster resource offline entry point
'Stop the website
Function Offline( )
    StopWebSite()
    Offline = true
End Function

'Cluster resource LooksAlive entry point
'Check for the health of the website and the application pool
Function LooksAlive( )
    Dim adminManager, appPoolState, configSection, i, appPoolName, appPool, index
    i = 0
    Set adminManager  = CreateObject("Microsoft.ApplicationHost.AdminManager")
    appPoolState = -1

    'Get the state of the website
    if GetWebSiteState(adminManager, SITE_NAME) <> 1 Then
        Resource.LogInformation "The resource failed because the " & SITE_NAME & " web site is not started."
        LooksAlive = false
        Exit Function
    End If

    'Get the state of the Application Pool
    if GetAppPoolState(adminManager, APP_POOL_NAME) <> 1 Then
         Resource.LogInformation "The resource failed because Application Pool " & APP_POOL_NAME & " is not started."
         LooksAlive = false  
         Exit Function
    End if

    'Web site and Application Pool state are valid return true
    LooksAlive = true
End Function

'Cluster resource IsAlive entry point
'Do the same health checks as LooksAlive
'If a more thorough than what we do in LooksAlive is required, this should be performed here
Function IsAlive()
    IsAlive = LooksAlive
End Function

'Cluster resource Open entry point
Function Open()
    Open = true
End Function

'Cluster resource Close entry point
Function Close()
    Close = true
End Function

'Cluster resource Terminate entry point
Function Terminate()
    Terminate = true
End Function
'<end script sample>