Microsoft’s Internet Information Services is one of the oldest web server platforms around. Originally launched in 1995, it’s been regularly updated alongside Windows Server. However, it’s been a while since the last major release, when IIS 10 Version 1809 launched in November 2018. Although Windows Server 2022 added QUIC and TLS 1.3 support, the core web server platform hasn’t changed.
Meanwhile, Microsoft has been quietly developing two alternative web server platforms as part of .NET, with a focus on delivering modern dynamic web applications. The first of these, Http.sys is a Windows-only server that’s ideal for running Windows-hosted ASP.NET Core applications at scale. The second, Kestrel, is the default ASP.NET Core web server and runs on all .NET Core platforms, including macOS. It’s designed to work behind load balancers such as Nginx, as well as to support modern web technologies such as gRPC.
Introducing Kestrel
Kestrel is an interesting option for anyone building .NET web applications. It’s a relatively lightweight server compared to IIS, and as it’s cross-platform, it simplifies how you might choose a hosting platform. It’s also suitable as a development tool, running on desktop hardware for tests and experimentation. There’s support for HTTPS, HTTP/2, and a preview release of QUIC, so your code is future-proof and will run securely.
The server installs as part of ASP.NET Core and is the default for sites that aren’t explicitly hosted by IIS. You don’t need to write any code to launch Kestrel, beyond using the familiar WebApplication.CreateBuilder method. Microsoft has designed Kestrel to operate with minimal configuration, either using a settings file that’s created when you use dotnet new to set up an app scaffolding or when you create a new app in Visual Studio.
Configuring Kestrel
Apps are able to configure Kestrel using the APIs in WebApplication and WebApplicationBuilder, for example, adding additional ports. As Kestrel doesn’t run until your ASP.NET Core code runs, this is a relatively easy way to make server configuration dynamic, with any change simply requiring a few lines of code. Similarly, you can add a new endpoint URL from the dotnet run command or by editing your app’s appsettings.json file. Alternatively, an ASP.NET Core environment variable can be set to manage the ports used. This flexibility makes Kestrel surprisingly simple to manage; you simply pick the method that fits best with your code.
Other options control the IP addresses a server listens for a connection on, with the ability to listen to all available interfaces. If you’re working with HTTPS and need to test an application before putting it in production, Kestrel comes bundled with a built-in test certificate to help you get started. This is installed as part of the ASP.NET Core SDK and can be managed from the dotnet command line tool. Once you go into production, use the various configuration tools to select an appropriate certificate.
With support for Unix sockets, Kestrel is compatible with most load balancers, including Nginx. This makes it ideal for use in containers, either under Linux or in a Windows container. Here Unix sockets support will let you add networking and security features to your code via a service mesh.
Customize your server, your way
Having a web server that’s configurable programmatically is useful; you can add features as required. For example, a production server might run with minimal logging. If problems occur, you can quickly configure Kestrel to add alternate logging providers to get more detail on operations. Other options include adding support for alternate services, for example, delivering static content from a file system.
There are a lot of options in ASP.NET Core for supported services, so you can expand beyond the default minimal API configuration, for example, adding session management tools or response caching. It is important to remember that adding new services does add more APIs to your server, which expands the attack surface, so only add features you know you are going to use and that you know will be protected by your security platform.
As Kestrel is highly customizable, you can manage the HTTP verbs that are supported when accepting requests from browsers, as well as manage the way your server responds to specific URI structures. Here you can take advantage of .NET primitives such as lambdas to parse data and use it in your applications, reducing the complexity of your code by handing functionality over to a server. This can make it easier to use URI encoding to handle state for an application, using routes to capture parameters in a URI. This way Kestrel and ASP.NET can build and run single-page web applications, using URI structure to determine the content that’s delivered to users.
Microsoft has come up with a flexible, powerful web server that’s a useful alternative to IIS for ASP.NET applications. By taking advantage of its minimal API approach, very little code is necessary to build a web server that can use routes and parameters to serve up static content alongside ASP.NET Core features, exactly what’s needed to build modern applications that can scale in cloud-native containers.
How Microsoft replaced IIS with Kestrel
As always, it’s interesting to see what Microsoft does with its own tools. Kestrel has recently been paired with the open source .NET reverse proxy YARP to replace the underlying web platform in Azure App Services, a process described as “a heavy lift.” An interesting blog post detailed the process used to manage the migration, one that’s well worth looking at if you’re thinking about doing similar with your own applications and services.
Although Azure App Services has the additional demand of being a massively scalable multitenant platform, there are lessons in Microsoft’s migration that can apply to any shift to Kestrel. It’s not the first service to make that shift: Bing, Azure Active Directory, and Dynamics 365 are already using the same server.
The scale of Azure App Services is massive, even for hyperscale services such as Azure. It currently supports more than 160 billion HTTP requests a day and hosts more than 14 million domain names. The underlying architecture is typical of cloud-scale services, with the platform behind a set of load balancers and with App Service code running on a set of workers, delivered through a web server. Globally, that means more than 200,000 cores running web servers.
Prior to the update, this was running on IIS and HTTP.sys. By moving to Kestrel and YARP, Microsoft could offer its users a wider set of HTTP protocols, enhance its secure connection options, and still allow developers to use their choice of app platforms.
The process of migrating a front-end service that works with traditional browsers as well as with REST and gRPC API clients revealed some issues with Kestrel that have since been remediated, making it more suitable for user-facing applications. Perhaps the most interesting change resulted in Kestrel behaving less strictly. Initially written to conform to the HTTP specification, it rejected requests that included leading newline characters. The Kestrel team has now relaxed its compliance, and newer releases now accept this class of request.
The shift to a new platform from one that’s been running since the earliest days of Azure allows us to get some interesting benchmarks: Throughput has increased by nearly 80% for standard HTTP requests. That translates to a significant reduction in CPU usage across the entire fleet of front-end service hosts, which the team continues to monitor in the hope of eventually reducing the number of cores they use, saving both costs and data center resources.
As Kestrel is cross-platform, the same code can now support Linux workloads, allowing Azure to remove the Nginx servers that provided the Linux worker front ends. Having the same infrastructure for Linux and Windows should reduce management overhead, with a common codebase for the service and a common set of features. As Kestrel and YARP update, new features will be available to all Azure App Service users, not only for an OS-specific subset.
The work Microsoft is doing to deliver Kestrel and YARP in Azure App Service will benefit everyone using Kestrel. Putting it at the heart of one of Azure’s most demanding platforms will quickly reveal edge cases that might not be visible to ASP.NET Core—at least not without requiring significant debugging.
It’s a win-win scenario for Kestrel and ASP.NET Core, making it more likely that your next project will target Kestrel rather than the aging IIS. The result should be web servers that are easier to configure and manage and that run on most common server platforms or in containers, without the trade-off between simplicity and security that’s too often a problem when running at scale.
Copyright © 2022 IDG Communications, Inc.
Originally posted on August 29, 2022 @ 4:00 am