Dockerizing NerdDinner's configuration

NerdDinner uses Web.config for configuration—both for application configuration values that are constant between releases and for environmental configuration values that change between different environments. The configuration file is baked into the release package, which makes it awkward to change. In Chapter 3, Developing Dockerized .NET Framework and .NET Core Applications, I split the appSettings and the connectionStrings sections from Web.config into separate files; doing this lets me run a container with a different set of configurations, by attaching a volume containing different config files.

There are different types of configuration, though, and having to mount a volume is quite a heavy option for developers. It's good for feature settings that you want to toggle without changing code—settings like UnobtrusiveJavaScriptEnabled do belong in configuration files. But settings that change for every environment and every developer—like BingMapsKey—should have an easier way to set them.

Ideally you want multiple layers of configuration, reading from files but with the option to override values using environment variables. That's how the configuration system works in .NET Core, and because the configuration packages in .NET Core are actually .NET Standard libraries, they can be used in classic .NET Framework projects too.

In preparation for the bigger changes to come, I've updated the code for this chapter to use the .NET Core configuration model for all environment configuration settings, as shown in the following code. The previous files appSettings.config and connectionStrings.config, have been migrated to the new JSON configuration style in appsettings.json:

{
"Homepage": {
"Url": "http://nerd-dinner-hompage"
},
"ConnectionStrings": {
"UsersContext": "Data Source=nerd-dinner-db...",
"NerdDinnerContext": "Data Source=nerd-dinner-db..."
},
"Apis": {
"IpInfoDb": {
"Key": ""
},
"BingMaps": {
"Key": ""
}
}
}

The JSON format is easier to read, and because it contains nested objects, you can group similar settings together, which I've done with the Apis object. I can get the Bing Maps API key in my code by accessing the current config object with the key Apis:BingMaps:Key. I'm still storing the config file in a separate directory, so I can use a volume to override the whole file, but I've also set the configuration to use environment variables. This means that if an environment variable called Apis:BingMaps:Key is set, the value of that variable overrides the value in the JSON file. In my code, I just reference the configuration key, and at runtime, .NET Core fetches it from environment variables or the config file.

This approach lets me use default values for the database connection strings in the JSON file so that the app is usable when developers start the database and web containers without having to specify any environment variables. The app isn't 100% functional, though, because the API keys are needed for Bing Maps and the IP geolocation services. These are rate-limited services, so you are likely to have different keys for each developer and each environment, which can be set with environment variables in the web container.

To keep environment values safer, Docker lets you load them from a file rather than specifying them in plain text in the docker container run command. Isolating values in a file means that the file itself can be secured so that only administrators and the Docker service account can access it. The environment file is a simple-text format, with one line for each environment variable, written as a key-value pair. For the web container, my environment file contains the secret API keys:

Apis:BingMaps:Key=[your-key-here]
Apis:IpInfoDb:Key=[your-key-here]

To run the container and load the file contents as environment variables, you can use the --env-file option.

Environment values still aren't secure. If someone gains access to your app, they could print out all the environment variables and get your API keys. The approach I'm using with a JSON file as well as environment variables means I can use the same application image in production with Docker secrets for configuration—and that is secure.

I've packaged those changes in a new version of the NerdDinner Docker image, which you can find at dockeronwindows/ch05-nerd-dinner-web:2e. Like the other examples from Chapter 3, Developing Dockerized .NET Framework and .NET Core Applications, the Dockerfile uses a bootstrap script as the entry point, which promotes environment variables to the machine level so the ASP.NET application can read them.

The new version of the NerdDinner website runs in Docker with this command:

docker container run -d -P `
--name nerd-dinner-web `
--env-file api-keys.env `
dockeronwindows/ch05-nerd-dinner-web:2e

The application needs other components to be running for it to start correctly. I have a PowerShell script which starts containers in the right order with the right options, but by the end of the chapter this script will be unwieldy. I'll address that in the next chapter when I look at Docker Compose.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.246.223