Docker Basics

A Docker container runs as a sandboxed process on a local system or a server, and consists of at least one image. An image is built from the source code of an application or service, and can be distributed, through a repository, to provide others the means of testing an existing software product with minimal setup.

Containers and images can be managed through the Docker Dashboard application. As such, a Docker container will require its own configurations and script that enable it to run as intended.

Though we are currently using the Docker Desktop application and Docker CLI, the repository of images is hosted by an Azure Container Registry.

Requirements

  • Docker Hub account
  • Docker Desktop application
  • Docker CLI
  • Windows Terminal or PowerShell
  • Azure CLI
  • Access to the repositories
  • Access to the Azure Container Registry

Getting Started

First, register an account with Docker Hub, at https://hub.docker.com/. After registering the account and doing whatever configuration is necessary, install the Docker Desktop client. The Docker CLI will also be required.

Note: When registering a Docker Hub account, make a note of the username/ID, as the portal logins sometimes don’t work with the registered email address.

There is a ‘Getting Started’ container, which has an nginx server and a few Web pages, that can be fetched and run, to check everything is working. To see this demo in action, run the following command in PowerShell or Windows Terminal:

docker run -d -p 80:80 docker/getting-started

Navigate the browser to http://localhost to view the site.

Creating an Image From an Existing Project

To build an image from a software project this into a Docker image, a ‘Dockerfile’ is added to the project directory. It might look something like:

# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

Then build the above project as a Docker container with the following command in the containing directory:

$docker build -t my-project .

The container should be ready to run with the following command:

$docker run -dp 3000:3000 my-project

The container should appear in the Docker Dashboard, where it could be managed.


Pushing Image to Azure Container Registry

docker tag local-image:tagname new-repo:tagname
docker push new-repo:tagname

The following is an example of the commands I used to push an image to the Docker Hub:

PS C:\Users\emma> docker login
Authenticating with existing credentials...
Login Succeeded
PS C:\Users\emma> docker tag test-application:dev test-repository/test-application:dev
PS C:\Users\emma> docker push test-repository/test-application:dev
The push refers to repository [docker.io/test-repository/test-application]
e7a596a1b658: Pushed
d89241eb9d34: Pushed
bb19eedc1dad: Pushed
d1597429d57f: Pushed
c891b6c5469a: Pushed
ad6562704f37: Pushed
dev: digest: [digest] size: 1578

  • latest
  • stage
  • production

Useful Commands

$docker image ls | grep [application-name]
$docker build -t [application-name]:latest
$docker build -t [application-name]:test
docker run -dp 3000:3000 [my-project]

Volumes

In Docker, volumes should be created if application data needs to be made persistent. A Docker volume is very much like a partition, with its own Linux file system. If a service or database image is to use a volume, it must be declared in the service section of docker-compose.yml and at the end of the file, e.g.

test.db:
    image: mcr.microsoft.com/mssql/server
    environment:
        - SA_PASSWORD=TestDB123
        - ACCEPT_EULA=Y
     ports:
        - "1433:1433"
     volumes:
        - test-datastore:/var/opt/mssql
...
volumes:
    - test-datastore:/var/opt/mssql

SQL Server Image

In order to set up a database for the Docker application, it’s necessary to have a base Microsoft SQL Server image. We can pull this from an external repository:

docker pull mcr.microsoft.com/mssql/server:2019-latest

The following command will initialise the database, configuring it to have the name ’testdb’, run on port 1433, and with the default login password of ‘TestDB123’. The database will use the test-datastore volume to make its data persistent:

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=TestDB123" -p 1433:1433 --name testdb -v test-datastore:/var/opt/mssql -d mcr.microsoft.com/mssql/server:2019-latest

In order to get a .NET application working with a SQL Server running as a Docker image, it might be necessary to use ‘host.docker.internal’ in the connection string as the host name, e.g.

"ConnectionStrings": {
  "DefaultConnection": "Server=host.docker.internal;Initial Catalog=dockertest;Integrated Security=false;User ID=SA;Password=StandardPass56"
}

If, for some reason, the project references an older database connection in Connected Services and Secrets.json, these must be removed, as the application will otherwise revert to that.

As for the docker-compose.yml configuration, this seems to work:

version: '3.4'

services:
  dotnet-core-6-mvc:
    image: ${DOCKER_REGISTRY-}dotnetcore6mvc
    build:
      context: .
      dockerfile: Test-MVC/Dockerfile

  DockerSqlServerNew:
    image: mcr.microsoft.com/mssql/server
    environment:
    - SA_PASSWORD=StandardPass56
    - ACCEPT_EULA=Y
    ports:
    - "1443:1433"

After running ‘docker-compose build’ and ‘docker-compose up’, the Docker registry should list a container that includes the DB service with the application.


Docker and Visual Studio

The first step is to set up Docker support in the Visual Studio project:

  • Right-click on the project root, and select ‘Add’ -> ‘Docker Support…’.
  • Right-click on the project root again, and select ‘Add’ -> ‘Container Orchestrator Support…’.

At this point, there should be a docker-compose section in the Visual Studio solution, with .dockerignore and docker-compose.yml files in the solution directory. There will also be a hidden override file there, plus a Dockerfile within the project. Combined, these should enable other developers to replicate the process of creating the images.

The docker-compose.yml file should initially contain the following:

version: '3.4'

services:
  dotnet-core-6-mvc:
    image: ${DOCKER_REGISTRY-}dotnetcore6mvc
    build:
      context: .
      dockerfile: Test-MVC/Dockerfile

Also, the Visual Studio debug options will be replaced with the configuration for Docker Compose. To run the project using IIS Express again, just click the drop-down with ‘docker-compose’ and select the original project name.

The .NET Core application will run as a Docker container, referenced in the Docker registry. Aside from not being able to connect to a local database server, it runs well enough.


Docker Compose Files

Each repository should include the following docker-compose files:

  • docker-compose.yml: This file is used for pulling all images from the Azure Container Registry and composing a Docker container with them. To be run within Windows Terminal or PowerShell. The image(s) for this project do not exist in the Azure Container Registry at the time of writing.
  • docker-compose-dependencies.yml: # This file is used for pulling images for any additional services required for debugging this project in the developer environment (e.g. Visual Studio or Visual Studio Code)
  • docker-compose-build.yml: This file is to be used when building a new Docker image from the source, with images for associated services being pulled from Azure Container Registry to ensure there are no breaking changes. Building this image and running it, alongside the others in a container, could be considered an integration test.

Process for Running Services in a Container

1. Clone the GitHub repository for the service/application being tested or built.

This repository should include all the required Docker configurations.

2. Autheticate the Windows Terminal or PowerShell session with Azure.

Use the az acr login command.

3. Get the image name(s) from the Azure Container Registry

az acr repository list --name [registry name]

4. Pull the required images from the Azure Container Registry

docker pull [registry].azurecr.io/[image name]

By default, the Azure client will pull the image tagged ’latest’.

5. Run the docker-compose script

How docker-compose should be used will depend on the context in which the container is to be run. To simply compose the container from a number of images, without making changes, navigate to the project’s directory and run the following command:

$ docker-compose -f docker-compose.yml up

To build a new image for the selected project, and run it in a container with other images:

$ docker-compose -f docker-compose-build.yml

And to build a container that enables debugging in an Integrated Developer Environment, with other images providing the dependencies:

$ docker-compose -f docker-compose-dependencies.yml

5. Run the docker-compose script

How docker-compose should be used will depend on the context in which the container is to be run. To simply compose the container from a number of images, without making changes, navigate to the project’s directory and run the following command:

$ docker-compose -f docker-compose.yml

To build a new image for the selected project, and run it in a container with other images:

$ docker-compose -f docker-compose-build.yml

And to build a container that enables debugging in an Integrated Developer Environment, with other images providing the dependencies:

$ docker-compose -f docker-compose-dependencies.yml

Troubleshooting (Getting Started)

  1. Open PowerShell as admin, and use the following:

    & 'C:\Program Files\Docker\Docker\DockerCli.exe' -SwitchDaemon
    
  2. In many cases the problem is caused by the Docker Engine failing to start. The indicators for this are the coloured bar at the lower left of the desktop GUI, and the set of ions to the right of the Windows task bar.

  3. Try enabling the Windows Subsystem for Linux (Turn Windows features on or off), and installing the latest WSL kernel update.


Troubleshooting (Building Docker Image from Source)

There were three problems encountered in getting one of the services to run as a Docker container.

  • Missing SDK Error: The two most likely causes are that a) The SDK base image doesn’t exist locally, and b) Docker is attempting to build the service without its dependencies, which are provided another project in the same solution directory. Check that docker-compose.yml is present in the solution’s root directory, and the Dockerfile is copying the entire solution directory into /app and building the service and the referenced projects. See the wiki page for this error for further details.

Other Information

Overview of docker-compose CLI