Windows Container port binding on Windows Server 2016 not working

5

2

I am running a Windows Container on a Windows host (Windows Server 2016 TP4).

The container shall run an IIS webserver on port 80 internally and I also want to bind port 80 to the host, so I can reach it via the host IP/URL.

I followed the instructions from Microsoft on

I tried both the approach via Powershell and Docker, and in both cases, the port binding to the host does not work.

========================= Powershell approach ==========================

Deploying container host to existing system (Windows Server 2016 TP4)

PS C:> wget -uri https://aka.ms/tp4/Install-ContainerHost -OutFile C:\Install-ContainerHost.ps1

PS C:> powershell.exe -NoProfile C:\Install-ContainerHost.ps1

Querying status of Windows feature: Containers...
Feature Containers is already enabled.
Waiting for Hyper-V Management...
Networking is already configured.  Confirming configuration...
Getting Container OS image (NanoServer) version 10.0.10586.0 from OneGet (this may take a few minutes)...
Container base image install complete.  Querying container images...
OS image (NanoServer) is already installed.
The following images are present on this machine:
    ContainerImage (Name = 'NanoServer') [Publisher = 'CN=Microsoft', Version = '10.0.10586.0']
    ContainerImage (Name = 'WindowsServerCore') [Publisher = 'CN=Microsoft', Version = '10.0.10586.0']

Docker is already installed.
Stopping Docker...
Starting Docker...
Tagging new base image (8572198a60f1)...
Base image is now tagged:
nanoserver          10.0.10586.0        8572198a60f1        5 months ago        0 B
nanoserver          latest              8572198a60f1        5 months ago        0 B
Script complete!

Preparing image and container that runs IIS (based on WindowsServerCore image)

These are the exact steps described in the Microsoft documentation at https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/manage_powershell. I create a container from WindowsServerCore, install IIS on it, and make a new image out of it, which I can then later reuse.

PS C:> Get-ContainerImage

Name              Publisher    Version      IsOSImage
----              ---------    -------      ---------
NanoServer        CN=Microsoft 10.0.10586.0 True
WindowsServerCore CN=Microsoft 10.0.10586.0 True


PS C:\> New-Container -Name TP4Demo -ContainerImageName WindowsServerCore -SwitchName "Virtual Switch"

Name    State Uptime   ParentImageName
----    ----- ------   ---------------
TP4Demo Off   00:00:00 WindowsServerCore


PS C:\> Get-Container

Name    State Uptime   ParentImageName
----    ----- ------   ---------------
TP4Demo Off   00:00:00 WindowsServerCore


PS C:\> Start-Container -Name TP4Demo

PS C:\> Enter-PSSession -ContainerName TP4Demo -RunAsAdministrator
[TP4Demo]: PS C:\Windows\system32> Install-WindowsFeature web-server

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


[TP4Demo]: PS C:\Windows\system32> exit
PS C:\> Stop-Container -Name TP4Demo

PS C:\> New-ContainerImage -ContainerName TP4Demo -Name WindowsServerCoreIIS -Publisher Demo -Version 1.0

Name                 Publisher Version IsOSImage
----                 --------- ------- ---------
WindowsServerCoreIIS CN=Demo   1.0.0.0 False


PS C:\> Remove-Container -Name TP4Demo -Force

Now I have an IIS container ready that I bind to the "Virtual Switch".

PS C:\> New-Container -Name IIS -ContainerImageName WindowsServerCoreIIS -SwitchName "Virtual Switch"

Name State Uptime   ParentImageName
---- ----- ------   ---------------
IIS  Off   00:00:00 WindowsServerCoreIIS


PS C:\> Start-Container -Name IIS

PS C:\> Invoke-Command -ContainerName IIS {ipconfig}

Windows IP Configuration

Ethernet adapter vEthernet (Virtual Switch-30179F35-A9BD-4231-B264-BDD2994BD956-0):

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::24f4:c726:ed9b:e603%28
   IPv4 Address. . . . . . . . . . . : 172.16.0.2
   Subnet Mask . . . . . . . . . . . : 255.240.0.0
   Default Gateway . . . . . . . . . : 172.16.0.1

Adding port mapping and firewall rule:

PS C:\> if (!(Get-NetNatStaticMapping | where {$_.ExternalPort -eq 80})) {Add-NetNatStaticMapping -NatName "ContainerNat" -Protocol TCP -ExternalIPAddress 0.0.0.0 -InternalIPAddress 172.16.0.2 -InternalPort 80 -ExternalPort 80}

PS C:\> if (!(Get-NetFirewallRule | where {$_.Name -eq "TCP80"})) {New-NetFirewallRule -Name "TCP80" -DisplayName "HTTP on TCP/80" -Protocol tcp -LocalPort 80 -Action Allow -Enabled True}

Now that I added the port mapping (and firewall rule), I should be able to reach the IIS through my host. (Just to be sure, I disabled the firewall on the host entirely.)

But the host port binding does not work. I cannot reach the IIS through the host IPs and bound port via http://localhost:80/ nor http://172.16.0.1:80/ nor http://10.10.0.79:80/

PS C:\> wget http://10.10.0.79:80/
wget : Unable to connect to the remote server
At line:1 char:1
+ wget http://10.10.0.79:80/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

PS C:\> wget http://172.16.0.1:80/
wget : Unable to connect to the remote server
At line:1 char:1
+ wget http://172.16.0.1:80/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

I can only reach the IIS default page via the container IP (http://172.16.0.2:80).

========================= Docker approach ==========================

And this is my approach using Docker to manage the container:

C:\> docker run --name iisbase -it windowsservercore cmd
C:\> powershell.exe Install-WindowsFeature web-server
C:\> exit
PS C:\Windows\system32> docker commit iisbase windowsservercoreiis
64271b60a1c4af29ce37ebcee45b00d824883eb67c717d4cee765d9f696867bb
C:\> powershell.exe "if(!(Get-NetFirewallRule | where {$_.Name -eq 'TCP80'})) { New-NetFirewallRule -Name 'TCP80' -DisplayName 'HTTP on TCP/80' -Protocol tcp -LocalPort 80 -Action Allow -Enabled True }"
C:\> docker run --name iisdemo -it -p 80:80 windowsservercoreiis cmd

At the end, I can only reach the IIS via the container IP, not via the host IP.

I'm using Docker version 1.10.0-dev, build 18c9fe0.

Mathias Conradt

Posted 2016-03-25T13:25:36.837

Reputation: 757

Tickets created at Docker https://github.com/docker/docker/issues/21558 and Microsoft Virtualization documentation: https://github.com/Microsoft/Virtualization-Documentation/issues/181

– Mathias Conradt – 2016-03-27T19:04:04.547

Answers

1

Seems to be an issue with Windows Server TP4.

Stefan Scherer from the Docker team replied to my reported issue: https://github.com/docker/docker/issues/21558#issuecomment-202536462

I can reproduce the problems of @mathiasconradt. Played with the voting-app last week with TP4, I have the same workaround: Opening firewall ports on the host, opening the web server url with container's IP addresses. Can't wait to test the voting-app on TP5.

Waiting for TP5... meanwhile I use Apache httpd on the host to handle the port forwarding.

Mathias Conradt

Posted 2016-03-25T13:25:36.837

Reputation: 757

TP5 should be available in about 7 hours, shouldn't it? – Falco Alexander – 2016-03-30T08:23:56.180

@FalcoAlexander I don't know, where did you get that info from? Also interested about the release date. I googled for it, saw https://twitter.com/wzornet/status/701532616839847937 which is already passed.

– Mathias Conradt – 2016-03-30T08:26:36.273

1The Build 2016 (in SF) conference starts in 7 hours. this is usually an event to distribute new releases. I guess: VisualStudio and TFS 2015 Update 2, new Windows 10 Release 1603 and Server 2016 TP5. – Falco Alexander – 2016-03-30T08:37:40.920

my bad...wrong speculations... :/ – Falco Alexander – 2016-04-02T09:59:42.607

0

Note: Finally this is found to be a bug in Windows Server 2016 TP4 that causes the behavior described below (see the comments). Even though the Docker Daemon is supposedly installed, the client seems to be used instead with all the limitations described below. This bug may be fixed in the future TP5.


This is a limitation of the Docker implementation on Windows, where you cannot connect to the container through the host.

This is the answer for this same question on bug report #15740 : port exposure on windows = ? :

The reason you're having this, is because on Linux, the docker daemon (and your containers) run on the Linux machine itself, so "localhost" is also the host that the container is running on, and the ports are mapped to.

On Windows (and OS X), the docker daemon, and your containers cannot run natively, so only the docker client is running on your Windows machine, but the daemon (and your containers) run in a VirtualBox Virtual Machine, that runs Linux.

To connect to the container, you must connect to the IP-address of the virtual machine, not of your Windows computer.

This is all described in the Windows installation documentation, found here; http://docs.docker.com/installation/windows/.

The Virtual Machine is described in this section;
http://docs.docker.com/installation/windows/#learn-the-key-concepts-before-installing

And an explanation on how to map ports, and connect to them is explained here;
http://docs.docker.com/installation/windows/#container-port-redirection

I'm going to close this issue, because this is not a bug, and explained in the documentation. I hope the above explanation helps you.

harrymc

Posted 2016-03-25T13:25:36.837

Reputation: 306 093

1Thanks for the link, but the linked github ticket refers to Windows 7 (or any Windows Server prior to Windows 2016), and the ticket reply says: "On Windows (and OS X), the docker daemon, and your containers cannot run natively, so only the docker client is running on your Windows machine, but the daemon (and your containers) run in a VirtualBox Virtual Machine, that runs Linux."; this is valid for Windows7, but this should not be the case for Windows 2016, where containers can run natively. – Mathias Conradt – 2016-03-27T15:42:45.347

1Also note the comment by thaJeztah on the github page you linked to: "Work is in progress to run Docker Engine natively on Windows Server 2016, but that engine will only run Windows applications, not Linux" <- this is what I am trying to do and using. – Mathias Conradt – 2016-03-27T15:47:02.723

But you are asking about Docker which is not developed by Microsoft. For seamless integration with Windows, you should be using Windows technology which is Windows Containers and Hyper-V Containers, not Docker. – harrymc – 2016-03-27T16:55:45.343

I know Docker is not developed by MS, I use it for Linux Containers without problems. Per Microsoft documentation, Docker should work on Win2016 the same way as Windows containers. I am explicitly avoiding Hyper-V in order to not have a VM in between. I will give the Windows Containers approach a try but would prefer to use Docker since its what I use outside of the Windows world as well. – Mathias Conradt – 2016-03-27T17:01:58.787

The differences will surely disappear in the future. For the current situation, see for example this article : Docker Engine for Windows Server 2016. I would guess that Microsoft should be gunning for total integration with Docker, maybe even in the release version of WS2016.

– harrymc – 2016-03-27T17:35:26.787

Thanks for the article link. I now tried the approach using powershell instead of docker, following the documentation step by step, but still no luck. See my update above ("UPDATE (Using powershell instead of Docker)") – Mathias Conradt – 2016-03-27T18:34:32.727

Strange, it seems like one guy managed to make this work already with PowerShell in WS2016 TP3. See his step-by-step account. You could try to copy exactly his commands.

– harrymc – 2016-03-27T19:37:06.837

I followed the steps in his article. It still does not work. That said, one thing to mention though: he seems to have different IP ranges of his vEthernet/Virtual Switch (and Ethernet0) than me. Not sure if it is because of TP3 vs. TP4. In his blog he wrote "Note: The VM host is created at this point, with a working NATing virtual switch that has an IP range of 192.168.250.0/24", but the MS default Install-ContainerHost.ps1 usually sets the Virtual Switch IP range to 172.16.0.0/16. It seems swapped, unless he changed the default config, confuses me a bit. Left him a comment under his article. – Mathias Conradt – 2016-03-27T21:28:07.610

Of course I ran the commands in his article adjusted to my IP addresses, etc. Without success :( – Mathias Conradt – 2016-03-27T21:34:18.470

BTW - is the Docker daemon service started? – harrymc – 2016-03-28T12:14:33.637

Yes, the Docker daemon is running as a service. btw: when I tried Docker, I followed the steps from the MS documentation, and I just see that the article was last updated yesterday 27.03., so it should be valid for TP4 to use Docker in the way described there. https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/manage_docker

– Mathias Conradt – 2016-03-28T13:21:33.557

I've tried tp4 in a VM, but I get stuck because of no virtualization support. As far as I can see, this should work on a physical machine. You might try your luck in the Windows Containers Forum where a MS engineer might answer. If you do, I suggest you reduce your question by a lot, just listing the PowerShell commands and their results. BTW - have you tried to telnet to host:8080?

– harrymc – 2016-03-28T13:39:20.510

I simplified my post and just put the output from my powershell approach step by step.I will check the Windows Container Forum as well; I already found a bug in Windows Container earlier regarding another issue that I reported and was approved by one of the MS engineers. Maybe it's another one. Telnet: don't have the telnet command, I did a wget, see updated post (unable to connect to the remote server). I also submitted two tickets to Docker's and MS Virtualization's github, maybe I will get a reply there as well. – Mathias Conradt – 2016-03-28T14:42:46.110

It might indeed be a regression of TP4, at least the networking codebase had been moved around - see the comment here: https://github.com/docker/docker/issues/21558#issuecomment-202165532. I guess I will wait for TP5 or a reply from the Docker/MS team. In the meantime, I will use Apache httpd on the host as a reverse proxy and do the port forwarding through there. That should do what I'm trying to achieve.

– Mathias Conradt – 2016-03-28T18:46:34.390

Stefan Scherer from the Docker team was able to reproduce my issue on TP4: https://github.com/docker/docker/issues/21558#issuecomment-202536462

– Mathias Conradt – 2016-03-28T19:46:34.813

I think you have a good chance that this will work correctly in the near future. Containers are a fascinating subject, even if not quite mature yet. – harrymc – 2016-03-28T20:50:14.537

Containers worked fine with Ubuntu for me so far, just on Win2016 the native support is all pretty new. But definitely a great help for app deployment. Looking forward to the final release. Thanks for your help regarding my question. – Mathias Conradt – 2016-03-29T13:37:34.837

You might have a look at WinDocks.

– harrymc – 2016-04-05T13:08:25.727

Thanks for the link. The website/project does not appear too trustworthy to me though (no official organization behind it, website looks a bit old-fashioned, they reference to Docker 1.7 in their docs - 1.11 is the current version). The port issue is not that critical, I'd rather to stick what's official from Docker and Microsoft. Doesn't feel that WinDock will be around for long. – Mathias Conradt – 2016-04-05T13:34:24.200