{"id":248,"date":"2024-06-23T07:27:27","date_gmt":"2024-06-23T07:27:27","guid":{"rendered":"https:\/\/dotnetconfig.org\/blog\/?p=248"},"modified":"2024-07-01T07:36:51","modified_gmt":"2024-07-01T07:36:51","slug":"leveraging-etcd-in-kubernetes-for-effective-net-configuration-management-across-containerized-applications","status":"publish","type":"post","link":"https:\/\/dotnetconfig.org\/blog\/leveraging-etcd-in-kubernetes-for-effective-net-configuration-management-across-containerized-applications\/","title":{"rendered":"Leveraging etcd in Kubernetes for Effective .NET Configuration Management Across Containerized Applications"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-249  alignleft\" src=\"https:\/\/dotnetconfig.org\/blog\/wp-content\/uploads\/2024\/07\/th-30.jpeg\" alt=\"Leveraging etcd in Kubernetes for Effective .NET Configuration Management Across Containerized Applications\" width=\"416\" height=\"416\" srcset=\"https:\/\/dotnetconfig.org\/blog\/wp-content\/uploads\/2024\/07\/th-30.jpeg 1024w, https:\/\/dotnetconfig.org\/blog\/wp-content\/uploads\/2024\/07\/th-30-300x300.jpeg 300w, https:\/\/dotnetconfig.org\/blog\/wp-content\/uploads\/2024\/07\/th-30-150x150.jpeg 150w, https:\/\/dotnetconfig.org\/blog\/wp-content\/uploads\/2024\/07\/th-30-768x768.jpeg 768w\" sizes=\"auto, (max-width: 416px) 100vw, 416px\" \/><span style=\"font-weight: 400;\">Managing configurations efficiently is paramount. Combining the power of etcd, Kubernetes, and .NET Core, developers can orchestrate robust distributed key-value stores tailor-made for dynamic environments. This article delves into the intricacies of using etcd within Kubernetes to manage .NET Configuration settings effectively for containerized applications.<\/span><\/p>\n<h3><b>Setting Up a Kubernetes Cluster with etcd<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">To harness etcd in managing .NET configurations, the first step involves setting up a Kubernetes cluster provisioned with etcd. Kubernetes, by default, utilizes etcd as its primary data store for cluster state management. However, leveraging etcd for application configurations requires additional steps.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># kube-etcd.yaml<\/span><\/p>\n<p><span style=\"font-weight: 400;\">apiVersion: v1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kind: Pod<\/span><\/p>\n<p><span style=\"font-weight: 400;\">metadata:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0name: etcd<\/span><\/p>\n<p><span style=\"font-weight: 400;\">spec:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0containers:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0&#8211; name: etcd<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0image: quay.io\/coreos\/etcd:v3.4.15<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0ports:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8211; containerPort: 2379<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8211; containerPort: 2380<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0volumeMounts:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0&#8211; name: etcd-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mountPath: \/etcd-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0volumes:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0&#8211; name: etcd-data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0emptyDir: {}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Deploy the etcd Pod in your Kubernetes cluster with the above YAML file.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kubectl apply -f kube-etcd.yaml<\/span><\/p>\n<h3><b>Configuring .NET Applications to Use etcd<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">.NET Core provides a robust configuration API, allowing developers to easily integrate multiple configuration sources. To incorporate etcd as a configuration source, we&#8217;ll leverage the ETCD Client for .NET, <\/span><span style=\"font-weight: 400;\">dotnet-etcd<\/span><span style=\"font-weight: 400;\">. First, install the necessary package in your .NET Core application.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">dotnet add package dotnet-etcd<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Next, adjust the application\u2019s Startup class to include etcd as a configuration provider.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using dotnet_etcd;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using Microsoft.Extensions.Configuration;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">public class Startup<\/span><\/p>\n<p><span style=\"font-weight: 400;\">{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public Startup(IConfiguration configuration)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Configuration = configuration;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public IConfiguration Configuration { get; }<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public void ConfigureServices(IServiceCollection services)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var etcdClient = new EtcdClient(&#8220;http:\/\/&lt;etcd-service-ip&gt;:2379&#8221;);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var etcdConfigurationSource = new EtcdConfigurationSource(etcdClient, &#8220;dotnet-config&#8221;);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var builder = new ConfigurationBuilder()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.AddConfiguration(Configuration)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.Add(etcdConfigurationSource);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0IConfigurationRoot configuration = builder.Build();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0services.AddSingleton&lt;IConfiguration&gt;(configuration);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Add other services required for your application<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public void Configure(IApplicationBuilder app, IWebHostEnvironment env)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Configure the HTTP request pipeline<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Implement an <\/span><span style=\"font-weight: 400;\">EtcdConfigurationSource<\/span><span style=\"font-weight: 400;\"> to fetch the configurations from etcd.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using dotnet_etcd;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using Microsoft.Extensions.Configuration;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using System.Threading;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">using System.Threading.Tasks;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">public class EtcdConfigurationSource : IConfigurationSource<\/span><\/p>\n<p><span style=\"font-weight: 400;\">{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly EtcdClient _client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly string _key;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public EtcdConfigurationSource(EtcdClient client, string key)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_client = client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_key = key;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public IConfigurationProvider Build(IConfigurationBuilder builder)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new EtcdConfigurationProvider(_client, _key);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">public class EtcdConfigurationProvider : ConfigurationProvider<\/span><\/p>\n<p><span style=\"font-weight: 400;\">{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly EtcdClient _client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly string _key;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public EtcdConfigurationProvider(EtcdClient client, string key)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_client = client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_key = key;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public override void Load()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var etcdResponse = _client.GetStringAsync(_key).Result;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Data = new Dictionary&lt;string, string&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{ _key, etcdResponse }<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0};<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public override void Set(string key, string value)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_client.PutAsync(key, value).Wait();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0base.Set(key, value);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<h3><b>Managing Configuration Changes in etcd<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">One of the significant advantages of using etcd is its capability to handle dynamic configuration changes without restarting the application. You can monitor changes and update configurations dynamically.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">public class EtcdConfigurationProvider : ConfigurationProvider<\/span><\/p>\n<p><span style=\"font-weight: 400;\">{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly EtcdClient _client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private readonly string _key;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private CancellationTokenSource _cancellationTokenSource;<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public EtcdConfigurationProvider(EtcdClient client, string key)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_client = client;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_key = key;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_cancellationTokenSource = new CancellationTokenSource();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0WatchEtcdKey(_key);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0private void WatchEtcdKey(string key)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Task.Run(async () =&gt;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0while (!_cancellationTokenSource.Token.IsCancellationRequested)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var watchResponse = await _client.WatchRangeAsync(key, _cancellationTokenSource.Token);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0foreach (var watchEvent in watchResponse.Events)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0switch (watchEvent.Type)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0case Mvccpb.Event.Types.EventType.Put:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Set(key, watchEvent.Kv.Value.ToStringUtf8());<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OnReload();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0case Mvccpb.Event.Types.EventType.Delete:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Data.Remove(key);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OnReload();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public override void Load()<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var etcdResponse = _client.GetAsync(_key).Result;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Data = etcdResponse.Kvs.ToDictionary(<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0kv =&gt; kv.Key.ToStringUtf8(),<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0kv =&gt; kv.Value.ToStringUtf8());<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0public override void Set(string key, string value)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_client.PutAsync(key, value).Wait();<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0base.Set(key, value);<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">}<\/span><\/p>\n<h3><b>Deploying and Managing the .NET Application in Kubernetes<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">Having configured your .NET application to leverage etcd, the next step is containerizing and deploying it into the Kubernetes cluster. Begin by creating a Dockerfile for your .NET application.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Dockerfile<\/span><\/p>\n<p><span style=\"font-weight: 400;\">FROM mcr.microsoft.com\/dotnet\/aspnet:5.0 AS base<\/span><\/p>\n<p><span style=\"font-weight: 400;\">WORKDIR \/app<\/span><\/p>\n<p><span style=\"font-weight: 400;\">EXPOSE 80<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">FROM mcr.microsoft.com\/dotnet\/sdk:5.0 AS build<\/span><\/p>\n<p><span style=\"font-weight: 400;\">WORKDIR \/src<\/span><\/p>\n<p><span style=\"font-weight: 400;\">COPY [&#8220;MyApp\/MyApp.csproj&#8221;, &#8220;MyApp\/&#8221;]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">RUN dotnet restore &#8220;MyApp\/MyApp.csproj&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">COPY . .<\/span><\/p>\n<p><span style=\"font-weight: 400;\">WORKDIR &#8220;\/src\/MyApp&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">RUN dotnet build &#8220;MyApp.csproj&#8221; -c Release -o \/app\/build<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">FROM build AS publish<\/span><\/p>\n<p><span style=\"font-weight: 400;\">RUN dotnet publish &#8220;MyApp.csproj&#8221; -c Release -o \/app\/publish<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">FROM base AS final<\/span><\/p>\n<p><span style=\"font-weight: 400;\">WORKDIR \/app<\/span><\/p>\n<p><span style=\"font-weight: 400;\">COPY &#8211;from=publish \/app\/publish .<\/span><\/p>\n<p><span style=\"font-weight: 400;\">ENTRYPOINT [&#8220;dotnet&#8221;, &#8220;MyApp.dll&#8221;]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Build the Docker image.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">docker build -t myapp:latest .<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Push the Docker image to a container registry.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">docker tag myapp:latest &lt;your-registry&gt;\/myapp:latest<\/span><\/p>\n<p><span style=\"font-weight: 400;\">docker push &lt;your-registry&gt;\/myapp:latest<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Create a Kubernetes deployment and service for your .NET application.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># myapp-deployment.yaml<\/span><\/p>\n<p><span style=\"font-weight: 400;\">apiVersion: apps\/v1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kind: Deployment<\/span><\/p>\n<p><span style=\"font-weight: 400;\">metadata:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0name: myapp-deployment<\/span><\/p>\n<p><span style=\"font-weight: 400;\">spec:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0replicas: 3<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0selector:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0matchLabels:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0app: myapp<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0template:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0metadata:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0labels:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0app: myapp<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0spec:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0containers:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8211; name: myapp<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0image: &lt;your-registry&gt;\/myapp:latest<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ports:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8211; containerPort: 80<\/span><\/p>\n<p><span style=\"font-weight: 400;\">&#8212;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">apiVersion: v1<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kind: Service<\/span><\/p>\n<p><span style=\"font-weight: 400;\">metadata:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0name: myapp-service<\/span><\/p>\n<p><span style=\"font-weight: 400;\">spec:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0selector:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0app: myapp<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0ports:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0&#8211; protocol: TCP<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0port: 80<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0targetPort: 80<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0type: LoadBalancer<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Deploy the configuration to your Kubernetes cluster.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">kubectl apply -f myapp-deployment.yaml<\/span><\/p>\n<h3><b>Scaling and Updating Configuration<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">One of Kubernetes&#8217; core features is scalability. The application scales seamlessly, and etcd ensures that configuration consistency is maintained across all instances. Any change in the etcd configuration is immediately propagated to all running instances of your application, ensuring uniform behavior.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When you need to update configuration values, simply update them in etcd. The changes will be detected and applied dynamically across all relevant application instances without needing a restart.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">etcdctl put \/dotnet-config\/key &#8220;new-value&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Strategically using etcd within Kubernetes for managing .NET application configurations allows developers to build highly scalable, fault-tolerant, and dynamically managed environments. By following the steps outlined, you can ensure that your application benefits from the robustness and scalability inherent in Kubernetes, while leveraging the distributed key-value store capabilities of etcd for efficient configuration management.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Managing configurations efficiently is paramount. Combining the power of etcd, Kubernetes, and .NET Core, developers can orchestrate robust distributed key-value stores tailor-made for dynamic environments. This article delves into the intricacies of using etcd within Kubernetes to manage .NET Configuration settings effectively for containerized applications. Setting Up a Kubernetes Cluster with etcd To harness etcd [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-248","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/posts\/248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/comments?post=248"}],"version-history":[{"count":1,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/posts\/248\/revisions"}],"predecessor-version":[{"id":250,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/posts\/248\/revisions\/250"}],"wp:attachment":[{"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/media?parent=248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/categories?post=248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dotnetconfig.org\/blog\/wp-json\/wp\/v2\/tags?post=248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}