本文转自:
http://webmodelling.com/webbits/aspnet/aspnet-deploy-iis.aspx
15 Sep 2016. This tutorial will show step by step how to deploy an ASP.NET Core 1 project to Internet Information Server (IIS) on a Windows 2012 R2 production server.
ASP.NET Core 1 hosting is quite different from earlier asp.net versions, this difference is hidden then developing using VS2015 & IISExpress but shows itself very clearly then comes time to deploy to an IIS production server - this tutorial will give you an understanding of the ASP.NET Core 1 hosting system and how to deploy ASP.NET Core 1 to an IIS production server.
Index :
Appendixes :
- Appendix : Create a self hosted ASP.NET Core App
- Appendix : Understanding proxies
- Appendix : Common errors & solutions
- Comments
Main references :
- ASP.NET Core server fundamentals @docs.asp.net
- ASP.NET Core Publishing to IIS @docs.asp.net
- ASP.NET Core Hosting @docs.asp.net
- ASP.NET Core & IIS @Rick Strahl - very good
Understanding ASP.NET Core 1 hosting
In ASP.NET Classic (latest version is 4.6.2) your web application dll was loaded into the IIS worker process, w3wp.exe, together with an instance of the .NET Framework. In IIS each application pool corresponds to 1 worker process, so if 2 web applications shared an application pool then the worker process would load each web application dll and one instance of .NET Framework.
In ASP.NET Core (latest version 1.0) your web application is loaded into it's own separate process together with an instance of Kestrel - together called ASP.NET Core App Host. The ASP.NET Core App Host process is startet by dotnet run, which is executed by ASP.NET Core Module from within IIS. The ASP.NET Core App Host will through Kestrel listen to a specific port and the ASP.NET Core Module will forward requests to that port.
In the above schematic IIS will map (eg. through http host header) an http request to a specific web application. The web application uses a web.config file to configure IIS to use ASP.NET Core Module for this web application. ASP.NET Core Module will use dotnet to load the web application as an ASP.NET Core App Host in an external process setting up a port (default 5000) based communication between the ASP.NET Core Module and the ASP.NET Core App Host. ASP.NET Core Module is then forwarding http requests to ASP.NET Core App Host. Inside of ASP.NET Core App Host the Kestrel part will poll the port and then receiving a request building an array of context relevant objects all collected into HttpContext, which is then made available for the ASP.NET Core App.
While indeed there are other ways to host ASP.NET Core, the above schematic is the current Microsoft recommended way to host ASP.NET Core on a Windows server (note that Kestrel is a cross platform web server and while I often read about how fast Kestrel is, the upcoming Windows only web server called WebListener should be even faster - as WebListener matures Microsoft may change their recommendation to use WebListener on Windows servers).
The ASP.NET Core App Host is running by itself and listening to http requests by itself as well - it will work all by itself. It is NOT necessary to use IIS or another webserver as a frontend (reverse proxy) for the ASP.NET Core App Host. However there are several reasons why the reverse proxy topology recommeded by microsoft is a good idea, here are some : (I probably don't get them all)
- Kestrel does NOT support http host header redirect - so with Kestrel only we can have only one web application on port 80.
- ASP.NET Core module guarantees that the ASP.NET Core App Host is loaded and also restarted if necessary - if manually starting up an ASP.NET Core App Host from the command prompt and the process crashes, you are responsible yourself to startup the app again.
- IIS can handle cashing, SSL and static content compression (I am not confident to which extent Kestrel can do these and there may be a host of other details better handled by IIS).
Prepare IIS & Production server
ASP.NET Core can run on either .NET Framework (latest version 4.6.2) or .NET Core (latest version 1.0). If the ASP.NET Core App is targeting .NET Framework then the .NET Framework runtime must be loaded within the ASP.NET Core App Host process, while if the ASP.NET Core App is targeting .NET Core then the .NET Core runtime must be loaded within the ASP.NET Core App Host process.
Then you publish an ASP.NET Core App from within Visual Studio and that App is targeting .NET Core, then the .NET Core Runtime is added to the published output (together with other library dependencies for that particular app) and will be loaded as part of the ASP.NET Core App Host - this means we do NOT need to install .NET Core Runtime on the production server if hosting only Visual Studio published ASP.NET Core Apps.
Then you publish an ASP.NET Core App using whatever means and that App is targeting .NET Framework, then the .NET Framework is NOT added to the published output (only specific library dependencies for that particular app) - this means that we DO need to install .NET Framework runtime on the production server if hosting ASP.NET Core Apps targeting .NET Framework.
- ASP.NET Core targeting .NET Framework : .NET Framework NEED to be installed on the production server.
- ASP.NET Core targeting .NET Core : .NET Core does NOT necessarily need to be installed on the production server.
-
Install ASP.NET Core Module :
Note that the ASP.NET Core Module installer also contains dotnet so that ASP.NET Core Module can use dotnet to startup an ASP.NET Core App Host.
- Download the ASP.NET Core Module installer from Microsoft here : https://www.microsoft.com/net/download
- Double click on the ASP.NET Core Module installer to install it.
- Confirm that the ASP.NET Core Module is installed :
- You can either use applicationHost.config :
-
- Or you can use IIS Manager :
-
- You can either use applicationHost.config :
-
Install .NET Core SDK :
Installing .NET Core SDK (or alternatively but less flexible .NET Core runtime standalone) is ONLY necessary if you want to compile ASP.NET Core Apps directly on the production server (which indeed you may want) or if you want to host ASP.NET Core Apps that have been published without amending the .NET Core Runtime.
.NET Core SDK contains .NET Core (that is the .NET Core runtime and libraries) as well as the command line version dotnet. With .NET Core SDK it is therefore possible to build & start ASP.NET Core Apps from the command line allowing us among other things to self host an ASP.NET Core App without using a reverse proxy.
- Download the .NET Core SDK installer from Microsoft here : https://www.microsoft.com/net/download
- Double click on the .NET Core SDK installer to install it.
- Confirm that the .NET Core SDK is installed :
-
- shell> cd /
- shell> mkdir testCoreSdk
- shell> cd testCoreSdk
- shell> dotnet new : creates a new C# .NET Core application in current folder.
- shell> dotnet restore : reads the project.json file and downloads all dependencies specified in project.json.
- shell> dotnet run : compiles and starts the .NET Core application.
-
-
-
Install .NET Framework :
Installing .NET Framework is ONLY necessary if you want to build ASP.NET Classic applications OR if you want to build ASP.NET Core Apps targeting .NET Framework (instead of targeting .NET Core).
- Download the .NET Framework installer from Microsoft here : https://www.microsoft.com/net/download
- Double click on the .NET Framework installer to install it.
- Confirm that the .NET Framework is installed :
- Open the Registry Editor.
- Navigate to HKEY_LOCAL_MACHINESOFTWAREMicrosoftNET Framework SetupNDPv4Full
-
- 394806 : .NET Framework 4.6.2
- 394271 : .NET Framework 4.6.1
- See full list of values here.
Prepare you web application
-
Create a new web application, here in Visual Studio :
- Open Visual Studio.
-
-
- In the "New Project" dialog web menu, you have 3 project options in the right pane :
-
-
After Visual Studio have created the project template files, you will need to wait a little bit while Visual Studio automatically downloads listed dependencies in the project.json file.
-
Be sure to know your project.json file :
-
Depending whether your ASP.NET Core App is targeting .NET Framework or .NET Core, you will get slightly different project.json - most significantly the frameworks{} section will be different :
-
-
-
Be sure to know your Program.cs file :
Your ASP.NET Core App Host must load a webserver in its own process, this means that a webserver (typically Kestrel or WebListener) needs to be chosen at application start. With ASP.NET Core 1 RTM, the entry point of the ASP.NET Core App was moved from Startup to Program and now looks like this : (Visual Studio 2015 Update 3 template)
using System.IO; using Microsoft.AspNetCore.Hosting; namespace testNetCore { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); } } }
The Program entry point, Main, is called by dotnet run and starts up the ASP.NET Core App Host as a console application. In Main we instantiate a WebHostBuilder by which we build, configure and startup the ASP.NET Core App Host.
The Visual Studio template added configurations are : (see more WebHostBuilder documentation here)
- .UseKestre() : Specifying an IServer is mandatory, here we tell IWebHost to use Kestrel.
- .UseContentRoot(Directory.GetCurrentDirectory()) : ContentRoot determines where the server will search for content files like eg. MVC View files. Since Program.cs is located in the project folder, Directory.GetCurrentDirectory() will return the project folder.
- .UseIISIntegration() : Obviously specifies that we will use IIS as a reverse proxy, however I don't understand exactly what UseIISIntegration will be doing (since ASP.NET Core Module is just forwarding requests to Kestrel, why does the IWebHost, ASP.NET Core App Host, need to know anything about it).
- .UseStartup<Startup>() : The most easy way to configure our ASP.NET Core App is to use the Startup class, here we specify that we want to do it that way.
- .Build : builds an IWebHost, the ASP.NET Core App Host, to host the ASP.NET Core App.
Note that the template code call .Run() on the IWebHost. We could instead call just .Start() to start listening on the configured addresses. However .Run() will in addition to invoking .Start() also block the calling thread until the host is shut down.
-
-
Publish your web application :
Here we will publish to local file system first and then in the next section copy the published project to production server.
-
- To continue publishing the project you will need a profile :
-
-
-
- Configuration : Release or Debug - note that if changing to Debug the target location in the Connection tab will change.
- Target Framework : can currently NOT be selected (that may change in the future) but is set from the project.json frameworks{} section
- Target Runtime : can also currently NOT be selected (that may change in the future) but is also set from the project.json frameworks{} section :
- Target Framework = .NET Core => Target Runtime = Any.
- Target Framework = .NET Framework => Target Runtime = win7-x64 (I am not sure if other runtimes are or will be available, eg. win7-x86)
-
-
-
Creating the IIS website
-
Copy published project to production server
-
-
- Source location : Dev machine - C:inetpubwwwroot\_sandbox estAspNetCoresrc estAspNetCoreinReleasePublishOutput
- Target location : Production server - C:WebsitesTestAspNetCore
-
-
Be sure to know your web.config file
Then publishing an ASP.NET Core App, the template added web.config file will be rewritten to the following :
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=". estAspNetCore.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout" forwardWindowsAuthToken="false" /> </system.webServer> </configuration>
Then a request is coming for an IIS website, IIS will load any handlers and forward the request to these handlers, here AspNetCoreModule. In addition the above web.config tells the ASP.NET Core Module to execute dotnet with the argument of the ASP.NET Core App DLL.
Note, you can compare the unpublished template created web.config with the published version and see the difference.
-
Create a new site in IIS
-
-
- Application pool : default a new web application will run under it's own application pool of the same name as the web application - this we also prefer this time.
- Physical path : C:WebsitesTestAspNetCore - physical path MUST point to the folder of published files.
- Host name : testaspnetcore.com - http1/1 will contain the domain in the header and IIS will use that header value to map the request to a specific website (note that the Host name you set in IIS have nothing to do with how the request find the IIS - it only take effect AFTER the request have found IIS, IIS will then further map the request to a specific website).
-
-
-
Whether your ASP.NET Core App is targeting .NET Framwework or .NET Core, the IIS website worker process, w3wp.exe, will not load any managed code only the ASP.NET Core Module - therefore the worker process should not load .NET Framework nor .NET Core (whatever runtime your ASP.NET Core App is targeting, that runtime will run within the ASP.NET Core App Host process).
-
-
DNS Mapping
If you have mapped the IIS website to a public domain you don't need to do this step. However in this totorial I have mapped the IIS website to an ad-hoc domain, testaspnetcore.com. None of my computers knows the IP address of testaspnetcore.com, so if I write http://testaspnetcore.com in my browser, the browser will NOT be able to resolve the domain to the IP address of the production server.
To be able to browse testaspnetcore.com, the browser need to be able to resolve testaspnetcore.com into the IP of the production server there testaspnetcore.com is hosted.
- On your development machine open a file explorer and navigate to C:WindowsSystem32driversetc.
-
-
- 192.168.1.214 : IP of the my test production server (you need to specify the IP of your own production server).
- testAspNetCore.com : domain that IIS on the production server will host header map to the IIS website.
- After saving your hosts file all programs (including ping & your browsers) will be able to resolve testaspnetcore.com into the IP of your production server.
-
-
Test it works
-
Congratulations, you should now understand how to host ASP.NET Core Apps using IIS as a reverse proxy - the Microsoft recommended ASP.NET Core hosting topology for windows.
Appendix : Create a self hosted ASP.NET Core App
A self hosted ASP.NET Core App just means an ASP.NET Core App Host that is not hiding behind a reverse proxy and instead is facing direct access from the outside (typically the internet).
- Open a command console.
-
-
-
-
-
Important : the host will output the domains & ports that it is listening on - default and in my case it is localhost:5000.
-
- Changing the domain and port :
Default dotnet run will set the domain to localhost and the port to 5000, however you can change it (here using the command line) using the ASPNETCORE_URLS environment variable.
- If your ASP.NET Core App Host process is running in your command console, the press ctrl+c to end the host process.
-
-
- Changing the environment :
The environment can have 3 different values : Development, Staging or Production. Default dotnet run will set the environment to Production, however you can change it (here using the command line) using the ASPNETCORE_ENVIRONMENT environment variable.
- If your ASP.NET Core App Host process is running in your command console, the press ctrl+c to end the host process.
-
Using the command line to set host, port & environment is a little cumbersome, it would be great if in the future we could set host, port & environment as parameters, eg. like this : shell> dotnet run -h * -p 5123 -e production
Appendix : Understanding Proxies
In general a proxy is an entity that acts on behalf of another entity, eg. a person granted power of attorny can act on behalf of you in legal matters. However, in IT a proxy is typically a process that acts on behalf of another process.
In IT we talk about forward proxies and reverse proxies :
- Forward proxy : acts on behalf of a client (a requesting host) or multiple clients.
- Reverse proxy : acts on behalf of a server (a responding host) or multiple servers.
Forward proxy
The image below shows how then a client on a network tries to access a target server, the client is routed instead to a forward proxy. The forward proxy will then create a new independent request to the target server and forward the response back to the client that initiated a request. The target server creating the response thinks the request comes from the forward proxy and does not know anything about any client behind the forward proxy.
The advantages of a forward proxy are :
- Increased security because :
- Hackers cannot know anything about clients behind the forward proxy.
- System admin can implement internet security rules on a single machine instead of all machines on the LAN.
- Increased speed if implementing cache on the forward proxy since identitical response payloads does not need to transfer all the way from the actual response creator.
Reverse proxy
The image below shows how then a client tries to access a service, let's say a file transfer service, the client is told that the service resides on the reverse proxy IP, however the reverse proxy does not actually contain the service itself but will request the relevant server that do contain the service and send the response back to the client.
The advantages of a reverse proxy are :
- Easy to scale to load balancing (in fact the only topology difference between reverse proxy and load balancing is that a reverse proxy also gives meaning with only 1 server).
- Increased security because :
- Hackers cannot know anything about servers behind the reverse proxy.
- System admin can implement security rules on a single point of access instead of on all the servers.
- Hackers can avoid their real servers being blocked (or even shut down) then a reverse proxy is identified as a malware source.
- Reverse proxies will often be responsible for caching in which case a reverse proxy will increase the speed then response creation can be skipped.
- In case of load balancing also :
- Increased availability because failure detection will allow the reverse proxy to avoid diverting traffic to the failing servers.
- Increased speed because multiple servers are in works.
Appendix : Common errors and solutions
When : Executing dotnet whether from a command prompt or by ASP.NET Core module.
Reason : The following often repeated quote may describe the reason "The problem is that the KB2999226 (Universal CRT) which is part of the Visual C++ Redistributable for Visual Studio 2015 failed to install".
Solution : (for all 3 downloads mentioned in this solution be sure to select correct image size : x86 or x64)
- Open Windows Update and select Check for updates. Keep updating, restarting and check for updates until no more updates are available.
- Check if dotnet is working - if not then continue :
- Uninstall any current version of Visual C++ Redistributable (Control Panel -> Programs -> Programs and Features -> Uninstall a program)
- Download Visual C++ Redistributable (https://beta.visualstudio.com/downloads/ - scroll down to find Visual C++ Redistributable)
- Install the just downloaded Visual C++ Redistributable.
- Check if dotnet is working - if not then continue :
- Download KB2975061 (https://www.microsoft.com/en-us/download/details.aspx?id=43530) and install it (KB2975061 seems to be a Windows 8.1 only patch, however it works well on Windows 2012 R2 and is NECESARY to successfully install the next patch KB2919355).
- Download KB2919355 (https://www.microsoft.com/en-us/download/details.aspx?id=42335) and install it (very big file).
- Open Windows Update again and select Check for updates. Keep updating, restarting and check for updates until no more updates are available.
- Check if dotnet is working - indeed it should be working now
When : Requesting your ASP.NET Core app in a browser.
I have personally had this error for the following 5 separate reasons :
Reason 1 : ASP.NET Core Module have not been installed
Reason 2 : dotnet have not been installed
Reason 3 : dotnet cannot execute (see error 1 above how to solve that)
Reason 4 : Trying to host an ASP.NET Core App targeting .NET Framework (instead of .NET Core) without .NET Framework have been installed
Reason 5 : Incompatible package versions in project.json, which happened because I used one of Visual Studio project templates which wrote out a huge project.json and in project.json I then updated .NET Core from 1.0.0 to 1.1.0 but missed one of the template written packages which then became incompatible.
Solution 1 : Download ASP.NET Core Module and install it.
Solution 2 : If ASP.NET Core Module is installed, then dotnet should also be installed. However if you install say ASP.NET Core Module and ASP.NET Core SDK and then de-install ASP.NET Core SDK, then dotnet will be de-installed as well even if the ASP.NET Core Module is still installed and working. One way to solve the problem is to repair your ASP.NET Core Module installation either by double click on the ASP.NET Core Module installer and select repair OR go through Control Panel -> Uninstall Programs and right click on the ASP.NET Core Module and select Change from the shortcut menu which will also launch the ASP.NET Core Module installer from which you can then select Repair.
Solution 3 : See above error 1 for how to solve that problem.
Solution 4 : Download .NET Framework (latest version is 4.6.2) and install it.
Solution 5 : Correct the version incompatibilities :
-
- An unhandled exception of type 'System.IO.FileLoadException' occurred in xxx.dll
- Additional information: Could not load file or assembly 'Microsoft.Extensions.Configuration.EnvironmentVariables, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
-
- In my case I could update version from 1.0.0 to 1.1.0, which matched the .NET Core version specified.