Windows Docker Containers

Introduction

In this post I am going to show step by step how to install a local version of Windows Server 2016 TP5 in Hyper-V and how to configure it to be a container host.

Tutorial

It is still not straight forward to configure a Windows Server 2016 as a container host and use Docker to build and run containers. Microsoft provides pre-configured VMs in Azure but I want to run a Windows Container host on my developer machine in Hyper-V. Up to TP4 I failed to do this. Now that TP5 is finally out I tried again and after some first failures I finally succeeded. I want to describe the steps necessary to achieve this on my machine and I hope that I can help some folks out there trying the same to avoid all the hassle I went through.

Enable Hyper-V

First we need to enable Hyper-V on our laptop. Do this via Control Panel. In the Programs section select Turn Windows features on or off.

Download Windows Server 2016 TP5

Once the feature is installed we have to reboot our machine. Next we want to download Windows Server 2016 TP5. We can get it from here. Make sure you download Windows Server 2016 Technical Preview 5 and not the Hyper-V or the Essentials version.

Define a switch

Now we can start the Hyper-V management console. First we add a new virtual switch. Make sure that you select the type External and that you link it to you Ethernet card. Note that there is an error that makes it impossible to link it to a WLAN port. I lost quite some time trying to configure my switch when I was on WLAN and not on a wired connection.

Create the VM

Once we have defined the external switch we can create a new VM. Navigate through the VM creation wizard and create a new VM. When asked, mount the ISO that you just downloaded to the VM. Once you’re done, start the VM. First we are asked to define a new password for the Admin. Then we can start working. Make sure that you have internet connection using e.g. ping or ipconfig.

Make it a container host

Now we are ready to make this Windows Server 2016 TP5 VM a container host. For this we use Powershell. On the command prompt enter powershell. Next we load a script prepared by Microsoft from http://aka.ms. We use the wget command for this. Please note the URL that we’re using. It is different from what is published in many documents and videos of Microsoft.

wget http://aka.ms/tp5/install-ContainerHost -OutFile c:\install-ContainerHost.ps1

Now we can use this Powershell file to configure our server as a container host by executing this command

powershell -NoProfile -ExecutionPolicy Bypass c:\install-ContainerHost.ps1

Now we need to be patient, very patient. You might wanna brew some coffee and watch a nice movie while the script is running. It took more than an hour on my decently fast machine having an SSD as the main drive. A lot of things are going to happen. The server is also going to reboot some time into the operation. After the reboot the script will download the WindowsServerCore container image and extract it. Your screen should look similar to this

When the script execution has finally come to an end we **need* to restart the server otherwise Docker won’t see the new image when executing docker images. When I executed the said command without first restarting the Docker Daemon I nearly got a heart attack since no image was listed. Another waste of time I thought, but then I did what I always do when nothing works anymore… We can do this like this net stop "Docker Daemon"
net start "Docker Daemon"

Now using the docker images command I got this

Hallelujah, I thought! There is is, the expected windowsservercore image. It is currently the only image available to us on our machine.

Tagging an image

To tag our image we can use the normal Docker tag command

docker tag [image-id] windowsservercore:latest

where [image-id] is the ID of the Docker image as listed in the previous picture. In my case this is dfee88ee9fd.

More images

We can use the Powershell command Find-ContainerImage to see what other images are available to us. We should see this

Apparently there is an image called NanoServer available to us. Lets install it on our machine using this command

Install-ContainerImage -Name NanoServer

This will take a few moments and once done we can verify that we now have not only windowsservercore but also nanoserver listed as available images on the local machine. Use this command

Get-ContainerImage

and the result should be similar to this

After restarting the Docker daemon (see above) we can also see the nanoserver image using the docker images command

Create Container Images

IIS Image

We can take it as our base and create some new images of from it. Let’s create an IIS image first as a test. Let’s run an instance of the image windowsservercore as follows

docker run -it windowsservercore powershell

Once we’re inside the container running in a Powershell console we use this command to install IIS

Install-WindowsFeature web-server

and we can exit the container by typing exit in the console. Finally we create an image from the current state of the container using this command

docker commit iisbase windowsservercore-iis

This operation takes a moment. Once we’re done we can use docker images to verify that we have indeed created a new image called windowsservercore-iis. We can run a Docker container from this image and open port 80 to the public by using this command

docker run -dt --name iisdemo -p 80:80 windowsservercore-iis

If we open a browser and navigate to the IP address of our container host VM (in my case this is 192.168.1.95) we see the expected result

Great, we have our first custom built container running! But wait a second… Docker does not recommend to build images using the docker commit command. The preferred way is to use a Dockerfile.

Using a Dockerfile to create an image

Let’s do this. Create a folder c:\docker\iis and navigate to this folder. Create a new file using

echo "" > Dockerfile

then use notepad to edit this file

notepad .\Dockerfile

Now add the following content to the file

FROM windowsservercore
RUN ["powershell","Install-WindowsFeature","web-server"]

Save the file. Make sure that you save the file as ASCII encoded or UTF-8 without encoding. Docker doesn’t like the BOM at the beginning of a standard UTF-8 encoded file. We should see an output similar to the following

And once again we can run an instance of this image with

docker run -dt --name iisdemo -p 80:80 iis

Navigating with the browser to the IP address of the VM shows the same result as above. The default IIS site is shown as expected.

Nginx Image

Now let’s create an nginx image as a second test. Let’s again run an instance of the image windowsservercore as follows

docker run -it windowsservercore powershell

Now after a short time we should find ourselves inside a Docker container in a Powershell console. Now we want to download nginx and can do this as follows

wget http://nginx.org/download/nginx-1.10.0.zip -UseBasicParsing -OutFile nginx.zip

This will take a few seconds to download. Now we need to extract this zip file using the following command

Expand-Archive nginx.zip

This will expand the file into a folder nginx. The binaries are in a subfolder nginx-1.10.0. We can now exit this container by typing exit in the Powershell console. Now we can use this container to create an image from it. First we need to know what it’s id is. Let’s do this by using docker ps -a. Our container should be in the list and it’s status should be Exited. Remember the ID of the container. Now we can use the following command to generate an image

docker commit [container-id] windowsservercore-nginx

When we now use docker images to get a list of container images we should see this

As mentioned above, a better way to create an Nginx based image is to use a Dockerfile. For this let’s create a folder c:\docker\nginx. In this folder we create a new new file called Dockerfile. The content of the file should be

FROM windowsservercore
RUN ["powershell","wget","http://nginx.org/download/nginx-1.10.0.zip","-UseBasicParsing","-OutFile","c:\\nginx.zip"]
RUN ["powershell","Expand-Archive","c:\\nginx.zip","-Dest","c:\\nginx"]
WORKDIR c:\\nginx\\nginx-1.10.0
ENTRYPOINT ["powershell",".\\nginx.exe"]

We can build this image using

docker build -t nginx .

and then we run a container like this

docker run -dt --name nginx -p 80:80 nginx

if we navigate with a browser to our IP address we see this

Nice. We just have successfully build a second custom image based on the popular Nginx web server.

Summary

In this post I have described how we can configure a developer machine to run a Windows Server 2016 TP5 VM in Hyper-V which is configured to be a Windows Container host. I also have shown how we can now use Docker to build container images and run instances of those as containers. It is very nice to see that the Docker API for Windows Containers is exactly the same as for Linux Containers. This makes life much easier for us developers that have to deal with both operating systems on a daily basis. If on the other hand you’re a pure Windows developer you can also use Powershell to create container images and instantiate and run containers.

About Gabriel Schenker

Gabriel N. Schenker started his career as a physicist. Following his passion and interest in stars and the universe he chose to write his Ph.D. thesis in astrophysics. Soon after this he dedicated all his time to his second passion, writing and architecting software. Gabriel has since been working for over 25 years as a consultant, software architect, trainer, and mentor mainly on the .NET platform. He is currently working as senior software architect at Alien Vault in Austin, Texas. Gabriel is passionate about software development and tries to make the life of developers easier by providing guidelines and frameworks to reduce friction in the software development process. Gabriel is married and father of four children and during his spare time likes hiking in the mountains, cooking and reading.
This entry was posted in ASP.NET vNext, containers, docker, How To, installation, introduction, Setup, tutorial. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Christopher Carrer

    Great write-up, Gabriel! Thank you for taking the time – I followed along and I’m now ready to DOCKER ALL THE THINGS!

    • gabrielschenker

      Happy to serve you!

  • Ritz

    somehow after this step docker run -dt –name iisdemo -p 80:80 iis

    I am not able to browse the site..while installing the install-containerhost.ps1, I got following error at the end..

    Error checking context: ‘can’t stat

    …..
    any ideas?

  • Darshan

    i have issue post docker run -it windowsservercore powershell command i got error panic runtime error invalid memory error or nil pointer derefernce

  • Darshan

    I found issue its because of Powershell ISE all above commands work only in powershell

  • Adam Biton

    hi there i try to use the instruction but i got an error someone can help me to find what i did wrong i’m adding a pic
    before i use this script i install the Container features and i install the docker my firewall is off
    i didn’t have on the machine the windows core image
    https://uploads.disquscdn.com/images/f2c7e36e2c8809e7d0eaaf23fc47839a128a975628e06c49683146c128c69aa0.jpg

    thanks