Create a Containers VM Host with NAT

There are several ways to deploy Windows Server Containers with Windows Server 2016 Technical Preview 3 (TPv3). You can enable the role on a physical server or use an existing virtual machine on any hypervisor that supports Windows Server 2016 TPv3 as a guest OS.
In this post, I’m going to show you how to perform scripted deployment of a Hyper-V virtual machine that’s running Windows Server Core. I’ll also show you how to enable Windows Server Containers. Don’t worry, the scripts have been provided by Microsoft. This post will focus on enabling containers that get connectivity via network address translation (NAT) in the VM host.

The Hyper-V Host

The first thing that you need is a Hyper-V host or cluster. Deploy Windows Server 2016 TPv3 onto the required hardware. Next, enable Hyper-V, provision some storage for virtual machines, and create a virtual switch that allows virtual machines to communicate on the network. Note that the setup has changed very little since Windows Server 2012 R2.

Deploy a VM Host

The solution that we are going to use is based on a set of scripts and images that Microsoft has shared. We will download a PowerShell script called New-ContainerHost.ps1. This script will:

  1. Download several gigabytes of files from Microsoft and expand them, if this has not already been done. One of these files is a VHD format. This is the VM host virtual machine template.
  2. The script creates a new Hyper-V virtual machine using differencing disks. Note that we don’t usually like differencing disks for normal, long-lived virtual machines, but containers are designed to be easy come, easy go. By using differencing disks, each new VM host takes up very little space, and it’s very quick to deploy.
  3. The virtual machine is powered up. The scripted process specializes the virtual machine. This includes downloading a second script, Install-ContainerHost.ps1, in the virtual machine and executing it to enable the containers role in the guest OS. You can customize Install-ContainerHost,ps1, save it outside of the virtual machine, and instruct New-ContainerHost.ps1 to use your copy. This is handy if you need to modify the NAT configuration.
  4. Windows Server Containers is configured with NAT connectivity for any new containers, and a container OS image for Windows Server Core is in a local (in the VM) containers repository.

Note that New-ContainerHost.ps1 performs some things by default, and there are some things that it won’t do at all. For example, Docker is installed in the VM host, and the VM host is not connected to a virtual switch. You can modify this behavior by using some parameters when you execute the script:

  • DockerPath: Indicate a path to an alternative Docker.exe.
  • Password: The password for the new VM host.
  • ScriptPath: Use a custom version of Install-ContainerHost.ps1 that will enable the containers role in the guest OS.
  • SkipDocker: Use this to not install Docker in the guest OS.
  • SwitchName: Instruct New-ContainerHost.ps1 to connect the new VM host to a virtual switch on your Hyper-V host.
  • UnattendPath: Use a different unattended answer file to specialize the guest OS when it first boots up.
  • VHDPath: Don’t use a Microsoft supplied VHD.
  • VMName: The name of your new VM host.

Launch PowerShell with elevated privileges, navigate to where you want to save your container-related lab, and then run the following to download the New-ContainerHost.ps1 script. In the world of containers, you are going to get used to downloading files using wget:

wget -uri https://aka.ms/newcontainerhost -OutFile New-ContainerHost.ps1

If you want to get a new VM host with a default configuration, then you can run the New-ContainerHost.ps1 from a PowerShell prompt:

.\New-ContainerHost.ps1 -VMName “VMHost1” -Password “Monkey123”

You will be prompted to agree some licensing terms from Microsoft; click Yes if you agree and want to continue.
If this is your first time running the script, then you’ll have a long wait as several gigabytes are downloaded. If you’ve run the script before, then after a a couple of minutes, you’ll have a brand new VM host waiting for you to log in.
Make sure you network the virtual machine:

# Find the name of your switch on the Hyper-V host
Get-VMSwitch
# Connect the VM to the switch on the Hyper-V host
Get-VM | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -Switchname "Virtual Switch"

Customizing the NAT Network

I had a New-NetNAT error when I ran New-ContainerHost in my lab for the first time. I logged into the newly created virtual machine and found that the private NAT network that the containers would communicate on was 172.16.0.0/12. On the face of it that, this should be okay because:

  • Container A would get 172.16.0.2 and Container B would get 172.16.0.3
  • I could forward traffic on specific ports to the VM host to the required ports in the containers. For example, any traffic on TCP 50002 to the VM host could be forwarded by the VM host to TCP 80 in Container A.

But my lab’s network address is 172.16.0.0/16, which means that there is a network address overlap. The NAT network address needed to be changed. The way to do this is to extract a copy of the default Install-ContainerHost.ps1 script (I mounted the VHD from Microsoft and copied the script), and modify one setting.
Near the top you will find the default values for the parameters of Install-ContainerHost.ps1. Search for a value called $NATSubnetPrefix that is set to 172.16.0.0/12.

The original NAT network address in Install-ContainerHost.ps1 (Image Credit: Aidan Finn)
The original NAT network address in Install-ContainerHost.ps1 (Image Credit: Aidan Finn)

I changed this value to a network address that did not overlap with my lab’s 172.16.0.0/16 network address. In the example below, I allow containers to use 192.168.250.0/24; this means that each new VM host will use this range as the private NAT network address. This doesn’t cause an issue, because it’s the same solution as just about every home and small business using 192.168.1.0/24 as their private network address. NAT on the VM host will redirect traffic on a specific port to that VM host to the appropriate container.
The modified NAT network address in Install-ContainerHost.ps1 (Image Credit: Aidan Finn)
The modified NAT network address in Install-ContainerHost.ps1 (Image Credit: Aidan Finn)

I saved my new version of Install-ContainerHost.ps1 somewhere safe. I then modified how I run New-ContainerHost.ps1 to get a new VM host:

.\New-ContainerHost.ps1 -VMName “VMHost1” -Password “Monkey123” -ScriptPath “C:\Scripts\Containers\Install-ContainersHost.ps1”

The networking of a VM host with a modified NAT network address (Image Credit: Aidan Finn)
The networking of a VM host with a modified NAT network address (Image Credit: Aidan Finn)

Now my copy of Install-ContainerHost is used to create a new VM host. Note that I’ve been focusing on using PowerShell to create and manage containers, so I do not install Docker:

.\New-ContainerHost.ps1 -VMName “VMHost1” -Password “Monkey123” –SkipDocker -ScriptPath “C:\Scripts\Containers\Install-ContainersHost.ps1”.


And to speed things up a bit, I also automate the connection to my Hyper-V host’s virtual switch:

.\New-ContainerHost.ps1 -VMName “VMHost1” -Password “Monkey123” –SkipDocker -SwitchName “Virtual Switch” -ScriptPath “C:\Scripts\Containers\Install-ContainersHost.ps1”.