Monday, October 17, 2016

First steps with Microsoft Containers - part 1

Windows Server 2016 has Container support. That’s what most of us have heard in the last months. I have spent many years in the virtualization field, but getting a grasp of Containers in Windows in 2016 has asked for a great deal of effort in order for me to move from a traditional administrator role, to becoming an automator in a stretched and heterogeneous IT environment.

HOW WE GOT THERE

The initial learning curve was steep, with the birth of PowerShell and the need to change my habit of relying on GUIs in order to get things really automated. I was pretty prone to dive into scripting since in the long term it meant less work for me, more for my servers.
Then it settled for a while. Learning Hyper-V was not difficult since I already had previous experience with VMware. And the cmdlets for managing those hypervisors are more or less the same (the consistency of Windows PowerShell is one of its primary assets).
During this period behind the curtains things were happening: Microsoft was trying to catch up with VMware and position their hypervisor in the ‘Leaders’ box of the Gartner quadrant. And at the same time Microsoft was building NanoServer, the thinnest OS they could provide on the road to containerization.
To make things even more complex to follow, Microsoft has also gone open source on some projects, like .NET Core or PowerShell, and is contributing to the Docker open source project. Whilst this has increased the quality of the product, thanks to the effort of hundreds of expert contributors, the risk for bugs to make it into the code has also increased. This is a common risk in the open source ecosystem. The most significative example? Just last week version 4.8 of the Linux kernel (which is marked as stable) has been released with the dangerous addition of a BUG_ON line which kills the kernel.
So, let’s now have a look at Microsoft Containers, how to use them and I will walk you through some of the technical problem you could encounter with this product. Since there are so many different instructions around, and releases are succeding pretty fast, it is difficult to know which one to use, so I’ll try to be as simple as possible.
The first step is go and get a Windows 2016 image. Once you have it, the upgrade process will be pretty easy. Personally I upgraded most of the systems in my labs, which were running Windows 2012 R2 or some Windows 2016 Technical Preview.


The process was painless, apart for a Hyper-V cluster that suffered a loss of its LBFO teaming and virtual switches caused by the Unaware Update burden I put on it…

INTRODUCING THE THING

Containers are all about operating system virtualization, so they are a different concept from virtual machines, which are all about hardware virtualization. Since containers work at a different level, they result significantly faster to setup and deploy. And you can pack a lot more containers than virtual machines on a single host.
Now Windows 2016 offers two Container models: Windows Containers and Hyper-V Containers. They differ in the fact that the isolation level is not the same: while the former shares the kernel with the host, the latter runs in a lightweight virtual machine (called partition) with a separated kernel.
Concerning Windows Containers, the process isolation mechanism is done by the Docker daemon, or service if you like, which allow them to reuse the host kernel through a sandbox. For this to happen, containerization primitives have been added to the Windows 2016 kernel (and to Windows 10 Anniversary Update), which is the reason why you won’t be able to run container on Windows 2012 R2. There is no tiny Linux virtual machine involved in this process, like you could read somewhere.
Source: https://blog.docker.com/2015/08/tp-docker-engine-windows-server-2016/
Concerning Hyper-V Containers, though the process isolation is achieved through the use of a minimalist hypervisor which is started with the Container and teared down when you stop it, they can also be managed with the Docker Client:
docker run --isolation=hyperv microsoft/nanoserver
There is for sure a greater overhead when you setup a Hyper-V Container, and startup times are a bit longer, but each of these two types of Containers has its use.
Ok, let's make a step back and see all this from the beginning.
The first step when setting up Containers on a vanilla Windows 2016 is to:
Install-WindowsFeature Containers
Success Restart Needed Exit Code      Feature Result
------- -------------- ---------      --------------
True    Yes            SuccessRest... {Containers}
WARNING: You must restart this server to finish the installation process.
Restart-Computer
This installs 10 new cmdlets:
(Get-Command).where{$_.Source -match 'Containers'}

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Add-ContainerNetworkAdapter                        1.0.0.0    Containers
Cmdlet          Add-ContainerNetworkAdapterStaticMapping           1.0.0.0    Containers
Cmdlet          Get-ContainerNetwork                               1.0.0.0    Containers
Cmdlet          Get-ContainerNetworkAdapter                        1.0.0.0    Containers
Cmdlet          Get-ContainerNetworkAdapterStaticMapping           1.0.0.0    Containers
Cmdlet          New-ContainerNetwork                               1.0.0.0    Containers
Cmdlet          Remove-ContainerNetwork                            1.0.0.0    Containers
Cmdlet          Remove-ContainerNetworkAdapter                     1.0.0.0    Containers
Cmdlet          Remove-ContainerNetworkAdapterStaticMapping        1.0.0.0    Containers
Cmdlet          Set-ContainerNetworkAdapter                        1.0.0.0    Containers
PROXY PROBLEMS

At this point the problems started for me. I spent a lot of time trying to understand the right procedure to follow (most of the tests I did were on Windows 2016 Evaluation build 14393.0).
Being in an corporate environment, I had a hard time making the following to work:
Install-PackageProvider ContainerImage
… and eventually failed:
WARNING: MSG:UnableToDownload «https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409» «»
WARNING: Unable to download the list of available providers. Check your internet connection.
WARNING: Unable to download from URI 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409' to ''.
Install-PackageProvider : No match was found for the specified search criteria for the provider 'ContainerImage'. The
package provider requires 'PackageManagement' and 'Provider' tags. Please check if the specified package has the tags.
At line:1 char:1
+ Install-PackageProvider ContainerImage
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (Microsoft.Power...PackageProvider:InstallPackageProvider) [Install-PackageProvider], Exception
    + FullyQualifiedErrorId : NoMatchFoundForProvider,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackageProvider
I tried a whole bunch of workarounds to make this cmdlet to work through a corporate proxy, and download the Nuget package provider. I went from the simple solution, which consists of using Netsh to configure the WinHTTP config after having defined a proxy in IE:
netsh winhttp import proxy source=ie
to using the Configure-Proxy function by fellow MVP Jeff Wouters:
function Configure-Proxy ($Proxy, $Port)
{
# Function that actually does the configuring of the proxy settings.
Set-ItemProperty “HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings” -Name ProxyEnable -Value 1
Set-ItemProperty “HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings” -Name ProxyServer -Value $Proxy”:”$Port
Set-ItemProperty “HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings” -Name ProxyOverride -Value “”
}
 
Configure-Proxy "10.x.x.x" 8080
…to using Install-Package provider with the –Proxy and –ProxyCredential parameters defined :
Install-PackageProvider ContainerImage -Force -proxy http://10.x.x.x:8080 -ProxyCredential $cred –Verbose
Nothing worked:
$Username = "proxyuser"
$Password = ConvertTo-SecureString "proxyuserpw" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential $Username, $Password
Install-PackageProvider ContainerImage -Force -proxy http://10.x.x.x:8080 -ProxyCredential $cred -Verbose
VERBOSE: Using the provider 'Bootstrap' for searching packages.
VERBOSE: Finding the package 'Bootstrap::FindPackage' 'ContainerImage','','','''.
WARNING: Unable to download from URI 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409' to ''.
VERBOSE: Cannot download link 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409', retrying for '2' more
times.
VERBOSE: Cannot download link 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409', retrying for '1' more
times.
VERBOSE: Cannot download link 'https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409', retrying for '0' more
times.
WARNING: Unable to download the list of available providers. Check your internet connection.
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: The -Repository parameter was not specified.  PowerShellGet will use all of the registered repositories.
Install-PackageProvider : No match was found for the specified search criteria for the provider 'ContainerImage'. The
package provider requires 'PackageManagement' and 'Provider' tags. Please check if the specified package has the tags.
At line:1 char:1
+ Install-PackageProvider ContainerImage -Force -proxy http://10.X.x. ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (Microsoft.Power...PackageProvider:InstallPackageProvider) [Install-PackageProvider], Exception
    + FullyQualifiedErrorId : NoMatchFoundForProvider,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackageProvider
I even decided to deliberately ignore the SSL errors using the pieces of code found at Mono project:
add-type @"
     using System.Net;
     using System.Security.Cryptography.X509Certificates;
     public class TrustAllCertsPolicy : ICertificatePolicy {
         public bool CheckValidationResult(
             ServicePoint srvPoint, X509Certificate certificate,
             WebRequest request, int certificateProblem) {
             return true;
         }
     }
"@

[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
but the outcome stayed the same: failure.
To make a long story short, I finished manually downloading the module (version 0.6.4.0) on a computer with direct Internet access, and imported it on my corporate server in the right folder:
To me, it really felt like this cmdlet was never intended to use in enterprise…
Once I went through these tasks, I discovered that this ContainerImage module only had three cmdlets:
Get-Command -Module ContainerImage

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Find-ContainerImage                                0.6.4.0    ContainerImage
Function        Install-ContainerImage                             0.6.4.0    ContainerImage
Function        Save-ContainerImage                                0.6.4.0    ContainerImage
Since these cmdlets didn’t work either because of the corporate firewall, I ended up retrieving the Wim files straight from their web source and copying them on my Windows 2016 test bed.
The download links can be found here.
The content of this file, which basically containes the direct download links, is:
[
{
"Name":  "NanoServer",
"Version":  "10.0.14300.1016",
"Description":  "Container OS Image of Windows Server 2016 Technical Preview 5 : Nano Server Installation",
"SasToken":  "https://az887518.vo.msecnd.net/pshctcontainer/NanoServer-10-0-14300-1016.wim"
},
{
"Name":  "WindowsServerCore",
"Version":  "10.0.14300.1000",
"Description":  "Container OS Image of Windows Server 2016 Technical Preview 5 : Windows Server Core Installation",
"SasToken":  "https://az887518.vo.msecnd.net/pshctcontainer/WindowsServerCore-10-0-14300-1000.wim"
}
]
I populated a ContainerImages folder where I put the image files:
ls

    Directory: C:\ContainerImages


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       12/10/2016     13:45      178574823 NanoServer-10-0-14300-1016.wim
-a----       12/10/2016     14:37     2874089226 WindowsServerCore-10-0-14300-1000.wim
Then tried to install them offline:
Install-ContainerImage -.\NanoServer-10-0-14300-1016.wim
WARNING: MSG:UnableToDownload «https://go.microsoft.com/fwlink/?LinkID=627338&clcid=0x409» «»
WARNING: Unable to download the list of available providers. Check your internet connection.
PackageManagement\Save-Package : No match was found for the specified search criteria and package name
'-.\NanoServer-10-0-14300-1016.wim'. Try Get-PackageSource to see all available registered package sources.
At C:\Program Files\WindowsPowerShell\Modules\ContainerImage\0.6.4.0\ContainerImage.psm1:492 char:23
+ ...   $downloadOutput = PackageManagement\Save-Package @PSBoundParameters
+                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Microsoft.Power...ets.SavePackage:SavePackage) [Save-Package], Exception
    + FullyQualifiedErrorId : NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.SavePackage

The property 'Name' cannot be found on this object. Verify that the property exists.
At C:\Program Files\WindowsPowerShell\Modules\ContainerImage\0.6.4.0\ContainerImage.psm1:494 char:5
+     $Destination = GenerateFullPath -Location $Location `
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

Install-ContainerOSImage : The term 'Install-ContainerOSImage' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At C:\Program Files\WindowsPowerShell\Modules\ContainerImage\0.6.4.0\ContainerImage.psm1:502 char:5
+     Install-ContainerOSImage -WimPath $Destination `
+     ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Install-ContainerOSImage:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Remove-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Program Files\WindowsPowerShell\Modules\ContainerImage\0.6.4.0\ContainerImage.psm1:512 char:8
+     rm $Destination
+        ~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Remove-Item], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.RemoveItemCommand
The key error here is :

The term 'Install-ContainerOSImage' is not recognized as the name of a cmdlet.

Something had changed from the different procedures I had tested in the past, and all lead me to think that the container cmdlets had been deprecated.

MOVING TO DOCKER

So, after reviewing the docs, I saw that Docker is the key required element in the process, as you can read on this article by Neil Peterson:



In the next post I will show how to configure Docker on Windows 2016.

1 comment:

  1. Hello a nice tutorial, thank you I'll certainly use it.

    I do feel it's worth mentioning that your logic around bugs in open source software needs a little more reinforcing if you want to hold that opinion. Just because the bug discovery and replication process is in the open, does not make it logically any less or more prone to bugs than proprietary software -- the benefit is you are more aware of them.

    Thanks again.

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...