.NET Core

NuGet is the .NET package manager. By default, the .NET Core SDK will use packages from the nuget.org website.

In this article, you'll learn how to deploy a NuGet server on Red Hat OpenShift Container Platform (RHOCP). We'll use it as a caching server and see that it speeds up our builds. Before we get to that, we’ll explore some general NuGet concepts and see why it makes sense to use a local NuGet server.

NuGet

NuGet packages are .NET's libraries: they allow us to package compiled code and use it in different applications. To share these packages, we make them accessible via HTTP. When we consume packages from nuget.org, for example, we are using packages from the NuGet feed at https://api.nuget.org/v3/index.json.

The SDK dotnet restore command retrieves the packages needed to build a project. To override the default of using nuget.org, we can use a NuGet.config file or specify the --source argument to the restore command.

We can use multiple feeds when performing a restore. For example, one feed could be the public repository at nuget.org, while another feed can point to a local NuGet server that hosts packages which are developed in-house.

That is a first use-case: hosting private packages. Note that a single NuGet server can host multiple distinct feeds. This allows different teams/projects to have their own feeds. You can also use this capability to have separate feeds for development builds and release builds.

A second use-case is caching packages. In this case, a feed is a cached instance of an upstream feed. By caching packages locally, we can reduce the time to restore the project. We are also no longer dependent on the availability of the upstream server. If the upstream server allows deleting packages, we will still have those in our cache. The caching server also reduces the number of packages that are fetched from the Internet.

Local hosting options

Microsoft's NuGet server is open source, but it doesn't run on Linux. Repository managers like JFrog Artifactory and Sonatype Nexus are feature-rich package managers that also support NuGet feeds.

If we are looking for a lightweight, NuGet-only option that we can run in a Linux container, BaGet is an interesting choice. It's an open source NuGet server that supports the v3 protocol, and it is implemented using ASP.NET Core.

RHOCP and NuGet

RHOCP can build our .NET Core application. The .NET Core builder accepts a number of environment variables to control its behavior. We're interested in DOTNET_RESTORE_SOURCES, which does the following:

Specifies the space-separated list of NuGet package sources used during the restore operation. This overrides all of the sources specified in the NuGet.config file.

So, to use a local NuGet server, we can set this variable to the feed URL. If you are using Microsoft NuGet Server, JFrog Artifactory, or Sonatype Nexus, have a look at the product documentation for creating a NuGet feed. In the next section, we'll explain how to deploy BaGet on RHOCP and use it as a caching NuGet server for nuget.org.

Using BaGet with RHOCP

For the following steps, I assume .NET Core support has been added to your RHOCP installation, as described in Installing Image Streams. The DOTNET_NAMESPACE variable used in the steps should be set to the Kubernetes namespace that contains the .NET Core builder images.

Using the RHOCP CLI (oc), import the BaGet template in your RHOCP project:

$ oc create -f https://raw.githubusercontent.com/redhat-developer/s2i-dotnetcore/master/templates/community/dotnet-baget.json
template.template.openshift.io/dotnet-baget-persistent created
template.template.openshift.io/dotnet-baget-ephemeral created

As we can see, this creates two templates: one for a persistent and one for an ephemeral deployment of BaGet.

These templates accept a number of parameters. For example, to see the parameters from dotnet-baget-persistent execute:

$ oc process --parameters dotnet-baget-persistent
Parameter Name Description Default
NAME The name assigned to all of the front-end objects defined in this template. nuget
MIRROR_PACKAGESOURCE Packages that are not found locally will be retrieved from this server. https://api.nuget.org/v3/index.json
NUGET_API_KEY Set this to a password required to push packages.  
DELETION_BEHAVIOR Set this to -Unlist to make packages undiscoverable, or to HardDelete to remove packages from storage. Unlist
MEMORY_LIMIT Maximum amount of memory the .NET Core container can use. 512Mi
VOLUME_CAPACITY Volume space available for data, e.g. 512Mi, 2Gi 512Mi
DOTNET_IMAGE_STREAM_TAG The image stream tag that is used to build the code. dotnet:2.2
NAMESPACE The RHOCP namespace where the .NET builder ImageStream resides. openshift
SOURCE_REPOSITORY_URL The URL of the repository with your application source code. https://github.com/loic-sharma/BaGet.git
SOURCE_REPOSITORY_REF Set this to a branch name, tag, or other reference for your repository if you are not using the default branch. v0.1.29-prerelease
DOTNET_STARTUP_PROJECT Set this to a project file (for example, csproj) or a folder containing a single project file. src/BaGet

We see that the service by default will mirror packages from nuget.org (MIRROR_PACKAGESOURCE). Our container is assigned 512MiB of RAM (MEMORY_LIMIT). We provision 512MiB (VOLUME_CAPACITY) for persistent storage. The NuGet service will be built from https://github.com/loic-sharma/BaGet.git v0.1.29-prerelease (SOURCE_REPOSITORY_URL and SOURCE_REPOSITORY_REF). The NUGET_API_KEY is empty: no key is required to push packages to this server. The hostname for our service is nuget.

We'll now deploy the server in our project:

$ oc new-app dotnet-baget-ephemeral -p NAMESPACE=$DOTNET_NAMESPACE

RHOCP will build BaGet from source. We can see the progress of the build:

$ oc logs -f bc/nuget

When the build finishes, the NuGet service will be deployed and can be used internally in the project via the URL http://nuget:8080/v3/index.json.

Let's see how this local NuGet service affects build times.

We'll deploy the dotnet-example template and trigger a number of builds:

$ oc new-app dotnet-example -p NAMESPACE=$DOTNET_NAMESPACE
$ oc start-build dotnet-example;oc start-build dotnet-example; oc start-build dotnet-example

In the OpenShift Console, we get an overview of the build times without the local NuGet server:

Overview of the build times without the local NuGet server

Now we'll deploy the same application and use the local NuGet server as a cache:

$ oc new-app dotnet-example -p NAMESPACE=$DOTNET_NAMESPACE -p NAME=dotnet-example-nuget -p DOTNET_RESTORE_SOURCES=http://nuget:8080/v3/index.json
$ oc start-build dotnet-example-nuget; oc start-build dotnet-example-nuget; oc start-build dotnet-example-nuget; oc start-build dotnet-example-nuget

Deploying the same application and use the local NuGet server as a cache

As we can see from the graphs, using the local NuGet server significantly reduces the build time. The builds using nuget.org directly have a huge variation between build times, which is gone with the local server. We see our first build with the local server took a bit longer. During this build, packages were retrieved from nuget.org and cached locally. Those packages were then re-used for the successive builds.

Conclusion

In this article, we’ve looked at options to deploy a NuGet server locally. We then saw how you can use a local NuGet server to speed up .NET Core builds on Red Hat OpenShift Container Platform.

Additional .NET Core articles on the Red Hat Developer blog

Last updated: March 24, 2023