Promoting environment variables
Modern apps increasingly use environment variables for configuration settings because they're supported by practically every platform, from physical machines to serverless functions. All platforms use environment variables in the same way, as a store of key-value pairs, so using environment variables for configuration, you make your app highly portable.
ASP.NET apps already have a rich configuration framework in Web.config, but with some small code changes, you can take key settings and move them to environment variables. This lets you build one Docker image for your app, which you can run in different environments, setting environment variables in containers to change configuration.
Docker lets you specify environment variables in the Dockerfile and give them initial default values. The ENV instruction sets environment variables, and you can set either one variable or many variables in each ENV, this example is from the Dockerfile for dockeronwindows/ch03-iis-environment-variables:
ENV A01_KEY A01 value ENV A02_KEY="A02 value" `
A03_KEY="A03 value"
Settings added to the Dockerfile with ENV become part of the image, so every container you run from the image will have these values set. When you run a container, you can add new environment variables or replace the value of existing image variables using the --env or -e option. You can see how environment variables work with a simple Nano Server container:
> docker container run `
--env ENV_01='Hello' --env ENV_02='World' `
microsoft/nanoserver `
powershell 'Write-Output $env:ENV_01 $env:ENV_02'
Hello
World
With apps hosted in IIS, there's a complication in using environment variables from Docker. When IIS starts, it reads all the environment variables from the system and caches them. When Docker runs a container with environment variables set, it writes them at the process level, but that's after IIS has cached the original values, so they don't get updated and IIS applications won't see the new value. IIS doesn't cache machine-level environment variables in the same way, though, so we can promote the values set by Docker to machine-level environment variables, and IIS apps will be able to read them.
Promoting environment variables can be done by copying them from the process level to the machine level. This PowerShell script does that by looping through all process-level variables and copying them to machine-level unless the machine-level key already exists:
foreach($key in [System.Environment]::GetEnvironmentVariables('Process').Keys) { if ([System.Environment]::GetEnvironmentVariable($key, 'Machine') -eq $null) { $value = [System.Environment]::GetEnvironmentVariable($key, 'Process') [System.Environment]::SetEnvironmentVariable($key, $value, 'Machine') } }
I can use this script block to the CMD instruction in my Dockerfile, but if I add that to the block to echo the log, the command runs to 10 lines, and it gets difficult to manage inside the Dockerfile. Instead, I've put the environment commands and the log echo commands into one script file and used that as ENTRYPOINT:
COPY bootstrap.ps1 C:\
ENTRYPOINT ["powershell", "C:\bootstrap.ps1"]
The application in the image is a simple ASP.NET Web Forms page that lists out environment variables. I can run this in a container in the usual way:
docker container run -d -P --name iis-env dockeronwindows/ch03-iis-environment-variables
When the container starts, I can get the IP address and open a browser on the ASP.NET Web Forms page:
$ip = docker inspect --format '{{ .NetworkSettings.Networks.nat.IPAddress }}' iis-env start "http://$ip"
I see output like this, with the default environment variable values from the Docker image:
You can run the same image with different environment variables, overriding one of the image variables and adding a new variable:
docker run -d -P --name iis-env2 ` -e A01_KEY='NEW VALUE!' ` -e B01_KEY='NEW KEY!' ` dockeronwindows/ch03-iis-environment-variables
Browse the container's IP address again, and you'll see the new values written out by the ASP.NET page:
I've added support for Docker's environment variable management into an IIS image now, so ASP.NET apps can use the System.Environment class to read configuration settings. I've retained the IIS log echo in this new image, so this is a good Docker citizen now you can configure the application and check the logs through Docker.
One last improvement I can make is to tell Docker how to monitor the application running inside the container, so Docker can determine whether the application is healthy and take action if it becomes unhealthy.