Kubernetes ConfigMaps with .NET Core

This tutorial will cover how to mount a Kubernetes ConfigMap into a pod and how to read that configuration in .NET. I will also demonstrate the different ways a .NET application can read configuration from an appsettings.json file. One demonstration will reload configuration on change and how to listen for those changes. That does not work with Kubernetes ConfigMaps but is great for local development.

GitHub

You can download the source code for the application here.
GitHub: Kubernetes Tutorial: ConfigMap in .NET

Kubernetes ConfigMaps

Kubernetes provides the ability to map configuration into pods. This information is typically not sensitive and can be either a JSON configuration file or a collection of key-value pairs. In this tutorial, I will show how to map in a JSON configuration file.

Kubernetes: ConfigMaps

Why use a ConfigMap over Environment Variables?

Environment Variables are great for passing in single values but if you have an entire configuration file like the appsettings.json file then you should use either a ConfigMap or Azure App Configuration.

Creating a Kubernetes ConfigMap

To install this ConfigMap into the Kubernetes cluster you will need to run this command. You could also download this configuration and edit it yourself.

Note: The deploy.ps1 script will execute all of the kubectl commands below.

To start with I’m creating a Kubernetes namespace titles “mrjamiebowman”.

kubectl create namespace mrjamiebowman

ConfigMap

kubectl apply -f k8s/configmap.yaml

ConfigMap using Files.Get

Another variation would be to use .Files.Get to load an appsettings.json file. This might make it easier to maintain. I haven’t figured out how to read this from a relative path either.

data:
  appsettings.json: |-
{{ .Files.Get "settings/appsettings.json" | indent 4 }}

Deployment

kubectl apply -f k8s/deployment.yaml

Service

In most cloud providers a service will automatically create a LoadBalancer that is immediately capable of accepting traffic to an IP address.

kubectl apply -f k8s/service.yaml

Exploring Configuration Options with .NET Core

There are several different ways configuration can be injected into the controller. With Kubernetes ConfigMaps, the configuration will not be re-loaded unless the pod is re-created by either deleting or a rolling restart. You can’t edit it in the pod either. Using the standard injection method below will work fine if you are using ConfigMaps, however, another option is to use the IOptionsMonitor Pattern which may ease local development. Knowing the difference is important.

Controller

In this tutorial, we are injecting the configuration twice using two different methods. The _valuesConfiguration will use standard injection while the _valuesMonitoredConfiguration will use the IOptionsMonitor Pattern.

    [ApiController]
    [Route("[controller]")]
    public class ValuesController : ControllerBase
    {
        private readonly ILogger<ValuesController> _logger;
        private readonly IOptionsMonitor<ValuesConfiguration> _valuesMonitoredConfiguration;
        private readonly ValuesConfiguration _valuesConfiguration;

        public ValuesController(ILogger<ValuesController> logger, IOptionsMonitor<ValuesConfiguration> valuesMonitoredConfiguration, ValuesConfiguration valuesConfiguration)
        {
            _logger = logger;
            _valuesMonitoredConfiguration = valuesMonitoredConfiguration;
            _valuesConfiguration = valuesConfiguration;
        }

Standard Injection

This is a very simple way of binding the configuration to an object and injecting that into controllers. This will work fine with Kubernetes ConfigMaps.

Startup.cs – ConfigureServices(IServiceCollection services)

// injects ValuesConfiguration
ValuesConfiguration valuesConfiguration = new ValuesConfiguration();
Configuration.GetSection(ValuesConfiguration.Position).Bind(valuesConfiguration);
services.AddSingleton<ValuesConfiguration>(valuesConfiguration);

Get Method

This is as simple as it gets. Configuration values can be returned from the injected configuration object.

[HttpGet]
public string Get()
{
	if (_valuesConfiguration?.Message == null)
		return "Configuration is NULL";

	return _valuesConfiguration?.Message;
}

IOptionsMonitor

Using IOptionsMonitor you can easily monitor your configuration file for changes and I’m going to demonstrate how that works. However, this does not work with Kubernetes ConfigMaps. Pods must be re-created for the new changes to apply.

Retrieving data from the IOptionsMonitor Pattern is slightly different. You have to select the CurrentValue to return the updated configuration setting.

Startup.cs – ConfigureServices(IServiceCollection services)

Configuration injection here is slightly different.

// injects IOptionsMonitor<ValuesConfiguration>)
services.Configure<ValuesConfiguration>(Configuration.GetSection(ValuesConfiguration.Position));

Get Method (IOptionsMonitor)

[HttpGet("ioptionsmonitor")]
public string GetIOptionsMonitor()
{
	if (_valuesMonitoredConfiguration?.CurrentValue?.Message == null)
		return "IOptionsMonitor Configuration is NULL";

	return "IOptionsMonitor Configuration: " + _valuesMonitoredConfiguration?.CurrentValue?.Message;
}

Reloading Configuration in Kubernetes

There are several ways to do this. Assuming the replication factor has been increased to something like 3 and there are now 3 pods running. Being that this tutorial is to teach people, I would expect that you would be doing this in a DEV environment. Most of these techniques you would not want to do in production because it would cause downtime. Here are some ideas of how to re-create pods.

Delete All Pods By Label

kubectl delete pods -l app=kubernetes-tutorial -n mrjamiebowman

Delete Each Pod Individually

The key thing here is to leave one running and delete the others.

kubectl delete pod/podname -n mrjamiebowman

Scale to 0…3

kubectl scale deployment kubernetes-tutorial -n mrjamiebowman --replicas=0

then scale back up…

kubectl scale deployment kubernetes-tutorial -n mrjamiebowman --replicas=3

Rolling Restart (Production)

A more production-friendly way of doing this would be to configure the Deployment to handle a rolling restart. That is for another tutorial in another day. I just want people to be aware of this.

kubectl rollout restart deployment kubernetes-tutorial

Other Configuration Management Options

While this information defeats the purpose of using a ConfigMap it is also worth being aware that there are more premium options for managing configuration. Now, the downside here is that Azure App Configuration starts at about $36 dollars per month for the Standard Tier of service.

Azure App Configuration

App Configuration allows for the easy importation of .NET JSON configuration files. The values can be managed in the Azure Portal and even includes functionality like “Features”. Convenience and distributed configuration is a prime reason to use Azure App Configuration. Azure App Configuration can also link values from the Azure Key Vault.

Microsoft: Azure App Configuration

Azure Key Vault

Azure Key Vault is great for managing API keys and sensitive information that may need updating periodically. It’s actually very easy because you can set expiration and start dates so that configuration can change smoothly. I’ve found that using the Azure App Configuration with Azure Key Vault to be extremely powerful and useful for managing configuration across multiple applications.

Microsoft: Azure Key Vault

Further Reading

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0
https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
https://www.bluematador.com/blog/kubernetes-deployments-rolling-update-configuration

LEAVE A REPLY

Please enter your comment!
Please enter your name here