ASP.NET Core • Posted 2 months ago
ALl the design, development and testing efforts on an application converge into the final stage of application lifecycle (SDLC) which is Deployment. One of the most important features AspNetCore offers over the traditional .NET Framework is its platform independence. AspNetCore supports deploying and running applications on Windows, Linux and MacOS environments as well by means of a hosting bundle provided by AspNetCore. This hosting bundle provides the necessary runtime for the application to run without any issues; be it a Linux, Mac or a Windows environment.
The Importance of a WebServer:
A typical web application runs on a web server, which provides a container for the app to receive and respond payloads over the network. A few Web servers such as IIS come with capabilities to act as a reverse proxy, which can handle and forward the incoming request to an application which is running inside the IIS or to any external node available for access to IIS. Alternatively, the AspNetCore library uses a built-in webserver called Kestrel which works as a container for the AspNetCore application to run.
This gives us two modes of deployment when using a AspNetCore web application. These are:
Self Hosting Model
In this approach, we configure the kestrel webserver which runs the AspNetCore application under it open for HTTP requests on a bound IP and Port. We configure these during the host building within the Main method and once configured, the kestrel server begins to listen for all requests over the specified address and port.
To configure an AspNetCore application to run over Kestrel, we chain the Host to use Kestrel server while building the Host as below:
public static IWebHostBuilder CreateWebHostBuilder(string args) => WebHost.CreateDefaultBuilder(args) .UseKestrel() .UseStartup<Startup>();
To specify the IP and Ports we can further chain another method to the Host as:
public static IWebHostBuilder CreateWebHostBuilder(string args) => WebHost.CreateDefaultBuilder(args) .UseKestrel() .UseUrls("http://0.0.0.0:3000") .UseStartup<Startup>();
When we run the application with this setup, it is hosted inside a Kestrel webserver and starts to listen over port 3000 on the IP address of the host system.
Although this approach is simple to setup and run, using Kestrel server directly is not recommended for production loads. Because:
This paves way for the second mode of deployment which overcomes the above mentioned shortcomings.
The Reverse Proxy Model
In this approach, we use the kestrel webserver as a container for the running application, while a wrapping webserver such as IIS, Apache or Nginx receive the incoming HTTP requests. The received request is then forwarded to the application via the kestrel server and the response goes in the same route. The wrapping webserver acts as a "proxy" for the actual webserver that hosts the application. The wrapping webserver in this approach is known as "a reverse proxy".
It is recommended to go in this approach for all production builds because of the following advantages provided by these web servers:
In order to have support for IIS integrating with Kestrel for a Windows Server deployment, we chain the Host with UseIISIntegration() method while building the Host.
public static IWebHostBuilder CreateWebHostBuilder(string args) => WebHost.CreateDefaultBuilder(args) .UseIISIntegration() .UseKestrel() .UseStartup<Startup>();
This approach is most widely used for deploying AspNetCore applications in production, and this is the process that happens even when we deploy our application in Cloud providers such as Azure App Services or AWS Elastic Beanstalk environments.
Code Publish and Deploy - Self Hosted Approach:
In the self-hosting approach, the kestrel server takes the web server role and listens on the configured port(s) for requests. Hence we are required to specify the IP address and Port on which the Kestrel server should listen.
This can be achieved in two ways.
public static IWebHostBuilder CreateWebHostBuilder(string args) => WebHost.CreateDefaultBuilder(args) .UseIISIntegration() .UseKestrel() .UseUrls("http://0.0.0.0:80,https://0.0.0.0:443") .UseStartup<Startup>();
As mentioned earlier, this approach should be used only for debugging and development purposes only.
Now that the code changes are made, we begin by publishing the code using dotnet CLI and then booting up the application.
> dotnet publish --runtime <hosting server runtime>
The --runtime parameter of the dotnet publish command takes in an RID (Runtime Identifier) which uniquely identifies an OS architecture against which the AspNetCore build is to be generated and run.
For example, to deploy an application targetting Windows 64 bit server environment we use the RID win-x64 which works for all the Windows x64 runtimes of AspNetCore 2.x and 3.x platforms. Similarly for a Linux environment we use linux-x64, and for mac we go with osx-x64 which represents OSX 10.12 or higher
> dotnet publish --runtime win-x64 -c Release -o build
The default publish folder will be \bin\Release\netcoreappX.Y\publish in the source code folder for the respective environment (RID) specified. In our case the code is built and the binaries are published onto a new folder /build under the directory where the command is being executed. The flag -c with value Release specifies that the dotnet runtime should take in Release configurations for packing the binaries.
Finally, the contents of the published folder are copied on to the hosting server and in the path of the build, we boot up the server using the below command:
> dotnet ./<ProjectName>.dll
The project name may be replaced with the dll name if the namespace used is different from the project name. If we are not to specify the host address in the code, we can specify it during the run command as below:
> dotnet <ProjectName>.dll --urls=http://0.0.0.0:80
*But this approach cannot override the Urls that are specified at the build host time.
In an AspNetCore3.x scenario, we can make use of the generated exe file
Hosting in IIS as an Application - Reverse Proxy Approach:
If we are to deploy in a production setup, we must first make sure we have added the UseIISIntegration() method to the host build pipepline. Once the code is published, we can now host the published contents by the following steps:
*As per my observation, when we want to host the application via a reverse proxy approach, it is not recommended to use the UseUrls() in the build pipeline which is intended to be used for self hosting approach - as it might cause issues with the hosting.
An in-detail process about hosting in IIS and the probable issues we might run into along with a few best practices has been discussed in another space.
Subscribe to my newsletter and get the first copy of every new post delivered straight into your inbox.