This article is second part of the series on Deploying Angular, ASP.NET Core and SQL Server on Linux to Azure Kubernetes Service (AKS) cluster. The first part, describes steps needed to deploy these components to AKS. App configuration in ASP.NET Core is based on key-value pairs established by configuration providers. Configuration providers read configuration data into key-value pairs from a variety of configuration sources. In this article I am going to share multiple ways to load App configuration in ASP.net Core Web API
- Hosting Environment specific appsettings.json
- Dockerfile Environment Variables
- Kubernetes
- Container Environment variables with data from ConfigMap/Secret
- Populate Volume (Config file) with data stored in a ConfigMap/Secret
- Azure Key Vault Secrets
The tools used to develop these components are Visual Studio for Mac/VS Code/VS 2017, AKS Dashboard, Docker for Desktop and kubectl. The formatting of code snippets in this article may get distorted (especially yaml), thus please refer to GitHub repository for complete source code for this article.
I have extended sample solution of first part of the series by adding new files i.e. ConfigController, Kubernetes_ConfigMap.yaml and Kubernetes_Deployment_V2.yaml
Hosting environment specific appsettings.json
Hosting environment specific versions of appsettings.json is one of the ways to define App configuration in ASP.net core. In ConfigureAppConfiguration
method, based on Hosting environment, App Config will be loaded as displayed below
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true)
I have defined a few configuration settings in appsettings.json
{
"AppConfiguration": {
"DatabaseConnectionString": "Server=mssql-sample-service,1433;Database=UsersDB;User Id=sa;Password=P@ssword1$;",
"IsVaultEnabled": false,
"Vault": "AZURE_KEY_VAULT_DNS_NAME",
"ClientId": "APPLICATION_ID",
"ClientSecret": "APPLICATION_KEY",
"DatabaseConnectionStringFromAppsettings": "DatabaseConnectionStringFromAppsettingsValue"
}
}
The Production environment file i.e. appsettings.Production.json also defines DatabaseConnectionStringFromAppsettings
setting, thus during Production Environment build, configuration from this file will get applied based on precedence.
{
"AppConfiguration": {
"DatabaseConnectionStringFromAppsettings": "Prod:DatabaseConnectionStringFromAppsettingsValue"
}
}
Dockerfile Environment Variable
Environment variables can be defined in Dockerfile and args can be passed during building Docker image. In ConfigureAppConfiguration
method, config.AddEnvironmentVariables()
loads environment variables. In DockerFile displayed below, argument ARG dbConnectionString
is defined along with environment variable ENV DatabaseConnectionString
. This ARG value can then be specified when building Docker image and the command is docker build --build-arg dbConnectionString=DatabaseConnectionStringFromDockerEnvVariable -t testappwebapi .
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
ARG dbConnectionString
ENV DatabaseConnectionStringFromDockerEnvVariable $dbConnectionString
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["SampleWebApp/SampleWebApp.csproj", "."]
RUN dotnet restore "SampleWebApp.csproj"
COPY . .
RUN dotnet build "SampleWebApp.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "SampleWebApp.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "SampleWebApp.dll"]
ConfigMaps and Secrets in Kubernetes
ConfigMaps and Secret allows you to decouple configuration artifacts from image content to keep containerized applications portable. For this article, I will show how to use ConfigMap to define app configuration.
Container Environment variables with data from ConfigMap/Secret
The ConfigMap resource displayed below defines DatabaseConnectionStringFromKubernetesEnvVariable
setting.
apiVersion: v1
kind: ConfigMap
metadata:
name: samplewebapp-configmap-1
namespace: default
data:
DatabaseConnectionStringFromKubernetesEnvVariable: DatabaseConnectionStringFromKubernetesEnvVariableValue
Container environment variables can be set from Secret using env[].valueFrom.secretKeyRef
and from ConfigMap using env[].valueFrom.configMapKeyRef
. Container environment variable defined in deployment resource(Kubernetes_Deployment_V2.yaml) will be set from ConfigMap and the partial yaml snippet is
env:
- name: DatabaseConnectionStringFromKubernetesEnvVariable
valueFrom:
configMapKeyRef:
name: samplewebapp-configmap-1
key: DatabaseConnectionStringFromKubernetesEnvVariable
Populate Volume (Config file) with data stored in a ConfigMap/Secret
This is similar to previous step however in this case, data stored in ConfigMap will be mounted to a json file. ASP.net core web api will load App Configuration from this json file. The ConfigMap resource displayed below defines data for AppConfig.json:
apiVersion: v1
kind: ConfigMap
metadata:
name: samplewebapp-configmap-2
namespace: default
data:
AppConfig.json: |-
{
"AppConfiguration": {
" DatabaseConnectionStringFromKubernetesMountedFile": "DatabaseConnectionStringFromKubernetesMountedFileValue"
}
}
AppConfig.json
file will be mounted from ConfigMap in deployment resource(Kubernetes_Deployment_V2.yaml). The partial yaml snippets where AppConfig.json
is mounted to path mountPath: /app/AppConfig.json
from samplewebapp-configmap-2
ConfigMap is
volumeMounts:
- name: samplewebapp-configmap-2
mountPath: /app/AppConfig.json
subPath: AppConfig.json
volumes:
- name: samplewebapp-configmap-2
configMap:
name: samplewebapp-configmap-2
In ConfigureAppConfiguration
method, App configuration is loaded from AppConfig.json
config.AddJsonFile("/app/AppConfig.json", optional: false, reloadOnChange: true
Azure Key Vault Secrets
Azure Key Vault can be used to securely store and tightly control access to tokens, passwords, certificates, API keys, and other secrets. Centralizing storage of application secrets in Azure Key Vault allows you to control their distribution. Key Vault greatly reduces the chances that secrets may be accidentally leaked. I will provide the steps needed to load App Configuration from Azure Key Vault secrets.
Create Azure AD Application
For Service-to-Azure-Service authentication, this approach involves creating an Azure AD application and associated credential, and using that credential to get a token. This approach does has short comings i.e. application credentials need to be specified in code and credentials expiry which Managed Service Identity fixes. Azure Kubernetes Service(AKS) is not currently natively integrated with Azure Key Vault however, the Azure Key Vault FlexVolume for Kubernetes project enables direct integration from Kubernetes pods to KeyVault secrets.
Navigate to Azure AD > App Registrations > New Application Registration
and select Application Type as Web app/API and specify Name and Sign-on URL
After Azure AD Application is created, navigate to the resource and take note of Application ID which is needed to be specified for ClientID in ASP.net Core Web API appsettings.json.
Open Settings and create a new Key. Take note of the value (you can only copy after saving) which is needed to be specified for ClientSecret in ASP.net Core Web API appsettings.json.
Create Azure Key Vault
Create a key vault resource using Azure CLI or Portal
You need to specify Access policies. Select principal as Application you created in previous step and grant permissions e.g. I have granted Read and List Secret permissions.
Create a Secret and specify Name and Value. The value for this secret is going to be loaded in App Configuration.
Keep note of the Key Vault’s DNS name which is needed to be specified for Vault in ASP.net Core Web API appsettings.json.
ASP.net Core Web API
Add Azure Key Vault Configuration provider nuget package Microsoft.Extensions.Configuration.AzureKeyVault
.
In ConfigureAppConfiguration
method snippet displayed below, App configuration is loaded from Azure Key Vault. As described above, you need to specify values for Vault, ClientId and ClientSecret in appsettings.json. Along with this you need to set IsVaultEnabled
to true in appsettings.json.
if (Convert.ToBoolean(configInProgress["AppConfiguration:IsVaultEnabled"]))
{
config.AddAzureKeyVault(configInProgress["AppConfiguration:Vault"],
configInProgress["AppConfiguration:ClientId"],
configInProgress["AppConfiguration:ClientSecret"]);
config.Build();
}
In ConfigureServices method of Startup class, Configuration instance is registered with AppConfiguration class
services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));
services.Configure<AppConfiguration>(Configuration);
public class AppConfiguration
{
public string DatabaseConnectionString
{
get;
set;
}
public bool IsVaultEnabled
{
get;
set;
}
public string Vault
{
get;
set;
}
public string ClientId
{
get;
set;
}
public string ClientSecret
{
get;
set;
}
public string DatabaseConnectionStringFromAppsettings
{
get;
set;
}
public string DatabaseConnectionStringFromDockerEnvVariable
{
get;
set;
}
public string DatabaseConnectionStringFromKubernetesEnvVariable
{
get;
set;
}
public string DatabaseConnectionStringFromKubernetesMountedFile
{
get;
set;
}
public string DatabaseConnectionStringFromAzureKeyVault
{
get;
set;
}
}
The code snippet from ConfigController displays App Configuration values from all the sources
[HttpGet]
public IEnumerable<string> Get()
{
return new List<string>()
{
$"DatabaseConnectionStringFromAppsettings: {_appSettings.Value.DatabaseConnectionStringFromAppsettings}",
$"DatabaseConnectionStringFromDockerEnvVariable: {_appSettings.Value.DatabaseConnectionStringFromDockerEnvVariable}",
$"DatabaseConnectionStringFromKubernetesEnvVariable: {_appSettings.Value.DatabaseConnectionStringFromKubernetesEnvVariable}",
$"DatabaseConnectionStringFromKubernetesMountedFile: {_appSettings.Value.DatabaseConnectionStringFromKubernetesMountedFile}",
$"DatabaseConnectionStringFromAzureKeyVault: {_appSettings.Value.DatabaseConnectionStringFromAzureKeyVault}"
};
}
Build the docker image and deploy the resources to Azure Kubernetes Service. Browse to “http://YOUR_HOST/api/Config” to see list of App Configurations with Values populated from these sources.
The source code for this article can be downloaded from GitHub repository
No comments:
Post a Comment