OpenShift Operator

In previous articles, I’ve covered two strategies for improving .NET Core build speed on Red Hat OpenShift by reducing time to restore dependencies: adding a local NuGet server and using incremental builds. In this article, I’ll look at another strategy: using a custom base image that has includes the dependencies.

ASP.NET Core

ASP.NET Core is the de facto framework for building web applications with .NET Core. The framework is open source on GitHub; however, for 1.x and 2.x, it does not have the infrastructure needed for a third party (e.g., Red Hat) to build it from source so that it matches Microsoft releases.

Building from source is a key feature for open source projects. It reduces vendor lock-in by enabling other companies and communities to build and support the software. The ability to build code from source is a requirement for including packages in the default package repositories of Linux distributions (like Fedora, Debian, and Ubuntu). The upcoming ASP.NET Core 3.0 will be buildable from source!

Because ASP.NET Core 2.x cannot be built from source, it cannot be included in Red Hat Enterprise Linux packages and Red Hat OpenShift’s .NET Core builder (s2i-dotnetcore). This leads to long restore times because applications need to fetch and extract those packages from nuget.org (or a local NuGet server).

aspnet image streams

As a Red Hat OpenShift user, to work around this limitation, you can build a custom image that includes the ASP.NET Core NuGet packages. I’ve written a resource file that does exactly that. You can import it into OpenShift using:

$ oc create -f https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/templates/aspnet-2.x.json

If you have a look into the file, you’ll see it defines an aspnet:2.1 and aspnet:2.2 image stream that builds on top of s2i-dotnetcore’s dotnet:2.1 and dotnet:2.2 using a Docker strategy. The ASP.NET Core packages are added by performing a dotnet new web. Because we build on top of the dotnet image streams, the aspnet streams will automatically be rebuilt when the base images are updated with security patches.

After importing the file, it takes a couple of minutes for the images to be built.

Let’s compare the difference in build speed by building https://github.com/redhat-developer/s2i-dotnetcore-ex using dotnet:2.2 and aspnet:2.2.

$ oc new-app --name=dotnetapp dotnet:2.2~https://github.com/redhat-developer/s2i-dotnetcore-ex#dotnetcore-2.2 --build-env DOTNET_STARTUP_PROJECT=app
$ oc new-app --name=aspnetapp aspnet:2.2~https://github.com/redhat-developer/s2i-dotnetcore-ex#dotnetcore-2.2 --build-env DOTNET_STARTUP_PROJECT=app

When we do five builds, we get these build times:

base image min max avg
dotnet:2.1 43s 1m30s 47s
aspnet:2.1 23s 31s 24s

As you can see, the build times are halved using the aspnet image. What’s more, the build time variance is much smaller.

Conclusion

We can significantly reduce build times for .NET Core applications by creating a custom build image that includes common dependencies. aspnet-2.x.json provides build configurations that add ASP.NET 2.x Core packages to the s2i-dotnetcore dotnet images.

Last updated: March 28, 2023