- Install Ansible on Windows - Thu, Jul 20 2023
- Use Azure Bastion as a jump host for RDP and SSH - Tue, Apr 18 2023
- Azure Virtual Desktop: Getting started - Fri, Apr 14 2023
I’m going to teach you how to deploy Windows Server 2016 in a Docker virtual container from an Azure-hosted virtual machine (VM). To help you understand our goal, take a look at the following diagram and I’ll walk you through it:
Schematic diagram of our lab environment
First, we’ll deploy a new Azure VM running the latest Technical Preview (TP) build of Windows Server 2016. This particular OS image includes the Docker runtime environment that enables containers in the first place.
The great beauty of running Docker containers is that each application or service running inside a container is completely isolated from any other app/service running on the host. Therefore, instead of maintaining and patching multiple VMs, we patch one host and control our containers either individual or “as a herd.”
Preparing our environment
Open an elevated PowerShell ISE session on your computer and connect to your Azure subscription. Of course, you’ll first need to install the Azure PowerShell module. In the following example, I connect to my subscription named “150dollar” and Azure storage account named “150west”:
Select-AzureSubscription -SubscriptionName '150dollar' Set-AzureSubscription -SubscriptionName '150dollar' -CurrentStorageAccountName 150west
The following code first retrieves the latest version of the Windows Server Container Preview operating system image. It then deploys a new small Azure VM named containerhost2 with user-specified administrator credentials and a connection to the Subnet-1 default virtual network address space:
$image = (Get-AzureVMImage | Where-Object {$_.label –like '*container*'}| Sort-Object –Property PublishedDate -Descending)[0].imagename New-AzureVMConfig -Name 'containerhost2' -InstanceSize Small -ImageName $image | Set-AzureSubnet -SubnetNames 'Subnet-1' | Add-AzureProvisioningConfig -Windows -AdminUsername 'trainer' -Password 'M@1Pa$$w0rd!' | New-AzureVM -ServiceName 'west150service' -VNetName 'West150Network' -WaitForBoot
After the process completes, you can always run Get-AzureVM to check the VM run status:
Get-AzureVM ServiceName Name Status ----------- ---- ------ west150service containerhost2 ReadyRole
Examining the environment
I had difficulty using the VM’s WSMan PowerShell remoting endpoint, so instead I cut my losses by logging in to the “classic” Azure management portal, navigating to Virtual Machines, selecting the containerhost2 VM, and clicking Connect to generate an .RDP file.
I hate to say it, but sometimes the GUI approach is faster than PowerShell.
What’s interesting is that, after you’re in the RDP session, all you get is a cmd.exe console prompt! I’m not exactly sure how Microsoft hacked this Windows Server 2016 build, but you can run sconfig so the environment feels and acts like Server Core. Technically, Microsoft calls this environment the Windows Server Container Host System.
Anyway, let’s poke around a bit, starting with verifying the Windows Server version:
C:\Users\trainer>ver Microsoft Windows [Version 10.0.10514]
Okay. Let’s switch to PowerShell and see if we can find Docker anywhere:
C:\Users\trainer>powershell Windows PowerShell Copyright (C) 2015 Microsoft Corporation. All rights reserved. PS C:\Users\trainer> Get-Service -Name *docker* Status Name DisplayName ------ ---- ----------- Running Docker Docker Daemon
Bingo! If you inspect the service list, you won’t find Hyper-V (remember that, under the hood, Azure VMs are themselves Hyper-V virtual machines).
Get-Command -Module Containers | Select-Object -Property Name | Sort-Object -Property Name | Format-Wide -Column 2 Add-ContainerNetworkAdapter Connect-ContainerNetworkAdapter Disconnect-ContainerNetworkAdapter Export-ContainerImage Get-Container Get-ContainerHost Get-ContainerImage Get-ContainerNetworkAdapter Import-ContainerImage Install-ContainerOSImage Move-ContainerImageRepository New-Container New-ContainerImage Remove-Container Remove-ContainerImage Remove-ContainerNetworkAdapter Set-ContainerNetworkAdapter Start-Container Stop-Container Test-ContainerImage
Here’s a critical note: You can manage a container either by using Windows PowerShell or by using native Docker commands. Again, that’s a binary proposition: either/or. In this tutorial, we’ll use native Docker commands because they are already entrenched in the community.
Of course, Windows PowerShell allows us to run external commands, and Docker is no exception. Run the following command to get a full command list:
docker help
Deploying a Windows Server 2016 container
In Docker nomenclature, an image represents a container template. We can “sorta kinda” consider a Docker image in the same vein as a Windows Imaging Format (.wim) “golden image.”
We’ll start by listing available containers on our host (we can also run Get-ContainerImage):
PS C:\> docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE windowsservercore 10.0.10514.0 0d53944cb84d 3 months ago 9.697 GB windowsservercore latest 0d53944cb84d 3 months ago 9.697 GB
Okay. So it looks like we have only Server Core available to us. No problem. We’ll use the run Docker verb to create a new container based on a given image. The -it switch puts us into an interactive terminal (console) session within the container after it’s online:
docker run -it --name corecontainer windowsservercore cmd
What happens next is a bit confusing because we’ve been in a console environment, and now you’ll find yourself in another console environment; namely, you’re in the Server Core container.
Start PowerShell and create a sample file in the root of drive C:. We’re doing this to prove that we’re working with a Windows Server 2016 container and not the host itself:
New-Item 'C:\container.txt' -Type File -Force -Value 'Hello from the container!'
Type exit twice to first close PowerShell and then leave the container and return to the host VM.
Now run dir *.txt and what do you see? Nothing, because the container.txt file exists in the Server Core container and not the host! This may be an underwhelming example, but I trust you get the idea.
Further actions
Let me introduce you to a few more Docker commands before we close today’s lesson. First of all, know that the --help switch gives you command-specific help. For example:
docker run --help
Somewhat confusingly, Docker uses ps to list all containers. You Linux admins know that we use an entirely different ps command to list processes,
I’ve modified the following output a bit to make it easier for you to read:
PS C:\> docker ps -a CONTAINER ID IMAGE COMMAND STATUS NAMES 000d85474a02 windowsserve "cmd" Exited corecontainer b7d3f7833cae windowsserve "-cmd" Created dockerdemo
We stop and start containers using the appropriate verbs along with the container ID or container name:
docker stop b7d3f7833cae docker start b7d3f7833cae
After a container is started, you can attach to it with, well, the attach verb:
docker attach b7d3f7833cae
Conclusion
Docker containers have many advantages over traditional hypervisor-based virtual machines, including but not limited to the following:
- Containers are much smaller than VMs are and they consume fewer system resources.
- Container density per physical host is greater than VM density is.
- Containers support rapid application deployment.
- Containers have a reduced attack surface compared to that of VMs.
- Container management is easier than VM management is.
- Containers are more portable (especially across OS kernels) than VMs are.
The Microsoft-Docker partnership bears beautiful cross-platform fruit. You must remember that Docker was created for us in the Linux/UNIX world. The fact that Windows Server 2016 supports the Docker runtime environment natively is nothing short of phenomenal.