Docker containers
CICD Containers Continuous Deployment DevOps Docker Docker Compose Microservice Visual Studio YAML

How to Set Environment Variables within a Docker Container

Welcome to today’s post.

In today’s post I will be showing you how to setup your docker containers to be configured with environment variables.

In a previous post I showed how to build Docker containers using the tools in the Visual Studio development environment. However, in the post I did not show how to integrate other dependent services within the container images. That is where the use of environment variables becomes useful, and that is where this post is focused.

Why use Environment Variables in Docker containers?

Why do we need to use environment variables within our docker containers? Why can’t we just use the variables within our container applications and API services?

The reason is that when we are in a development or test environment, using settings internal to our applications might be acceptable in some situations, but when we deploy the docker images from test into a production environment, the settings we configure using our application configuration file, appSettings.json, can be exposed within the production environment. If we then store the application source within a source repository, any sensitive settings are exposed.

In the next two sections I will discuss the options for storing environments: either in the Docker script or outside of the script.

Storing Environment Settings in the Docker Compose Script File

One option is to store settings environment data in a docker-compose.yml script file with explicit key names and values:

version: '3.4'

services:
  bookloan.catalog.api:
    image: ${DOCKER_REGISTRY-}bookloancatalogapi
    environment: 
        - DB_CONN_STR=Server=172.x.x.x,1433;Database=aspnet-BookCatalog;User Id=xxxxx;Password=xxxxx;
        - URL_CATALOG_API=http://localhost/BookLoan.Catalog.API/
    build:
      context: .
      dockerfile: BookLoan.Catalog.API/Dockerfile

To limit exposure of our container application settings, we can store the environment settings variables outside of the container applications, and within an environment configuration file that is outside of the source code folder repository. 

Storing Environment Settings Outside of the Docker Compose Script File

To store our environment settings outside of the docker compose script file, we define our environment settings files with a .env file extension.

With docker compose, we define our environment settings files with a .env file extension.

An example is shown below:

bookloan_env.env:

# DB connection string
DB_CONN_STR=Server=172.x.x.x,1433;Database=aspnet-BookCatalog;User Id=xxxxx;Password=xxxxx;
# catalog API URL
URL_CATALOG_API=http://localhost/BookLoan.Catalog.API/

Accessing the environment file is done using a docker-compose script with an env_file section.

When the environment settings file is specified without a qualified path, the default location is the same folder as the current folder of the docker-compose.yml file.   

docker-compose.yml

version: '3.4'

services:
  bookloan.loan.api:
    image: ${DOCKER_REGISTRY-}bookloanloanapi
    env_file: 
      - bookloan_env.env

    build:
      context: .
      dockerfile: BookLoan.Loan.API/Dockerfile

Below is an example of how to specify the environment settings file with an absolute file path:

version: '3.4'

services:
  bookloan.loan.api:
    image: ${DOCKER_REGISTRY-}bookloanloanapi
    env_file: 
      - C:\development\environment\bookloan_env.env

    build:
      context: .
      dockerfile: BookLoan.Loan.API/Dockerfile

Running the Containers with Environment Variables in Visual Studio

We can run our container application within Visual Studio and view the environment variables within the docker container.

To view the environment variables we first list the running containers:

docker container ls

The output from the above command is shown below:

CONTAINER ID        IMAGE                 	COMMAND               CREATED             	STATUS              PORTS                   	NAMES
0678e489bb46        bookloanloanapi:dev   	"tail -f /dev/null"   15 minutes ago 	
Up 15 minutes       0.0.0.0:32774->80/tcp   	BookLoan.Loan.API

Obtain the container id, then run the following command to view the variables.

docker container inspect 0678e489bb46

The environment variables are within the values of the “Env” key of the JSON output:

…  
"Env": [
    "DB_CONN_STR=Server=172.x.x.x,1433;Database=aspnet-BookCatalog;User Id=xxxxx; Password=xxxxx;",
    "URL_CATALOG_API=http://localhost/BookLoan.Catalog.API/",
    "ASPNETCORE_ENVIRONMENT=Development",
    "DOTNET_USE_POLLING_FILE_WATCHER=1",
    "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true",
    "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages",
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "ASPNETCORE_URLS=http://+:80",
    "DOTNET_RUNNING_IN_CONTAINER=true"
],
…

Running the Containers with the Environment Variables outside of Visual Studio

Likewise, we can run our docker containers outside of the Visual Studio 2019 IDE in a command prompt and achieve the same outcome.

First run the build:

docker-compose build

The output from the above command is below:

Building bookloan.loan.api
Step 1/16 : FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
 ---> 46c1b808c34b
Step 2/16 : WORKDIR /app
 ---> Using cache
…
…
Step 16/16 : ENTRYPOINT ["dotnet", "BookLoan.Loan.API.dll"]
 ---> Running in 30b37d227063
Removing intermediate container 30b37d227063
 ---> 6a56c111df7c

Successfully built 6a56c111df7c
Successfully tagged bookloanloanapi:latest

Start the image within as a container application:

docker-compose up

The output for the above command is below:

Recreating bookloanloanapi_bookloan.loan.api_1 ... done                                                                 Attaching to bookloanloanapi_bookloan.loan.api_1
bookloan.loan.api_1  | warn: Microsoft.EntityFrameworkCore.Model.Validation[30000]
bookloan.loan.api_1  | No type was specified for the decimal column 'DailyOverdueFee' on entity type 'AdminFeeViewModel'. This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'.
…
bookloan.loan.api_1  | Hosting environment: Development
bookloan.loan.api_1  | Content root path: /app
bookloan.loan.api_1  | Now listening on: http://[::]:80
bookloan.loan.api_1  | Application started. Press Ctrl+C to shut down.

To test our container application can run with the consumed environment variables, we will need to obtain the IP address of the HostPort of the docker container.

After running the following command:

docker container ls

The output from the above command shows the IP address is 32776:

CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                   NAMES
07f105b07a22        bookloanloanapi       "dotnet BookLoan.Loa…"   2 minutes ago       Up 2 minutes        0.0.0.0:32776->80/tcp   bookloanloanapi_bookloan.loan.api_1

Then we just run a CURL command to call our API request within the docker container:

curl -X GET "http://localhost:32776/api/Loan/Details/1" -H "accept: */*"

{"id":1,"loanedBy":"billy@bookloan.com","dateLoaned":"2020-12-12T00:00:00","dateDue":"2020-12-26T00:00:00","dateReturn":"0001-01-01T00:00:00","onShelf":false,"dateCreated":"2020-12-12T00:38:18.4419333","dateUpdated":"2020-12-12T00:38:18.4419389","bookID":5,"book":null,"overdueLoans":null}

There may be possible problems running our container application with environment variables. When starting our docker container you may see the following state of the container:

This means that there was an abnormal exit of the container application, likely caused by an invalid configuration value or when retrieving the environment variable from the container application.

We can see a more detailed error when we try running docker-compose up:

docker-compose up

The output from the above command is shown below:

Creating network "bookloanloanapi_default" with the default driver
Creating bookloanloanapi_bookloan.loan.api_1 ... done                                                                   Attaching to bookloanloanapi_bookloan.loan.api_1
bookloan.loan.api_1  | Application startup exception: System.ArgumentNullException: 
Value cannot be null. (Parameter 'connectionString')
bookloan.loan.api_1  | at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
...
bookloan.loan.api_1  |    at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
bookloan.loan.api_1  |    at BookLoan.Loan.API.Program.Main(String[] args) in /src/BookLoan.Loan.API/Program.cs:line 18
bookloanloanapi_bookloan.loan.api_1 exited with code 139

To investigate this issue, either add logs to your application or re-run the container within the VS 2019 IDE debugger and trace the problem.

That’s all for today’s post.

In a future post I will discuss how to combine multiple images within the same container as a microservice.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial