Running Multiple Instances of a .NET Core Application with Dynamic Configuration Loading
In today’s blog, we will explore how to run a .NET Core application in four different instances, each loading its own appsettings.json
configuration file based on an environment variable called INSTANCE_NAME
. This approach allows us to maintain a single codebase while providing each instance with its specific configuration, enabling efficient management of resources in a microservices architecture.
Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. Uncover more by visiting our https://dotnet-fullstack-dev.blogspot.com reach out for further information.
Overview
- .NET Core Application Setup: We will modify our application to load configurations based on the
INSTANCE_NAME
environment variable. - Creating Instance-Specific
appsettings.json
Files: We will create different configuration files for each instance. - Kubernetes Deployment Configuration: We will configure a Kubernetes deployment to manage the instances.
Let’s dive into each step in detail.
Step 1: .NET Core Application Setup
1.1 Create a New .NET Core Application
First, we need to create a new .NET Core web application. You can use the command line to create the project:
dotnet new webapi -n MyDotNetApp
cd MyDotNetApp
1.2 Modify the Program.cs
Open the Program.cs
file and modify it to load configurations dynamically based on the INSTANCE_NAME
environment variable:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
namespace MyDotNetApp
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
// Fetch the INSTANCE_NAME environment variable
var instanceName = Environment.GetEnvironmentVariable("INSTANCE_NAME") ?? "default";
// Load the shared appsettings.json
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
// Load instance-specific appsettings.{instanceName}.json
config.AddJsonFile($"appsettings.{instanceName}.json", optional: true, reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Explanation
- Environment Variable: We retrieve the
INSTANCE_NAME
usingEnvironment.GetEnvironmentVariable()
. If it’s not set, it defaults to"default"
. - Configuration Loading: We first load the common
appsettings.json
, then load the instance-specific configuration if it exists.
Step 2: Creating appsettings.json
Files
Now, let’s create our configuration files. You will create five configuration files in the project directory:
appsettings.json
(common settings)appsettings.Instance1.json
appsettings.Instance2.json
appsettings.Instance3.json
appsettings.Instance4.json
2.1 appsettings.json
(Common Configuration)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
2.2 appsettings.Instance1.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=Instance1-DB;Database=ProductDB;User Id=sa;Password=password1;"
},
"InstanceSpecificSetting": "This is instance 1"
}
2.3 appsettings.Instance2.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=Instance2-DB;Database=ProductDB;User Id=sa;Password=password2;"
},
"InstanceSpecificSetting": "This is instance 2"
}
2.4 appsettings.Instance3.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=Instance3-DB;Database=ProductDB;User Id=sa;Password=password3;"
},
"InstanceSpecificSetting": "This is instance 3"
}
2.5 appsettings.Instance4.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=Instance4-DB;Database=ProductDB;User Id=sa;Password=password4;"
},
"InstanceSpecificSetting": "This is instance 4"
}
Explanation
Each instance-specific configuration contains a unique connection string and an instance-specific setting.
Step 3: Kubernetes Deployment Configuration
Now we will create a Kubernetes deployment configuration to manage our instances.
3.1 Create deployment.yaml
Create a file named deployment.yaml
with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dotnet-app
spec:
replicas: 4 # Number of instances to run
selector:
matchLabels:
app: my-dotnet-app
template:
metadata:
labels:
app: my-dotnet-app
spec:
containers:
- name: dotnet-app
image: my-dotnet-app-image:latest
env:
# Manually set INSTANCE_NAME for each replica
- name: INSTANCE_NAME
value: "Instance1" # Instance 1
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dotnet-app-instance2
spec:
replicas: 1 # Number of instances to run
selector:
matchLabels:
app: my-dotnet-app-instance2
template:
metadata:
labels:
app: my-dotnet-app-instance2
spec:
containers:
- name: dotnet-app
image: my-dotnet-app-image:latest
env:
- name: INSTANCE_NAME
value: "Instance2" # Instance 2
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dotnet-app-instance3
spec:
replicas: 1 # Number of instances to run
selector:
matchLabels:
app: my-dotnet-app-instance3
template:
metadata:
labels:
app: my-dotnet-app-instance3
spec:
containers:
- name: dotnet-app
image: my-dotnet-app-image:latest
env:
- name: INSTANCE_NAME
value: "Instance3" # Instance 3
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-dotnet-app-instance4
spec:
replicas: 1 # Number of instances to run
selector:
matchLabels:
app: my-dotnet-app-instance4
template:
metadata:
labels:
app: my-dotnet-app-instance4
spec:
containers:
- name: dotnet-app
image: my-dotnet-app-image:latest
env:
- name: INSTANCE_NAME
value: "Instance4" # Instance 4
ports:
- containerPort: 80
Explanation
- Deployments: We create four separate deployments for each instance, each setting the
INSTANCE_NAME
environment variable accordingly. - Replicas: The first deployment has four replicas, while the others have one, indicating that we want to run one instance of each specific configuration.
Step 4: Sample Controller to Display Loaded Configurations
To verify that each instance is loading its specific configuration, we can create a controller that returns the loaded settings.
4.1 Create ConfigController.cs
Add a new controller to your project named ConfigController.cs
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
namespace MyDotNetApp.Controllers
{
[ApiController]
[Route("[controller]")]
public class ConfigController : ControllerBase
{
private readonly IConfiguration _configuration;
public ConfigController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public IActionResult GetConfig()
{
var instanceSpecificSetting = _configuration["InstanceSpecificSetting"];
var connectionString = _configuration.GetConnectionString("DefaultConnection");
return Ok(new
{
InstanceSpecificSetting = instanceSpecificSetting,
ConnectionString = connectionString
});
}
}
}
Explanation
- GetConfig Method: This endpoint retrieves and returns the instance-specific setting and the connection string based on the configuration that was loaded.
Step 5: Build and Deploy the Application
5.1 Build the Application
Make sure you have your Dockerfile set up correctly in your project directory.
5.2 Deploy to Kubernetes
Finally, deploy your application to Kubernetes:
Conclusion
By following this guide, you can run multiple instances of a .NET Core application using different configurations dynamically loaded based on an environment variable. This approach not only enhances the maintainability of your codebase but also allows for efficient management of resources in a microservices architecture.