Docker on Windows

Posted in container on January 11, 2017 by Adrian Wyssmann ‐ 8 min read

Everyone loves docker even Microsoft embraces docker.

docker loves windows

Back in September Docker and Microsoft announced the commercial partnership to extend Docker Engine to Windows Server. This is interesting, then so far Docker was available for Linux only. Sure, you could install the Docker Toolbox on Windows, but this solution uses boot2docker is a lightweight Linux distribution based on Tiny Core Linux. So the images that run in containers still use a Linux-based host and therefore you cannot run native Windows applications.

user@windowshost MINGW64 /u
$ docker version
Client:
 Version:      1.12.0
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   8eab29e
 Built:        Thu Jul 28 23:54:00 2016
 OS/Arch:      windows/amd64

Server:
 Version:      1.12.3
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   6b644ec
 Built:        Wed Oct 26 23:26:11 2016
 OS/Arch:      linux/amd64

This is a is a huge limitation for usage of Docker for x-platform development and deployment so for example if you would like to run or test your asp.net application in an IIS. With the support of native Windows applications in Docker running on Windows 10 Pro, Enterprise and Education (1511 November update, Build 10586 or later). To do so you need to download and install Docker for Windows.

As on my work computer I still run Windows 7 I try to run Docker for Windows on a VM running in Virtualbox. Unfortunately I just get this error:

`Unable to write to the database. Exit code: 1`
``   at Docker.Backend.Database.WriteKeys(ICollection`1 keyValues)``
``   at Docker.Backend.Database.ModifyDatabase(ICollection`1 keyValuesToWrite, ICollection`1 keysToRemove)``
`   at Docker.Backend.Database.Write(Settings settings)`
`   at Docker.Backend.ContainerEngine.Linux.DoStart(Settings settings)`
`   at Docker.Backend.ContainerEngine.Linux.Start(Settings settings)`
`   at Docker.Core.Pipe.NamedPipeServer.<>c__DisplayClass8_0.<Register>b__0(Object[] parameters)`
`   at Docker.Core.Pipe.NamedPipeServer.RunAction(String action, Object[] parameters)`

Trying to run within powershell I get another error:

C:\Users\me>docker info
An error occurred trying to connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/info: open //./pipe/docker_engine: The system cannot find the file specified.

A search in internet brings me to the this thread which one of the responses is

Since Docker for Windows also uses virtualisation the underlying system needs to support nested virtualisation. We have no control nor visibility on how the Windows VM is configured nor how well the various hypervisors support nested virtualisation (it’s very hard to get right) so we can’t really support this scenario.

So I create an VM in azure and follow the steps found in this blog to install Docker on the machine, which works without hassle. So running docker version shows me

PS C:\Users\me> docker version
Client:
 Version:      1.12.2-cs2-ws-beta
 API version:  1.25
 Go version:   go1.7.1
 Git commit:   050b611
 Built:        Tue Oct 11 02:35:40 2016
 OS/Arch:      windows/amd64

Server:
 Version:      1.12.2-cs2-ws-beta
 API version:  1.25
 Go version:   go1.7.1
 Git commit:   050b611
 Built:        Tue Oct 11 02:35:40 2016
 OS/Arch:      windows/amd64

Running docker containers

Now I can run a windows based docker image.

PS C:\Users\me> docker run microsoft/dotnet-samples:dotnetapp-nanoserver
Unable to find image 'microsoft/dotnet-samples:dotnetapp-nanoserver' locally
dotnetapp-nanoserver: Pulling from microsoft/dotnet-samples

bce2fbc256ea: Pull complete
10bf725c5388: Pull complete
ab745e0519ee: Pull complete
0d586140722a: Pull complete
e2f9be25b2f7: Pull complete
c69b9ada9b2a: Pull complete
2e77a553540a: Pull complete
e68e2b8fe22c: Pull complete
3d0afdc51bb1: Pull complete
Digest: sha256:998ee3f2f5ba0f6aea23bed21ac36a81f1b171b44da187c2e9e886a13c049d76
Status: Downloaded newer image for microsoft/dotnet-samples:dotnetapp-nanoserver

        Dotnet-bot: Welcome to using .NET Core!
    __________________
                      \
                       \
                          ....
                          ....'
                           ....
                        ..........
                    .............'..'..
                 ................'..'.....
               .......'..........'..'..'....
              ........'..........'..'..'.....
             .'....'..'..........'..'.......'.
             .'..................'...   ......
             .  ......'.........         .....
             .                           ......
            ..    .            ..        ......
           ....       .                 .......
           ......  .......          ............
            ................  ......................
            ........................'................
           ......................'..'......    .......
        .........................'..'.....       .......
     ........    ..'.............'..'....      ..........
   ..'..'...      ...............'.......      ..........
  ...'......     ...... ..........  ......         .......
 ...........   .......              ........        ......
.......        '...'.'.              '.'.'.'         ....
.......       .....'..               ..'.....
   ..       ..........               ..'........
          ............               ..............
         .............               '..............
        ...........'..              .'.'............
       ...............              .'.'.............
      .............'..               ..'..'...........
      ...............                 .'..............
       .........                        ..............
        .....


**Environment**
Platform: .NET Core 1.0
OS: Microsoft Windows 10.0.14393

What happens if I try to run a windows based docker image on Linux or vice versa? Well as docker images are bound to the host system a windows based image will not run under Windows and actually return an error docker: unknown blob

[me@dockerhost: ~]$ docker run microsoft/dotnet-samples:dotnetapp-nanoserver
Unable to find image 'microsoft/dotnet-samples:dotnetapp-nanoserver' locally
dotnetapp-nanoserver: Pulling from microsoft/dotnet-samples
bce2fbc256ea: Retrying in 1 second
10bf725c5388: Downloading
ab745e0519ee: Download complete
0d586140722a: Download complete
e2f9be25b2f7: Download complete
c69b9ada9b2a: Download complete
2e77a553540a: Download complete
e68e2b8fe22c: Download complete
3d0afdc51bb1: Download complete
docker: unknown blob.
See 'docker run --help'.

As on Linux, you can also create interactive sessions for instance by running docker run -it microsoft/nanoserver powershell which results in a running container:

PS C:\Users\me> docker ps
CONTAINER ID        IMAGE                  COMMAND             CREATED             STATUS              PORTS               NAMES
cfc688d6887a        microsoft/nanoserver   "powershell"        3 minutes ago       Up 2 minutes                            grave_meitner

You can also mount data volumes. For this purpose I simply create a folder C:\test on my Windows Server and create a file win2016_me.txt within the folder. Now I can specify with -v to map the data volume to e.g. C:\example

PS C:\Users\papanito> docker run -it  -v C:\test:C:\example microsoft/nanoserver powershell

… and examine the content within the container

Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.
PS C:\> ls .\example

    Directory: C:\example

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       12/23/2016  10:47 AM            106 win2016_me.txt

PS C:\> cat .\example\win2016_me.txt
This file was created by user 'me' on windows host

Cgroups, Namespaces and Overlay FS?

Linux uses kernel features like cgroups, kernel namespaces and an overlay file system for isolation. To support container in windows similar features are required and therefore required architectural changes which are only available from certain Windows versions (Windows 10 Anniversary Update, Some versions of Windows 2016 Server). This presentation gives some more insights into the architecture.

Generally Docker uses Job Objects a similar feature as cgroups for Linux

Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. Operations performed on a job object affect all processes associated with the job object. Examples include enforcing limits such as working set size and process priority or terminating all processes associated with a job.

On the other hand we have the Object Namespaces which corresponds to kernel namespaces on Linux:

An object namespace protects named objects from unauthorized access. Creating a private namespace enables applications and services to build a more secure environment.

The last piece is the “Layer Capabilities” of the filesystem. Due to the amount of features provided by the NTFS, Microsoft decided not to completely build a new UFS layer for NTFS but rather provide use a hybrid approach. Each container gets it’s own virtual disk with NTFS file system and files are linked back to the source (on the host) via symlinks (also called reparse points). When files are changed these changes are the made persistent into the virtual block device. For the Windows registry on the other hand a true union FS was built to avoid cloning of full set of registry hives per container.

Types of containers

Windows container technology supports two types of containers: Windows Server-Container und Hyper-V-Container. Both are handled the same way and use the same images. The main difference is the degree of isolation according to this page

Windows Server containers: Multiple container instances can be run on a single host at the same time, using isolation technologies for namespaces, resource control, and processes. Windows Server containers share with each other and the same kernel with the host.

Hyper-V containers: Multiple container instances can run on one host at a time, but each container runs on a particular virtual machine. This results in isolation at the core level between each Hyper-V container and the container host.

https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/hyperv-container actually give you a nice explication about the differences of the isolation. Windows Server containers share the same host and behave similar than containers on Linux. For example if we run a “ping” command within a container we actually can see the process running on the host system but we also see clearly that it has a “Job Object ID” associated - the same as the powershell which runs inside the container.

When running a Hyper-V container - started with parameter --isolation=hyperv - the ping process would be actually hidden from the host system. The difference to the “Windows Server Containers” is that instead a single shared kernel there is single shared hypervisor and multiple kernels on top. So for each container there is a separate VM instance running. The problem with VMs is the extra overhead required for each machine like the additional memory consumption or the relatively long startup for a VM. The Docker team addressed this issue with different measure in the implementation. To reduce the VM footprint Docker team create a small, stateless VM running a special Windows distribution containing only the necessary services and kernel configuration required to run containers - they call it “utility VM”.

In addition Docker takes advantage of how the access of the host file system is implemented. Hyper-V-Containers are using  SMB protocol over the VMBus -  a logical inter-partition communication channel to map portions of the host file system into the VM. This also enables to physically map portions of the executable into the VM so that they can be run from the file cache.

As a last piece they implemented a so called “cloning” functionality. As you can see in the image above the only thing that differs from Hyper-V Container to Hyper-V Container is the “Windows Server Container” part. All the rest, like the Windows Kernel or the Basic System Processes are the same for all VMs. Therefore to avoid duplication of memory, an initial VM is started. Once this is running, the memory state of the VM is freezed by pausing all virtual processes and shared. Now when a container is started, Docker forks the VM and use the shared memory state.

A quick demo of that can be found in the presentation at dockercon16.

Update 2019-09-25

Updated outdated links