How to deploy Windows containers in Microsoft Azure
Want to build and manage your own Windows Server containers? Here's how to do it using native Docker commands and a PowerShell module.
Docker is an application virtualization technology that's quite popular in the free and open source software community....
Continue Reading This Article
Enjoy this article as well as all of our content, including E-Guides, news, tips and more.
A Docker container takes a given app -- Web server, database server, among others -- and runs it and all its dependencies in an isolated environment.
DevOps engineers and programmers like Docker because they can spin up entire multicontainer environments within a few seconds. The containers are network-aware and behave as if they're running on their own dedicated hardware. When a container is no longer needed, it can be stopped and deleted with very little latency.
Microsoft's Jeffrey Snover, technical fellow and lead architect for the Enterprise Cloud Group, and Mark Russinovich, CTO of Azure, both feel strongly about supporting free and open source software in general and Docker containers in particular.
In Windows Server 2016 Technical Preview 4 (TP4), the Docker runtime is integrated with the Windows Server operating system (OS). The Windows and Linux kernels are fundamentally different, so we can't run Linux-based containers on a Windows container host. Instead, Microsoft offers Windows Server-based containers. This tip will show how to deploy Windows containers from an Azure-based virtual machine (VM) host running Windows Server 2016 TP4.
Creating the container host VM
The physical server or VM that hosts the Docker runtime environment and associated containers is called, not surprisingly, a container host. Although we could set up an on-premises container host by downloading the TP4 ISO and following some rather tedious setup instructions, I thought it easier to do the same thing in the Azure public cloud.
If you don't already have a Microsoft Azure account, sign up for a free trial. You'll get $200 in service credit that covers a month of Azure access.
Log in to the Azure portal and create a VM using the Windows Server 2016 Core with Containers Tech Preview 4 image, as shown in Figure 1.

After Azure creates the container host VM, click Connect -- shown in Figure 2 -- to download a preconfigured Remote Desktop Protocol (RDP) .rdp file.

There are a few important features to be aware of in this Azure-based VM:
- The VM runs Windows Server 2016 TP4 in the Server Core mode, meaning there is no GUI.
- The Windows containers feature is preinstalled, as is the Docker runtime environment.
- The image contains one pre-made Docker container running Windows Server 2016 TP4 in the Server Core configuration
Getting comfortable with the commands
Next, open the RDP connection to connect to the container-host VM. You'll find yourself at a cmd.exe prompt, so type powershell to enter an administrative PowerShell console session. First, let's verify the containers feature is installed:
Get-WindowsFeature -Name Containers
Display Name Name Install State
------------ ---- -------------
[X] Containers Containers Installed
Next, we'll make sure the Docker service is present and started:
Get-Service -Name Docker
Status Name DisplayName
------ ---- -----------
Running Docker Docker Daemon
Excellent on both counts. We can manage Windows Server containers in one of two ways:
Those who've used Docker in open source environments are likely to start with the native Docker commands, which behave the same way they do under Linux. However, I suggest we use the PowerShell commands because we already understand PowerShell syntax. Also, in my experimentation, I've found the PowerShell commands to be more reliable than the native Docker commands.
Let's take a quick look at the Containers commands to see what's available:
Get-Command -Module Containers | Select-Object -Property Name | Sort-Object -Property Name
Name
----
Add-ContainerNetworkAdapter
Add-ContainerSharedFolder
Connect-ContainerNetworkAdapter
Disconnect-ContainerNetworkAdapter
Export-ContainerImage
Get-Container
Get-ContainerHost
Get-ContainerImage
Get-ContainerMemory
Get-ContainerNetworkAdapter
Get-ContainerProcessor
Get-ContainerSharedFolder
Get-ContainerStorage
Import-ContainerImage
Install-ContainerOSImage
Move-ContainerImageRepository
New-Container
New-ContainerImage
Remove-Container
Remove-ContainerImage
Remove-ContainerNetworkAdapter
Remove-ContainerSharedFolder
Set-Container
Set-ContainerMemory
Set-ContainerNetworkAdapter
Set-ContainerProcessor
Set-ContainerSharedFolder
Set-ContainerStorage
Start-Container
Stop-Container
Test-ContainerImage
Uninstall-ContainerOSImage
Creating a Windows Server container
In Docker nomenclature, a container is an instance of a container image. In turn, we can look at a container image as a blueprint for a virtualized OS and application.
We can use Get-ContainerImage to view the VM's one prebuilt container image:
Get-ContainerImage
Name Publisher Version IsOSImage
---- --------- ------- ---------
WindowsServerCore CN=Microsoft 10.0.10586.0 True
As you can see, we have a single Windows Server OS image named WindowsServerCore. We'll use New-Container to create a new container named core1 using this image as a template:
New-Container -Name 'core1' -ContainerImageName 'WindowsServerCore' -SwitchName 'Virtual Switch' -RuntimeType Default
Name State Uptime ParentImageName
---- ----- ------ ---------------
core1 Off 00:00:00 WindowsServerCore
I want to discuss two parameters from the previous code in greater detail. First, the -SwitchName parameter binds the container to a Hyper-V virtual switch running on the container host.
The virtual switch is important because it allows networking between the container, the host and -- potentially -- the world beyond. Try this:
$switch = Get-VMSwitch -Name 'Virtual Switch'
$switch.NATSubnetAddress
172.16.0.0/12
So we see that, under the covers, the virtual switch uses Network Address Translation with the 172.16.0.0/12 address range. Thus, we can expect that our container will have a 172.16.0.0/12 IPv4 address.
Second, the -RuntimeType parameter of the New-Container cmdlet specifies the isolation level of the container. We have two choices here:
- Default: This is the "garden variety" Windows Server container isolation, which assumes a high degree of trust between containers and the host.
- Hyper-V: This is a much more strongly isolated container environment that's intended for containers that require total isolation for security purposes.
Note that Microsoft doesn't support Hyper-V containers in Azure. However, you can deploy this container type when running Windows Server 2016 TP4 on premises. I've had mixed results, but that's to be expected because Windows Server 2016 and the Docker integration are in an unfinished -- and therefore highly volatile -- state at this point.
Working within Windows containers
Windows Server 2016 doesn't start new containers by default. We do it manually by invoking Start-Container:
Start-Container -Name 'core1'
As you'd expect, we use Get-Container to see our containers in much the same way we used Get-ContainerImage to view our container images:
Get-Container
Name State Uptime ParentImageName
---- ----- ------ ---------------
core1 Running 00:00:10.4520000 WindowsServerCore
We can interact with the container through PowerShell remoting commands. For starters, let's log in to the container with Enter-PSSession:
Enter-PSSession -ContainerName 'core1' -RunAsAdministrator
[core1]: PS C:\Windows\system32>
Now, any commands we issue will take place on core1 instead of the container host. For example, let's check the container's IPv4 address:
[core1]: PS C:\Windows\system32> Get-NetIPAddress | Select-Object -Property IPv4Address
IPv4Address
-----------
172.16.0.2
Just as we suspected: The container's IP address is NAT'ed to the host by means of that virtual switch. Type exit to detach from the container and return to the host.
We send commands to the container by using Invoke-Command:
Invoke-Command -ContainerName 'core1' -ScriptBlock { Get-Service -Name Server }
Status Name DisplayName PSComputerName
------ ---- ----------- --------------
Stopped LanmanServer Server core1
If you've used Invoke-Command, then you are probably accustomed to the -ComputerName parameter to specify your target computers. In Windows Server 2016, the new -ContainerName parameter enables us to target containers.
Cleaning the environment
Let's finish this test by stopping our container:
Stop-Container -Name 'core1'
Then we'll delete the container:
Remove-Container -Name 'core1' -Force
Close the RDP session and return to the Azure portal. Be sure to stop the VM -- shown in Figure 3 -- to avoid any billing surprises, or, more likely, prematurely running through your Azure credit.
