Detatch from a Docker Container without Stopping It

Saturday morning Docker fun times (still only on Windows Server Core) – here is something I found out that might be useful. It is in the Docker documentation but it is not mentioned in the Microsoft container documentation.

Once you have attached to a Docker Container via a CMD console typing exit at the console detatches from the container and Stops it. This is not usually what I want to do. To detatch from the container without stopping it press CTRL+P followed by CTRL+Q.

The container is still running after being detached.

The container is still running after being detached.

Note: This only applies to Docker Containers that have been attached to via docker attach or docker run. Windows Server Containers that have been connected to via Enter-PSSession can be exited using the Exit command.

Well that is enough for a Saturday morning.

Install all DSC Resources in a Repository

Something quick for Friday morning! Are you feeling enthusiastic about DSC? Do you want to update or install every DSC resource in a repository onto your computer?

Try this (only on PowerShell 5.0 computers – what do you mean you’re not using PowerShell 5.0?):

Find-DscResource | Select-Object -ExpandProperty ModuleName -Unique | % { Install-Module -Name $_ -Force }

This will download and install (or update if you’ve got older versions) of all DSC Resources in all PowerShell Repositories registered on your computer. This can definitely take some time.

If you would like to limit this to only using a specific repository use:

$RepoName = 'PSGallery'
Find-DscResource -Repository $RepoName | Select-Object -ExpandProperty ModuleName -Unique | % { Install-Module -Name $_ -Repository $RepoName -Force }

Important Note: I’d suggest you only install DSC resources from repositories you trust. You might therefore want to make sure you’ve marked those repositories as trusted otherwise you’ll need to confirm the installation of each module (which is a little irritating).

To trust a repository:

Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
First Container - making progress

Docker and Containers on Nano Server Continued

This is a continuation of my investigation of how to get Containers and also possibly the Docker engine running on Windows Server Nano 2016 TP 3. The initial investigation into this can be found here: How to use Containers on Windows Nano Server.

This post is mainly documenting the process of manually creating containers on Windows Nano Server 2016 TP3 as well as some additional details about what I have managed to find out. The documentation on Windows Server Containers from Microsoft is relatively thin at the moment (not surprising – this is very much in technical preview) and so a lot of the information here is speculation on my part. Still, it might be useful to get an idea of how things eventually will work. But of course a good deal of it could change in the near future. This information is really to help me get my head around the concepts and how it will work, but it might be useful for others.

Step 1 – Create a Nano Server Virtual Machine

Anyone who has played around with Nano Server should already be very familiar with this step. The only thing to remember is that the following packages must be included in the VHDx:

  1. Guest – All Nano Server VHDx files running as  VM should have this package. If you’re installing Nano Server onto bare metal you won’t need this.
  2. Compute – Includes the Nano Server Hyper-V components. Required because Containers use Hyper-V networking and are a form of Virtualization.
  3. OEM-Drivers – Not strictly required but I tend to include it anyway.
  4. Containers – This package provides the core of Windows Server Containers.

If you’re unfamiliar with creating a Nano Server VHDx, please see this post.

Step 2 – Configure the Container Host Networking

Any container that needs to be connected to a network (most of them usually) will need to connect to a Hyper-V Virtual Switch configured on this Container Host. There are two virtual switch types that can be configured for this purpose:

  1. NAT – This seems to be a new switch type in Windows Server 2016 that causes performs some kind of NAT on the connected adapters.
  2. DHCP – this is actually just a standard External switch with a connection to a physical network adapter on the Container Host.

The installation script normally performs one of the above depending on which option you select. However on Nano Server both of these processes fail:

NAT

Creating a NAT VM Switch on Nano Server actually works. But the command to create a NAT Network connection to the VM Switch fails because the NETNAT module is not available on Nano Server.

DHCP

Creating a standard External VM Switch on Nano Server

Creating a DHCP/External VM Switch on Nano Server just fails with a cryptic error message. The same error occurs when creating a Private or Internal VM Switch, so I expect Hyper-V on Nano Server isn’t working so well (or at all). Not much point pursuing this method of networking.

Step 3 – Install a Base OS Image from a WIM File

Every container you create requires a Base OS Image. This Base OS Image contains all the operating system files and registry settings for the OS a container uses. Windows Server Containers expects to be provided with at least one Base OS Image in the form of a WIM file. You can’t create a container without one of these. At this point I am unsure if the WIM file that Windows Server Containers will use is a customized version of the WIM file provided with an OS or if it is standard.

During an installation of Windows Server Containers onto a Windows Server Core operating system, the process automatically downloads a WIM file that is used as the Base OS Image.

To install a Base OS Image from a WIM File to the Container Host using the PowerShell function:

Install-ContainerOSImage -WimPath CoreServer.wim -Verbose

Installing a Base OS Image

This function does several things:

  1. Creates a new folder in c:\programdata\microsoft\windows\images with Canonical Name of the new Base OS Image:
    Contents of the Images folder
  2. Inside the Canonical Name folder a subfolder called files is created where the Base OS Image file is extracted to:The contents of an Image Files
  3. Another subfolder called hives is also created in the Canonical Name folder which contains the default registry hives for the Base OS Image:
    The Image Registry Hives
  4. Two additional files are created in the Canonical Name folder that contain metadata about the image:
    Metadata.json
    Version.wcxImage Metadata
  5. Adds the Base OS Image into the list of Image Containers that are available to create new Containers from:
    All Base OS Images installed

I have tried using the Install.wim from the ISO, the NanoServer.wim from the ISO and the Core.wim downloaded using the Core Edition Containers install script. Also note, the INSTALL.WIM file on the TP3 ISO still refers to Windows Server 2012 R2 SERVERSTANDARDCORE (I double checked this and you can confirm by the Version number in the OS Image).

The Test-ContainerImage cmdlet can be used to identify “problems” with container images:

Testing Containers

None of the container images report any problems which is nice to know.

Step 4 – Create a Container

This is obviously where things should start to get exciting! The next step is to create a shiny new container using one of our Base OS Images. However, if you try and create a new container at this point a cryptic error message will occur:

New Container? Nope!

I don’t know what causes this, but if you reboot your Nano Server VM the error goes away and you should be able to successfully create the container:

First Container - making progress

Unfortunately only the Base OS Image downloaded from Microsoft and used with Containers for Windows Server 2016 Core results in a valid Container. So it would seem there are some things that are done to a WIM file to make it able to be Containerized (is that a word?).

Step 5 – Start the Container

I’m not holding my breath here. This is what happens when the container is started:

Starting up the Container - nope!

Looking closely at the text of the error it would appear that there was a mismatch between the Container Host OS version and that of the Base OS version that the container was using. This is probably because the Container Host is a Nano Server and the Base OS that was downloaded was for a Core Server.

Next Steps

It would seem at this point we have to wait for Microsoft to provide a Base OS file for Nano Server and also fix the Virtual Switch issues with Nano Server before any further progress can be made experimenting with Containers on Nano Server.

However, it may still be possible to get the Docker Engine working under Nano Server and see if that offers any more information. So that will be what I’ll look into next.

Also, it is interesting to dig around into the files that are created when the new container was created:

Files Created with a Container

When a container is created the container files are stored in the C:\ProgramData\Microsoft\Windows\Hyper-V\containers folder. Unfortunately the files all binary so we aren’t able to dig around in them to glean any other information.

Well, that is enough for today.

How To use Containers on Windows Nano Server

Edit: I wrote this article when examining containers on Windows Nano Server TP3which wasn’t in a working state. I have not yet had a chance to fully examine containers on Windows Nano Server TP4, but when I get a spare day hours I will no doubt deep dive into it.

If you’re looking for instructions on installing and using containers on Windows Nano Server TP4, start here.

These instructions are more focused on setting up a container host on Windows Server Core TP4, but I have managed to get them working on Windows Nano Server TP4 just fine:

ss_nano_containerhostworking

I do plan to document this process over the next week or so.


 

You’d be forgiven for believing that it was just a simple click of a button (or addition of a package) to get Docker Containers working on a shiny new Windows Nano Server TP3 install. That is what I thought too. But after careful examination of the available documentation I found that there isn’t much information on running actually getting containers working on Nano Server. Sure, there is lots of information on running it on a full or core version of WIndows Server TP3, but Nano is lacking. So, because I’m a bit obsessive I decided I’d have a try and adapting the standard installation process.

Edit: Initially I had a bit of success, but I’ve run into some rather stop dead issues that I haven’t been able to resolve (see later on in this post).

I have continued the investigation here with a much more in depth look at the issues.

tl;dr: Containers on Windows Server Nano 2016 TP3 does not work yet! The Base OS WIM file for Windows Server Nano is required, but has not been provided.

Problems with the Standard Containers Install Script

First up I grabbed a copy of this script from Microsoft which is what is used to install containers on a full Windows Server 2016 install. I took a look at it and identified the things that wouldn’t work on a Nano Server 2016 install. This is what I found:

  1. The script can optionally configure a NAT switch – this requires the NetNat PS module which isn’t available on Nano.
  2. The script will install a VM Switch – therefore the Compute package is required to be installed on the Nano Server (the Compute package contains the Hyper-V components).
  3. The script can download various files from the internet (using the alias wget). Wget and Invoke-WebRequest are not available on Nano Server – so we’ll need to download the files to another machine and pre-copy them to the Nano Server.
  4. The Expand-Archive is used to extract the NSSM executable, but this cmdlet is not available on Nano Server either – so we’ll need to extract the NSSM.exe on another machine and copy it to the server.

The Process of Installing a Container Host

The process of actually installing a Container Host in Windows Nano Server is as follows:

  1. Create a Nano Server VHDx with the packages Guest, OEM-Drivers, Compute and Containers.
  2. Create a new VM booting from the VHDx – this is our Container Host.
  3. Upload a Base OS WIM file to the Container Host containing that will be used to create new containers.
  4. Upload Docker.exe to c:\windows\system32\ on the Container Host.
  5. Upload NSSM.exe to c:\windows\system32\ on the Container Host – this is used to create and run the Docker Service.
  6. Run the installation script on the Container Host – this will install the networking components and configure the Docker service as well as create the container OS image.
  7. Create a Container!

In theory the Container Host is now ready to go!

What is Required to build a Nano Server Container Host

A bit of experience with PowerShell is a good help here!

So, to create a Nano Server Container Host you’ll need a few things:

  1. A machine that can run Generation 2 Hyper-V machines (Gen 1 will probably work but I’m using Gen 2) – this will host your Nano Server. This machine must also be running PowerShell 5.0 (I’m using some PS5.0 only cmdlets)!
  2. A copy of the Windows Server 2016 TP 3 ISO from here.
  3. A working folder (I used D:\Temp) where you’ll put all the scripts and other files etc.
  4. The scripts (I’ll provide them all in a zip file), but they are:
    1. New-ContainerHostNano.ps1 – this will do everything and is the only script you’ll run.
    2. Install-ContainerHostNano.ps1 – this is the script that gets automatically run on the Container Host. It is a version of the Microsoft one from here that I have adjusted to work with Nano Server.
    3. New-NanoServerVHD.ps1 – this is a script I wrote a while back to create Nano Server VHDx files (see this post for more details).
    4. Convert-WindowsImage.ps1 – this script is required by New-NanoServerVHD.ps1 and is available on Microsoft Script Center.

How Can I use all This?

I haven’t really finished implementing or testing these scripts and I am encountering a problem creating the VM Switch on the Nano Server, but if you’re interested you can get a hold of the scripts in my GitHub repository.

To use them:

  1. Create a working folder (I used d:\temp).
  2. Download the four PS1 scripts from the GitHub repository to the working folder.
  3. Download the Windows Server 2016 TP3 ISO from here and put it in the working folder.
  4. Download the Base OS Container Image from here (3.5GB download) and put it in the working folder.
  5. Edit the New-ContainerHostNano.ps1 file in the working folder and customize the variables at the top to suit your paths and such – fairly self explanatory.
  6. In an Administrative PowerShell run the New-ContainerHostNano.ps1 file.

Please note: This is a work in progress. There are definitely some bugs in it:

  1. An error is occurring when trying to create the VM Switch in DHCP mode or NAT mode.
  2. If using NAT mode the NAT module isn’t included in Nano Server so although the VM switch gets created the NAT Network adapter can’t be created.
  3. NSSM isn’t creating the Docker Service – which may just be an issue with running the PowerShell installation script remotely.

None of the above will stop containers being created though. The containers might not be able to communicate with the world via networking and the Docker management engine might not work, but in theory the containers should still work (at least that is my understanding).

The BIG Problem

Any container that you create requires a WIM file that contains the container base OS image that container will use. Microsoft has so far only provided a base WIM file for WIndows Server 2016 Core installations – they haven’t provided a container base OS Image for Windows Server 2016 Nano yet. You can download the Core one from here (3.5GB download).

If you try to use the NanoServer.WIM file from the Windows Server 2016 ISO as the container base OS image you can’t even create the container at all.

I did try putting the Core WIM file downloaded above onto the Nano Server. I could then create a container OK, but an error would occur starting it up:

Nope - can't use the Core WIM with a Nano Server Container Host!

Nope – can’t use the Core WIM with a Nano Server Container Host!

Update 2015-10-29: There is a new video available online from Microsoft of Mark Russinovich (Azure CTO) doing a container demonstration using a Nano Server. It clearly shows that the NanoServer Base Container Image does exist. So perhaps we’ll see this in the TP4 release.

The video can be seen here.

Feel free to let me know if you can solve any of these issues! Any help is appreciated. I’ll continue to work on this and post any additional results.

PowerShell CmdLets available in the Containers Module on a Nano Server

Just a Monday morning quickie:

Here is a list of all the cmdlets available in the PowerShell containers module on a Nano Server with the containers package installed:

Containers - the next big thing!

Containers – the next big thing!

And here is the text version:

Function        Install-ContainerOSImage                           1.0.0.0    Containers
Function        Uninstall-ContainerOSImage                         1.0.0.0    Containers
Cmdlet          Add-ContainerNetworkAdapter                        1.0.0.0    Containers
Cmdlet          Connect-ContainerNetworkAdapter                    1.0.0.0    Containers
Cmdlet          Disconnect-ContainerNetworkAdapter                 1.0.0.0    Containers
Cmdlet          Export-ContainerImage                              1.0.0.0    Containers
Cmdlet          Get-Container                                      1.0.0.0    Containers
Cmdlet          Get-ContainerHost                                  1.0.0.0    Containers
Cmdlet          Get-ContainerImage                                 1.0.0.0    Containers
Cmdlet          Get-ContainerNetworkAdapter                        1.0.0.0    Containers
Cmdlet          Import-ContainerImage                              1.0.0.0    Containers
Cmdlet          Move-ContainerImageRepository                      1.0.0.0    Containers
Cmdlet          New-Container                                      1.0.0.0    Containers
Cmdlet          New-ContainerImage                                 1.0.0.0    Containers
Cmdlet          Remove-Container                                   1.0.0.0    Containers
Cmdlet          Remove-ContainerImage                              1.0.0.0    Containers
Cmdlet          Remove-ContainerNetworkAdapter                     1.0.0.0    Containers
Cmdlet          Set-ContainerNetworkAdapter                        1.0.0.0    Containers
Cmdlet          Start-Container                                    1.0.0.0    Containers
Cmdlet          Stop-Container                                     1.0.0.0    Containers
Cmdlet          Test-ContainerImage                                1.0.0.0    Containers

Comparing Objects using JSON in PowerShell for Pester Tests

Recently I spent the good part of a weekend putting together Pester Tests (click here if you aren’t familiar with Pester) for my LabBuilder PowerShell module- a module to build a set of Virtual Machines based on an XML configuration file. In the module I have several cmdlets that take an XML configuration file (sample below) and return an array of hash tables as well as some hash table properties containing other arrays – basically a fairly complex object structure.

A Pester Test config file for the LabBuilder module

A Pester Test config file for the LabBuilder module

In the Pester Tests for these cmdlets I wanted to ensure the object that was returned exactly matched what I expected. So in the Pester Test I programmatically created an object that matched what the Pester Test should expect the output of the cmdlets would be:

$ExpectedSwtiches = @( 
  @{ name="General Purpose External"; type="External"; vlan=$null; adapters=[System.Collections.Hashtable[]]@(
    @{ name="Cluster"; macaddress="00155D010701" },
    @{ name="Management"; macaddress="00155D010702" },
    @{ name="SMB"; macaddress="00155D010703" },
    @{ name="LM"; macaddress="00155D010704" }
    )
  },
  @{ name="Pester Test Private Vlan"; type="Private"; vlan="2"; adapters=@() },
  @{ name="Pester Test Private"; type="Private"; vlan=$null; adapters=@() },
  @{ name="Pester Test Internal Vlan"; type="Internal"; vlan="3"; adapters=@() },
  @{ name="Pester Test Internal"; type="Internal"; vlan=$null; adapters=@() }
)

What I needed to do was try and make sure the objects were the same. At first I tried to use the Compare-Object cmdlet – this actually wasn’t useful in this situation as it doesn’t do any sort of deep property comparison. What was needed was to serialize the objects and then perform a simple string comparison. The ConvertTo-JSON cmdlet seemed to be just what was needed. I also decided to use the [String]::Compare() method instead of using the PowerShell -eq operator because the -eq operator seems to have issues with Unicode strings.

The Pester test that I first tried was:

Context "Valid configuration is passed" {
  $Switches = Get-LabSwitches -Config $Config
	
  It "Returns Switches Object that matches Expected Object" {
    [String]::Compare(($Switches | ConvertTo-Json),($ExpectedSwtiches | ConvertTo-Json)) | Should Be 0
  }
}

This initially seemed to work, but if I changed any of the object properties below the root level (e.g. the adapter name property) the comparison still reported the objects were the same when they weren’t. After reading the documentation it states that the ConvertTo-JSON cmdlet provides a Depth property that defaults to 2 – which limits the depth that an object structure would be converted to. In my case the object was actually 4 levels deep. So I needed to add a Depth parameter to the ConvertTo-JSON calls:

[String]::Compare(($Switches | ConvertTo-Json -Depth 4),($ExpectedSwtiches | ConvertTo-Json -Depth 4)) | Should Be 0

This then did pretty much exactly what I wanted. However, I also needed the comparison to be case-insensitive, so I added a boolean parameter to the [String]::Compare static call:

[String]::Compare(($Switches | ConvertTo-Json),($ExpectedSwtiches | ConvertTo-Json),$true) | Should Be 0

The end result was an deep object comparison between a reference object and the object the cmdlet being tested returned. It is by no means perfect as if the properties or contents of any arrays in the object are out of order the comparison will report that there are differences, but because we control the format of these objects this shouldn’t be a problem and should enable some very test strict cmdlet tests.

How the the Final Pester Test in Visual Studio 2015 (with POSH tools)

How the the Final Pester Test in Visual Studio 2015 (with POSH tools)

Edit: after writing a number of Pester tests using the approach I realized it could be simplified slightly by replacing the generation of the comparison object with the actual JSON output produced by the reference object embedded inline in a variable. For example:

Performing the object comparison using JSON in a variable in the test.

Performing the object comparison using JSON in a variable in the test.

The JSON can be generated manually by hand (before writing the function itself) to stick to the Test Driven Design methodology or it can be generated from the object the function being tested created (once it it working correctly) and then written to a file using:

Set-Content -Path "$($ENV:Temp)\Switches.json" -Value ($Switches | ConvertTo-Json -Depth 4)

The $switches  variable contains the actual object that is produced by the  working command being tested.

A Word of Caution about CRLF

I have noticed that when opening the JSON file in something like Notepad++ and copying the JSON to the clipboard (to paste into my Pester test) that an additional CRLF appears at the bottom. You need to ensure you don’t include this at the bottom of your variable too – otherwise the comparison will fail and the objects will appear to be different (when they aren’t).

This is what the end of the JSON variable definition should look like:

Good JSON CRLF Formatting

And this is what it should not look like (the arrow indicates the location of the extra CRLF that should be removed):

Good JSON CRLF formatting

Note: I could have used the Export-CliXML and Import-CliXML CmdLets instead to perform the object serialization and comparison, but these cmdlets write the content to disk and also generate much larger strings which would take much longer to compare and ending up with a more complicated test.

Well, hopefully someone else will find this useful!