IIS 应用程序池未能正常启动而没有报错

IIS ApplicationPool fails to start properly without error

我们的 IIS 部署遇到了一个奇怪的问题。

ApplicationPools 有时无法正常启动,但在启动时不会抛出错误。 应用程序池中唯一包含的站点没有响应(甚至没有返回 500 等,只是在一段时间后超时)。

就 IIS 而言,ApplicationPool 和站点已启动并且 运行(未停止)。

重新启动站点或 ApplicationPool 无法解决问题。

但是,删除站点和 ApplicationPool 并使用相同的属性重新创建它确实可以解决问题。

一旦任何 ApplicationPool 达到此状态,解决此问题的唯一方法(据我们所知)就是重新创建整个 ApplicationPool。

我们很乐意以自动化的方式这样做,但是没有错误可以分别捕获和处理。

一些背景数据:

我们怀疑问题可能是多个 ApplicationPool 启动同时发生(因为它们是由我们的 CI/CD 管道自动触发的)。

现在,我绝不是 IIS 专家,所以我的问题是:

Would it be possible, that many app pool starts (circa 20-60) happening at roughly the same time cause such behaviour?

很难说。应用程序池只是一个空容器,花费时间和限制此数量的主要是您的应用程序代码和依赖项在启动时所做的事情,运行时间和一些 dotnet 预编译开销。

What could I do to investigate this further?

  1. 检查 Windows 文件夹中的 HTTPERR 日志 - 如果您没有看到其他地方记录的请求,可能会提供线索。

  2. 监控 w3wp.exe 进程本身 - 这些是您的应用程序池(也称为“应用程序域”)。他们可能会卡住而不是“正常”崩溃,这听起来像你的情况。

假设您所有的应用程序都正常工作,而您只是想要一种恢复随机故障的方法,试试这个...

当您的应用程序池损坏时,运行 在您的服务器上从 PowerShell 或 ISE(以管理员身份)执行以下操作以查看 运行ning IIS 工作进程:

Get-WmiObject Win32_Process -Filter "name = 'w3wp.exe'" | Select-Object ProcessId,CommandLine

以上输出工作进程 ID 以及用于启动它们的参数。在参数中,您可以看到站点名称 - 使用正确的 ProcessId 和命令 Stop-Process -Force -Id X(将 X 替换为 ProcessId 编号)以强行终止进程。终止进程后尝试访问应用程序是否成功启动?

如果您知道要终止的应用程序池的名称,您可以使用此代码终止进程:

$AppPoolName = 'NAMEOFMYAPPPOOL';
Stop-Process -Force -id (Get-WmiObject Win32_Process -Filter "name = 'w3wp.exe' AND CommandLine like '%-in%$($AppPoolName)%'").ProcessId

(应用程序池名称替换为 NAMEOFMYAPPPOOL,管理员替换为 运行)

如果终止停滞的进程足以让它成功重启,那么编写一个简单的健康检查脚本将相当容易。我会阅读每个站点的绑定,向每个绑定发出 HTTP 请求并确认应用程序池确实是 running/responsive 和 returns 200 OK 响应。如果请求在合理的超时后失败,请尝试终止进程并重新请求 HTTP 请求以重新启动应用程序池。添加一些重试逻辑,并可能在尝试之间添加延迟,以免陷入循环。

只是一个想法 - 尝试为每个应用程序池提供自己的临时文件夹 - 在每个站点 web.config 中配置:

<system.web>
  <compilation tempDirectory="D:\tempfiles\apppoolname" />

启动期间此处的串扰可能是怪异的来源。

问题似乎是由于我们的部署脚本没有等待应用程序池实际处于 Stopped 状态,然后继续删除旧应用程序文件并用新文件替换它们并立即启动再次应用程序池。

我们在今年早些时候注意到了与此相关的问题,当时由于仍在使用文件而无法删除文件,即使在停止 ApplicationPool 之后(我们通过实施重试机制“解决”了这个问题)...

解决方案

停止 ApplicatonPool 后调用以下代码似乎可以解决问题....

$stopWaitCount = 0;
while ((Get-WebAppPoolState -Name $appPool).Value -ne "Stopped" -and $stopWaitCount -lt 12)
{
    $stopWaitCount++
    Write-Log "Waiting for Application-Pool '$appPool' to stop..."
    Start-Sleep -Seconds $stopWaitCount
}

我们在 2 天前实施了这个,此后在 100 多个部署中都没有出现问题。