Spending the day going over Docker topics.

Containers are really just about running software with all the things required bundled in and ready to go. When getting started, use that as the basis for why you’d use them, everything else complicates the discussion (but there are other benefits). The container means we can deploy and the user can assume the required things are included and ready to go.

Docker for Windows uses Hyper-V to be able to run the Linux containers. (That’s why you need Hyper-V to install). The benefit is that Windows can run both Windows and Linux containers. You’ll also need Hardware Virtualization available. There’s a few ways to check, but the quickest is just in task manager.


Once installed, we can use docker info to get the details about the Docker environment. One note that seems odd at first is that we see that Docker for Windows is ready to run Linux containers. We can use docker version to see the information about the client (which will be Windows).

docker run -dp 80:80 docker/getting-started

Will get a test container up and running. It’ll drag it down from the Docker repo, then start it on port 80 on localhost. (80:80 mapping is host:container)

We can also use the format

docker run -p 8080:80 nginx

Then, we use docker ps to see the running containers.

We can use docker stop d86 where d86 is the start of a container Id (or name).

We can use docker ps -a to see all installed containers, where ps on its own only shows running containers.

We can use docker rm d86 to remove a container. You can string multiple together like docker rm d86 d99 t44


What about Windows Containers though?

You switch modes to get the Windows containers in a desktop environment:


Then we could do something like docker run -p 8080:80 -d --name iis microsoft/iis:nanoserver where nanoserver is the tag. Leaving it default would have used full server core because it is the image assigned to the default tag.

BUT! That’s for us to mess about. For a production environment, you’d configure a windows server to host Windows containers or a Linux server to run Linux containers.

docker inspect 6ac can show us the detail of the container. The container IP is interesting for example - its the IP that the container apps are bound to, you can connect to them via that IP as well (or ping it etc).

c:\temp>docker inspect 6a
        "Id": "6ac057b18be4703a92928dca90ddfa9953a4efc01ca337c194c469966a831567",
        "Created": "2020-05-25T17:11:48.370351Z",
        "Path": "C:\\ServiceMonitor.exe",
        "Args": [
<< snip >>
            "Networks": {
                "nat": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "661669bb21c5ac004fdb0ec6ddec297403af311de07aedf6425717ab6b754b97",
                    "EndpointID": "6f34e274c25df87366504b964095f5d021106f162e62ec6b7c306f15ef69ac31",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "00:15:5d:98:91:7b",
                    "DriverOpts": null

Platform Component Differences

For Linux Containers:

  • Proxy: com.docker.proxy
  • VM on Hyper-V
  • “docker run nginx” –> com.docker.proxy –> Linux VM –> container

For Windows (Desktop) Containers:

  • dockerd.exe (docker engine for Windows)
  • com.docker.proxy
  • “docker run iis” –> com.docker.proxy –> dockerd.exe –> container

For Windows Containers:

  • dockerd.exe
  • “docker run iis” –> dockerd.exe –> container

Installing for Windows Server

Install-Module DockerMsftProvider -Force
Install-Package Docker -ProviderName DockerMsftProvider -Force
WARNING: A restart is required to enable the containers feature. Please restart your machine.

Name                           Version          Source           Summary
----                           -------          ------           -------
Docker                         19.03.5          DockerDefault    Contains Docker EE for use with Windows Server.

(Installs dockerd.exe and docker.exe in c:\program files\docker and configures dockerd as a service)

Jumping ‘into’ a Windows container

docker run -it mcr.microsoft.com/windows/servercore:1809

Remember that all containers on the host are sharing the same kernel.

The container is the usermode binaries. Even in windows case, this will be the win32 libraries that the apps use, that make the system calls to the kernels.

We use layers in containers to share some of the common libraries between containers. Docker uses read only layers for the base OS things.

Windows Container Compatibility

Here’s the matrix showing which containers run on which hosts. “As we’ve been improving the Windows container features, we’ve had to make some changes that can affect compatibility. Older containers will run the same on newer hosts with Hyper-V isolation, and will use the same (older) kernel version. However, if you want to run a container based on a newer Windows build, it can only run on the newer host build.”

It’s actually pretty strict for process isolation without Hyper-V; the pattern below repeats for all the OS tabs. You need to match the container and host OS release.


While it makes sense with the shared kernel and all, it puts a bit of a downer in the container benefits column for Windows. When you get it wrong, it looks like this:

PS C:\Users\administrator.JMPESP> docker run -it microsoft/dotnet:nanoserver
C:\Program Files\Docker\docker.exe: Error response from daemon: hcsshim::CreateComputeSystem 8f5ff8b49acbdedaba339474e101cf5ab640a195013cbf03c4010b3a35300a42: The container operating system does not match the host operating system.
(extra info: {"SystemType":"Container","Name":"8f5ff8b49acbdedaba339474e101cf5ab640a195013cbf03c4010b3a35300a42","Owner":"docker","VolumePath":"\\\\?\\Volume{7a71c6ab-6183-4bf0-8485-f70f9e853e12}","IgnoreFlushesDuringBoot":true,"LayerFolderPath":"C:\\ProgramData\\docker\\windowsfilter\\8f5ff8b49acbdedaba339474e101cf5ab640a195013cbf03c4010b3a35300a42","Layers":[{"ID":"64412752-35ed-5a44-bc8e-2c828a2d0086","Path":"C:\\ProgramData\\docker\\windowsfilter\\48a2a6bd0296616476652b32588fd4dd687d6309aeb21ff351c1ed372ddd216d"},{"ID":"882f1b68-9839-5672-a0b7-4ec6c4b0c48b","Path":"C:\\ProgramData\\docker\\windowsfilter\\24bfe125698be2d11a0f25c1c47e8921a30bc33cf3af5a3c60f2ca29bf131707"},{"ID":"6c2e4657-f2ee-58bf-b849-396d6492ae9f","Path":"C:\\ProgramData\\docker\\windowsfilter\\a03f1a7ba9c37165e258a1d7c8bfc8b9224961dfb8214ea25e2c47dd433fed79"},{"ID":"207587ea-8db4-57b5-b258-c531ad292776","Path":"C:\\ProgramData\\docker\\windowsfilter\\2d717da57f8ab8aa6552b580645ab563bcd850b59039f59930e6c5c47010fb02"},{"ID":"714f130d-03cb-590f-ae06-516cd25fef1c","Path":"C:\\ProgramData\\docker\\windowsfilter\\7e149efdd60f07074b26448db7448e1c08ffcca209e7fcfc579224574f8c8f82"},{"ID":"e7a6ef02-1675-5324-8ec7-9f5411a68950","Path":"C:\\ProgramData\\docker\\windowsfilter\\89eba2053a56a03c0de6afc623273987bdf3ae97ced6f843eba9e337f3e4ea9e"},{"ID":"b2d97b4d-dc64-567b-ba20-34ddbd0482ce","Path":"C:\\ProgramData\\docker\\windowsfilter\\93dbd7e7f05ed00ccaea9811236d5c1c4f7bd50eb930b4471b850cd42077b2f4"},{"ID":"3049dd7e-a22d-5571-a379-cacde34905b5","Path":"C:\\ProgramData\\docker\\windowsfilter\\b226494957ff6a6e975b6f82671976690f11160ecf90341f4e52a41d07917b52"},{"ID":"d7b20a56-b3a2-5798-a252-eed5c6c3c220","Path":"C:\\ProgramData\\docker\\windowsfilter\\9ba3b245bf59aa766f4f2e375608224ad77928e99e6b97a562f4076c500e8732"}],"HostName":"8f5ff8b49acb","HvPartition":false,"EndpointList":["E19A9E42-D3B3-4ACD-A3DA-2820A67B5639"],"AllowUnqualifiedDNSQuery":true}).


  • Windows Server containers use isolation similar to the Linux approach (process isolation). This wont run on the client SKU’s, requires server SKU.
  • Windows Hyper-V containers have isolation via the Hypervisor (vm isolation). If the machine has Hyper-V components installed the container can be launched with --isolation=hyperv to force this mode.

Save image:

docker save mcr.microsoft.com/dotnet/framework/runtime:4.8-20200527-windowsservercore-2004 -o windows.rar

Building images:

You’ll speed it up and also know if the architecture will run on your platform if you try to run the base of your new container first. Like:

docker run mcr.microsoft.com/windows/servercore:ltsc2019

Then, customize a Dockerfile file:

# escape=`
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN powershell -Command `
Add-WindowsFeature Web-Server; `
Invoke-WebRequest -UseBasicParsing -Uri "https://dotnetbinaries.blob.core.windows.net/servicemonitor/" -OutFile "C:\ServiceMonitor.exe"
ENTRYPOINT ["C:\ServiceMonitor.exe", "w3svc"]

^ or a more simple example might be copying in some content for a websute in a folder called “app”

FROM mcr.microsoft.com/windows/servercore/iis

COPY app c:/inetpub/wwwroot

Then from the location you saved Dockerfile run:

docker build -t iis_test .

(iis_test is the new name for our container)

It will hopefully look like this:

PS C:\Users\Administrator\Desktop> docker build -t iis_test .
Sending build context to Docker daemon  4.608kB
Step 1/4 : FROM mcr.microsoft.com/windows/servercore:ltsc2019
 ---> 561b89eac394
Step 2/4 : RUN powershell -Command Add-WindowsFeature Web-Server; Invoke-WebRequest -UseBasicParsing -Uri "https://dotnetbinaries.blob.core.windows.net/servicemonitor/" -OutFile "C:\ServiceMonitor.exe"
 ---> Running in 426c47316627

Success Restart Needed Exit Code      Feature Result
------- -------------- ---------      --------------
True    No             Success        {Common HTTP Features, Default Documen...

Removing intermediate container 426c47316627
 ---> 10c16f5d1b79
Step 3/4 : EXPOSE 80
 ---> Running in cf424cb1fac7
Removing intermediate container cf424cb1fac7
 ---> 97b3f30fc866
Step 4/4 : ENTRYPOINT ["C:\ServiceMonitor.exe", "w3svc"]
 ---> Running in beb6bf91fcca
Removing intermediate container beb6bf91fcca
 ---> 5912408a16eb
Successfully built 5912408a16eb
Successfully tagged iis_test:latest
PS C:\Users\Administrator\Desktop>

We should be able to run it like this:

docker run -p 8080:80 -d iis_test:latest

Because i’m on Windows i have that issue with accessing it on the local host via the port i just mapped. For that i can use the internal address. I can either create a shell (docker exec {identifier} -it powershell)into the machine or do something like docker inspect

Removing all the stopped containers in the lab

docker container prune

Docker Compose

Build sets of related docker containers from a .yaml file.

docker-compose up Do the yaml.

We get an embedded DNS server for internal resolution of the machine names which will work on the internal docker engine switch. That allows us to pass them as environment variables in the yaml for complex applications with dependencies between containers.


We can mess with them with docker network


PS C:\Users\Administrator> docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
8cbd7933a480        nat                 nat                 local
a12ad0a25fcc        none                null                local

We can pass a --net parameter with docker run to connect to specific networks if needed.

Troubleshooting tool for Microsoft Server Docker hosts

Invoke-WebRequest https://aka.ms/Debug-ContainerHost.ps1 -UseBasicParsing | Invoke-Expression