首页 > 代码库 > 在IIS上发布并运行ASP.NET Core

在IIS上发布并运行ASP.NET Core

英文原文地址:https://weblog.west-wind.com/posts/2016/Jun/06/Publishing-and-Running-ASPNET-Core-Applications-with-IIS
 
当构建一个ASP.NET Core应用程序并且计划将其运行在IIS中时,你会发现Core应用程序和之前版本的ASP.NET程序在IIS中的运行方式是完全不一样的。

在本篇文章中,你可以了解ASP.NET Core是如何运行在IIS上下文中的,怎样才能把ASP.NET Core应用程序发布在IIS上。

IIS和ASP.NET Core

关于ASP.NET Core,最重要的一点就是它托管于一个独立运行的控制台程序。它不用托管在IIS中,也不依赖IIS。ASP.NET Core应用程序有自托管的Web服务器,并且可以使用这个自托管的服务器实例在内部处理请求。

不过,你可以将IIS作为ASP.NET Core应用的前置代理。这是因为Kestrel是一个最基础的Web服务器,它并不像IIS这样一个完整Web服务器,有一些特性Kestrel是不支持的。为了提供80/443前置端口,推荐在Windows平台上使用IIS,因为kestrel无法直接提供这种使用方式。对于IIS(或其他反向代理)来说,即便是在ASP.NET Core应用程序中,也仍将作为服务器的一部分扮演着重要的角色。现在我们一起看一下IIS是如何和ASP.NET Core应用相配合的。
 

经典的ASP.NET托管

在正式了解ASP.NET Core托管前,让我们回顾一下运行ASP.NET应用程序经典ASP.NET模式。
 
技术分享
 
在经典ASP.NET应用程序中,所有一切都托管在IIS工作进程中(w3wp.exe),这也被称为IIS应用程序池。ASP.NET程序被托管在应用程序池中,并且被按照IIS内建的ASP.NET托管特性所实例化。当请求从http.sys传入到ASP.NET应用程序管道时,本地运行时管理器会实例化一个代表应用程序的.NET运行时,同时引入HttpRuntime对象用来处理这个请求。来自http.sys的请求被派送到对应的应用程序池和HttpRuntime实例的托管站点。

ASP.NET Core与IIS

ASP.NET Core则完全不同,它并不是运行在IIS的工作进程中,而是独立运行的。它运行于控制台应用程序之中,控制台中则运行了Kestrel Web服务器组件。Kestrel作为一款.NET Web服务器的实现,它在吞吐量性能方面做了很多工作。它可以快速将来自网络的请求接入到应用程序中,但是它仅仅是一个最基本的Web服务器。它没有类似IIS的Web管理服务,也没有IIS那么多的功能。

当选择在Windows平台上运行时,将IIS作为Kestrel的前置服务器可以获得额外一些基础功能。比如通过主机头的80/443端口转发、处理生命周期管理和证书管理等。

下图展示了IIS作为Web前置服务器与运行的ASP.NET Core应用程序的关系:
 
技术分享
 

ASP.NET Core程序独立运行在控制台应用程序中,并通过dotnet运行时命令调用。它并没有被加载到IIS工作进程中,但是IIS却加载了名为AspNetCoreModule的本地Module,这个Module用于执行外部的控制台程序。

AspNetCoreModule是作为ASP.NET Core Server Hosting Bundle的一部分被安装在服务器上的。

当安装ASP.NET核心服务器托管捆绑(或在开发机上安装.NET Core SDK)时,AspNetCoreModule就会出现在IIS的本地Module列表中:
 
技术分享
 

AspNetCoreModule作为IIS本地Module,在IIS处理管道周期的最开始就能hook到请求,紧接着就将所有请求重定向到后端的ASP.NET Core程序。这里是所有的请求,也就是说即便是顶级的映射Handler,比如aspx。这些都将从IIS管道中分流到ASP.NET Core中处理。这意味着,没法简单的将ASP.NET Core和其他框架放在同一个站点(虚拟目录)中。这看起来有点退步了,因为在此之前可以混合使用不同框架。

这里IIS站点(虚拟目录)仍然需要配一个IIS应用程序池,不过这个应用程序池应当使用No Managed Code。这里应用程序池仅仅作为前置代理接受请求,所以它不用实例化.NET运行了。
 
技术分享
 

AspNetCoreModule的作用是确保在第一个请求来之前你的程序已经加载好了,并且保证ASP.NET Core在由于某些原因崩溃后的重启工作。这实际上和被WAS(Windows Activation Service)管理的经典ASP.NET程序是一样的。

当程序运行起来后,AspNetCoreModule会处理接入的Http请求,并将其路由到ASP.NET Core程序中。

因此,来自Web的请求进入到内核模式,由http.sys驱动程序路由到IIS的主要端口(80)或SSL端口(443)上。请求接着被转发到ASP.NET Core程序配置的非80/443端口中。从本质上来说,IIS在这里扮演的是反向代理的角色,它仅仅简单的将请求转发到运行在Kestrel Web服务器上不同端口的ASP.NET Core程序里。

Kestrel接收到请求,并将其推送到ASP.NET Core的中间件处理管道中,该处理管道稍后将处理这个请求并将请求传入到应用程序逻辑代码中。HTTP的输出结果会再次传回到IIS,接着IIS将结果通过Internet返回给初始化这个请求的客户端,它可能是浏览器、手机客户端或者一个应用程序。

AspNetCoreModule配置在位于应用程序根目录的web.config文件中。配置文件定义了用来加载.NET Core程序的启动命令(dotnet)和参数(应用程序主dll)。web.config文件中的配置标明了需要加载的应用程序模块和启动DLL的根目录。

下面就是web.config的例子:
 
 1 <?xml version="1.0" encoding="utf-8"?><configuration> 2   <!-- 3     Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380 4   --> 5   <system.webServer> 6     <handlers> 7       <add name="aspNetCore" path="*" verb="*" 8         modules="AspNetCoreModule" resourceType="Unspecified" /> 9     </handlers>10     <aspNetCore processPath="dotnet" arguments=".\AlbumViewerNetCore.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />11   </system.webServer>12 </configuration>
 
这里可以看到AspNetCoreModule引用了dotnetexe和.NET Core程序中包含Main方法编译入口点DLL。

还需要IIS么?

前面已经讨论过在Windows上运行ASP.NET Core时,推荐将IIS作为前置代理。然而直接通过IP地址和端口也是可以访问Kestrel的,但还是有理由可以说服你不在生产环境中直接暴露应用程序。

首先,当你在单个服务器上运行多个应用程序,并且需要共享80和443端口时,直接运行Kestrel就不行了。Kestrel不支持多个80端口绑定到单个IP地址的主机头路由方式。抛开IIS(实际上是http.sys),目前为止单独使用Kestrel是无法实现的(并且我想这也不在计划中)。

运行在IIS中的AspNetCoreModule同时提供了必要的进程管理措施,它可以确保程序在首次访问前已经被加载好,也可以确保程序持续运行即便在崩溃后也可以重新启动。AspNetCoreModule提供的必要进程管理来保证AspNetCore程序即便是在崩溃之后也始终可用。

让IIS处理SSL请求也是很好的选择,通过IIS的证书存储区设置证书,并让IIS正确处理SSL认证。从IIS出来到应用程序的请求可以是一个非安全的HTTP请求。这意味着只有一个前置的IIS服务器需要证书,即便是你后面有多个真正提供http内容的服务器。

IIS还可以提供静态文件服务、静态文件的gzip压缩、静态文件缓存、Url重定向和IIS本地可以提供的其他功能。IIS实际上在处理非应用程序请求时即好又高效,所以很值得利用IIS的这些优势。让IIS处理它擅长的任务,让剩下的动态任务传递给ASP.NET Core程序来处理。

当你将ASP.NET Core部署在Windows平台上时,你会希望使用AspNetCoreModule的。

不要把IIS作为开发时服务器

我经常会遇到类似这种问题:

我可以像之前开发经典ASP.NET Web应用程序那样在IIS中运行和调试ASP.NET Core程序么?

让我们先把这个问题放一边,几乎没有理由在开发过程中使用IIS。的确,在过去,完全在IIS中开发是有必要的,因为IIS和IIS Express两者在某些方面存在一些不同的行为。

然而,在使用ASP.NET Core的情况下就完全没有理由再考虑在开发过程中使用IIS。这是为什么?因为ASP.NET Core程序并不是运行在IIS内部的。无论是从IIS或IIS Express中的调用或直接从命令行使用dotnet run命令,实际运行的都是同一套代码并且大多数情况下是在完全相同的运行环境中。在IIS内部运行就不能简单的使用命令行环境下模拟了。

唯一可能需要在IIS下运行的一种原因就是:IIS提供的一些依据HTTP服务的东西,是真正和ASP.NET Core的处理相分离的。但是,即便是这样,这些特性也不会在调试应用程序上下文时用到。

“运行”IIS

在调试环境中,不能“仅仅运行IIS”,这是因为ASP.NET Core程序必须得先发布,然后才能执行。开发文件夹中并没有包含所有运行程序所需的文件。当选择“调试”或“运行”程序时,首先会将程序发布到一个单独的地方,再从那里运行。由于这个原因,你Visual Studio的例子中就看不到IIS选项了。

如果非要在IIS下运行,那么你得先将程序发布到本地文件夹中,然后将该文件夹配置为IIS的虚拟目录或站点,接着就可以运行了。

向IIS发布ASP.NET Core程序

为了在IIS下运行程序,第一步就是要将应用程序发布出来。现在,提供了两种发布方式:

  • 使用dotnet publish命令
  • 使用Visual Studio的发布功能

使用 dotnet publish

dotnet publish构建程序并将一个可运行、自托管的项目版本拷贝到磁盘的新位置。你可以指定一个输出文件夹作为发布文件的存放处。这与在经典ASP.NET中将Web站点发布到一个临时文件夹类似。在ASP.NET Core中显示选择在某个路径下发布程序,这样文件不再会隐藏,也不会四处复制。

典型的发布命令可能是这样的:
 
技术分享

这表示将程序发布到c:\temp\AlbumViewerWeb。

打开这个文件夹,可以看到这里面包含了原始的程序结构和所有依赖的nuget程序集。
 
技术分享

IIS托管发布文件夹指导

当应用程序发布后,将其复制到服务器上(通过FTP或其他方式),接着就可以将IIS和这个文件关联起来。

接下来先创建一个虚拟应用程序目录:
 
技术分享
 

要注意我创建的这个AspNetCore应用程序池,要把它的.NET运行时设置为前面所说的非托管代码。

现在,可以浏览网站或虚拟目录,应用程序同时也就跑起来了。

现在把本地发布的网站拷贝到Web服务器上(通过FTP或直接拷贝文件或者其他方式),设置好站点或虚拟目录就可以了。

从Visual Studio发布

dotnet publish这步将整个项目拷贝到另一个文件夹中,但这实际上并没有将项目发布为网站(目前,还需要稍微等一会)。

使ASP.NET Core应用程序增量发布是非常重要的,你需要使用MsDeploy来管理这么多的依赖项,MsDeploy是作为Visual Studio Web发布功能的一部分。

目前,Visual Studio的图形工具还是有缺陷的的,但是底层的功能是支持的。这里按照我做出的一些调整,然后就可以使用了。

当你使用Visual Studio的RC2 Web工具和发布对话框时,你发现无法创建针对IIS的发布概要。这里只有文件和基于Azure的发布选项,没法直接通过界面创建新的Web网站发布。

不过,你可以自己创建.pubxml并将其放在项目中\Properties\PublishProfiles文件夹内,这样就可以欺骗开发工具了。

特定版本的解决方法

很显然,这个问题在RC2之后的版本会得到更新。所以,当你在一个月后读到本文的这些步骤时,请先确认下是否可以直接通过Visual Studio界面完成IIS发布。

在ASP.NET Core Web项目中创建“手动概要”:

  • 创建文件夹\Properties\PublishProfiles
  • 创建文件<MyProfile>.pubxml
你可以从非ASP.NET Core项目中拷贝一个已有的.pubxml文件。下面是IIS下的概要文件例子:
 
 1 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 2   <PropertyGroup> 3     <WebPublishMethod>MSDeploy</WebPublishMethod> 4     <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration> 5     <LastUsedPlatform>Any CPU</LastUsedPlatform> 6     <SiteUrlToLaunchAfterPublish>http://samples.west-wind.com/AlbumViewerCore/index.html</SiteUrlToLaunchAfterPublish> 7     <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish> 8     <ExcludeApp_Data>False</ExcludeApp_Data> 9     <PublishFramework>netcoreapp1.0</PublishFramework>10     <UsePowerShell>True</UsePowerShell>11     <EnableMSDeployAppOffline>True</EnableMSDeployAppOffline>12     <MSDeployServiceURL>https://publish.west-wind.com</MSDeployServiceURL>13     <DeployIisAppPath>samples site/albumviewercore</DeployIisAppPath>14     <RemoteSitePhysicalPath />15     <SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>16     <MSDeployPublishMethod>RemoteAgent</MSDeployPublishMethod>17     <EnableMSDeployBackup>False</EnableMSDeployBackup>18     <UserName>username</UserName>19     <_SavePWD>True</_SavePWD>20     <ADUsesOwinOrOpenIdConnect>False</ADUsesOwinOrOpenIdConnect>21     <AuthType>NTLM</AuthType>22   </PropertyGroup>23 </Project>

 

AuthType NTLM修正

注意文件底部的<AuthType>NTLM</AuthType>键。此键非常重要,如果错误发布操作将无法执行。如果是从一个现有的文件拷贝过来的,请确保此键的设置。

当创建好.pubxml文件后,现在打开Visual Studio的发布对话框,如下所示:
 
技术分享
 

此时,你就可以将网站发布到远程服务器上的IIS中,并且可以增量更新内容。

这是目前为止对IIS托管和发布的总结,还没有文档描述这些,整个发布过程有些粗糙。显然微软知道这件事,也会在ASP.NET Core的RTM版本中修复。

在IIS上发布并运行ASP.NET Core