Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Docker in PowerShell on Windows 10

Using Docker on Windows 10 (AU)

There are a lot of good reasons to use Docker. Right now, my reason is that I need to work with PowerShell on Linux, and with Windows 10 anniversary update, Windows containers now support Nano Server as well (which is the other logical place to test the new open source PowerShell).

Currently, Docker supports running Linux images or Windows images in their container service, but not both in the same server, so to get both, we need to first install Docker using the installer, (which handles dependencies like requiring Hyper-V and Containers) and then install the most recent version of the Windows service separately, and configure them to run together.

Start by installing the Linux Container server

Here's a full explanation, but you could just run the whole script and then skip to installing nanoserver

NOTE: All of these commands should be run from an elevated host.

$DockerInstaller = Join-Path $Env:Temp InstallDocker.msi
## Personally, I like BITS:
# Start-BitsTransfer https://download.docker.com/win/stable/InstallDocker.msi -Destination $DockerInstaller
Invoke-WebRequest https://download.docker.com/win/stable/InstallDocker.msi -OutFile $DockerInstaller

## Now install it 
msiexec -i $DockerInstaller -quiet

Install the Windows Container server

To support Windows images (including Nano Server) in docker, we need the separate service, which currently means we need the very latest release of the docker service for windows containers. Note that this command writes directly to Program Files, since we're elevated.

Invoke-WebRequest "https://master.dockerproject.org/windows/amd64/dockerd.exe" -OutFile "${Env:ProgramFiles}\docker\dockerd.exe"

We need to manually register the service for that one, and to specify an alternate pipe address to listen on. I also like to customize the service name (by default it's just "docker") so that it matches the one set by the Linux engine.

& "${Env:ProgramFiles}\docker\dockerd.exe" -H npipe:////./pipe/win_engine --service-name=com.docker.windows --register-service

Make sure that Containers work

If you don't want the Linux container host, then you can follow the docs for containers, but since we've already taken care of dependencies by running the Docker installer, we just need to fix one known issue with Windows Containers, which requires disabling Oplocks:

# Ensure the feature is enabled (this was taken care of by the Docker Installer, but for completeness sake) ... 
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V, Containers -All

If you've played with Windows docker previously, and disabled Oplocks, you should enable them again now. If you didn't, there's no need to worry about this.

Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 0 -Force

In fact, to make sure I can tell them apart, I like to customize the display text so I can remember which is which:

Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.windows DisplayName "Docker Engine for Windows"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.windows Description "Windows Containers Server for Docker"

Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.service DisplayName "Docker Engine for Linux"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.service Description "Linux Containers Server For Docker"

Install the Docker PowerShell module

For PowerShell support, there is a Docker module, so we can avoid parsing the output of docker ps -a every time we need to know the name or id of a running image. However, it's also still in pre-release, so you have to install it from it's appveyor feed:

Register-PSRepository -Name DockerPS-Dev -SourceLocation https://ci.appveyor.com/nuget/docker-powershell-dev
Install-Module Docker -Repository DockerPS-Dev -Force

You should not even need to reboot, just start the windows service(s).

Start-Service com.docker* -Passthru

Multiple Docker Services

ScreenShot of docker version

We actually have two Docker services (aka daemons) running, so we can run Linux or Windows docker containers. However, whenever we want to use the second one, we have to provide the host address.

Eventually, Microsoft and Docker will get this worked out so they can all play together and we won't need two services on Windows, but in the meantime, it's not that big of a deal, because we can just put the parameter in a hashtable and splat it each time.

Installing NanoServer

For instance, here's how to get a simple image for each:

$dw = @{ HostAddress = 'npipe://./pipe/win_engine' }
Request-ContainerImage @dw microsoft/nanoserver
Request-ContainerImage ubuntu

And to set up nano and remote into it, using the same $dw hashtable:

$w = New-Container @dw microsoft/nanoserver | Start-Container @dw -Passthru
# interestingly, New-PSSession didn't need me to tell it about the host:
$session = New-PSSession -Name Nano -ContainerId $w.ID

# Now you can enter and `exit` at will, or use Copy-Item -ToSession ...
Enter-PSSession $session

If you forget to specify the HostAddress, you'll just get errors because the container or image isn't available on the other service.

Installing PowerShell on Ubuntu

Since we've gone ahead and installed an ubuntu image, and we're die-hard PowerShellers, we really need that Enter-PSSession functionality for ubuntu, right?

$u = New-Container ubuntu -Input -Terminal -name psu | Start-Container -Passthru
Start-ContainerProcess $u apt-get update
Start-ContainerProcess $u apt-get install libicu55 libunwind8

# download here and push in, just to show how:
Invoke-WebRequest https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.9/powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb -OutFile ~/Downloads/powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb
Copy-ContainerFile $u ~/Downloads/powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb -Destination /home -ToContainer 

# Install it using dpkg -i
Start-ContainerProcess $u dpkg '-i' /home/powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb

And that's it. You can now run PowerShell on your Ubuntu host. I've had to do so using the actual docker exec command rather than Start-ContainerProcess, because

Currently the Docker module locks up the host when it goes interactive.

docker exec -it psu powershell

Remember, Ctrl+P, Ctrl+Q to get out, and to get back in:

docker attach psu

FROM ubuntu:16.04
MAINTAINER Joel Bennett <Jaykul@HuddledMasses.org>
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
libicu55 \
libunwind8 \
&& URL=$(curl https://api.github.com/repos/PowerShell/PowerShell/releases/latest | grep browser_download_url.*ubuntu.*16\.04 | cut -d '"' -f 4) \
&& curl -o ./powershell.deb -L $URL \
&& dpkg -i ./powershell.deb \
&& apt-get remove -y \
ca-certificates \
curl \
&& rm ./powershell.deb \
&& rm -rf /var/lib/apt/lists/*
#requires -RunAsAdministrator
#requires -Version 5.1
function Download {
[CmdletBinding()]
param([Parameter(Mandatory)]$url, [Parameter(Mandatory)]$path)
$null = mkdir (Split-Path $path) -Force
$null = if((Get-Service BITS).Status -eq "Running") {
Start-BitsTransfer $Url -Destination $Path
} else {
Invoke-WebRequest $Url -OutFile $Path
}
Get-Item $Path
}
$Installer = Download "https://download.docker.com/win/stable/InstallDocker.msi" "$Env:Temp\InstallDocker.msi"
$Docker = Download "https://master.dockerproject.org/windows/amd64/dockerd.exe" "$Env:ProgramFiles\docker\dockerd.exe"
## Now install it
msiexec -i $Installer -quiet
&$Docker -H npipe:////./pipe/win_engine --service-name=com.docker.windows --register-service
# Ensure the feature is enabled (this was taken care of by the Docker Installer, but for completeness sake) ...
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V, Containers -All
# Temporary Fix for Containers Bug
Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 0 -Force
# Make the services easier to tell apart
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.windows DisplayName "Docker Engine for Windows"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.windows Description "Windows Containers Server for Docker"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.service DisplayName "Docker Engine for Linux"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\com.docker.service Description "Linux Containers Server For Docker"
# Temporary location for the docker module
Register-PSRepository -Name DockerPS-Dev -SourceLocation https://ci.appveyor.com/nuget/docker-powershell-dev
Install-Module Docker -Repository DockerPS-Dev -Force
# Start the service(s)
Start-Service com.docker* -Passthru
# Set a hashtable for ease:
$dw = @{ HostAddress = 'npipe://./pipe/win_engine' }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment