├── .gitignore
├── prepare-vms
├── azure
│ ├── aks
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── create
│ ├── terraform
│ │ ├── .gitignore
│ │ ├── FirstLogonCommands.xml
│ │ ├── circle-build.sh
│ │ ├── variables.tf
│ │ ├── init.tf
│ │ ├── README.md
│ │ ├── create-passwords.sh
│ │ ├── provision.ps1
│ │ └── windows.tf
│ ├── packer
│ │ ├── bginfo.bgi
│ │ ├── Logo-Docker.jpg
│ │ ├── background.jpg
│ │ ├── vertical-logo-monochromatic.png
│ │ ├── kb4482887.ps1
│ │ ├── install-containers-feature.ps1
│ │ ├── circle-build.sh
│ │ ├── updates.ps1
│ │ ├── openssh.ps1
│ │ ├── windows_2016.json
│ │ ├── windows_2019.json
│ │ └── provision.ps1
│ ├── README.md
│ └── initialize-azure.sh
├── images
│ └── pipeline.png
└── README.md
├── docs
├── assets
│ ├── book.png
│ ├── compose.png
│ ├── dockertls.png
│ ├── keyboard.png
│ ├── rdp-osx.png
│ ├── warning.png
│ ├── base_images.png
│ ├── powershell.png
│ ├── rdp-windows.png
│ ├── docker-windows.png
│ ├── base_images_2019.png
│ ├── connect_rdp_docker.png
│ ├── mvp_docker_captain.png
│ ├── what_is_a_container.png
│ └── rdp-with-local-folder.png
├── Dockerfile
├── start.sh
├── README.md
├── index.html
└── Slides.md
├── LICENSE
├── README.md
├── ABSTRACT.md
└── .circleci
└── config.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.tfstate*
2 | crash.log
3 |
--------------------------------------------------------------------------------
/prepare-vms/azure/aks/.gitignore:
--------------------------------------------------------------------------------
1 | home-*
2 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/.gitignore:
--------------------------------------------------------------------------------
1 | .terraform/
2 | passwords.tf
3 | machines.md
4 | machines.pdf
5 |
--------------------------------------------------------------------------------
/docs/assets/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/book.png
--------------------------------------------------------------------------------
/docs/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM stefanscherer/webserver-windows
2 |
3 | COPY . C:\site
4 | WORKDIR C:\\site
5 |
6 | EXPOSE 8080
7 |
--------------------------------------------------------------------------------
/docs/assets/compose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/compose.png
--------------------------------------------------------------------------------
/docs/assets/dockertls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/dockertls.png
--------------------------------------------------------------------------------
/docs/assets/keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/keyboard.png
--------------------------------------------------------------------------------
/docs/assets/rdp-osx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/rdp-osx.png
--------------------------------------------------------------------------------
/docs/assets/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/warning.png
--------------------------------------------------------------------------------
/docs/assets/base_images.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/base_images.png
--------------------------------------------------------------------------------
/docs/assets/powershell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/powershell.png
--------------------------------------------------------------------------------
/docs/assets/rdp-windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/rdp-windows.png
--------------------------------------------------------------------------------
/docs/assets/docker-windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/docker-windows.png
--------------------------------------------------------------------------------
/docs/assets/base_images_2019.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/base_images_2019.png
--------------------------------------------------------------------------------
/prepare-vms/images/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/prepare-vms/images/pipeline.png
--------------------------------------------------------------------------------
/docs/assets/connect_rdp_docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/connect_rdp_docker.png
--------------------------------------------------------------------------------
/docs/assets/mvp_docker_captain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/mvp_docker_captain.png
--------------------------------------------------------------------------------
/docs/assets/what_is_a_container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/what_is_a_container.png
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/bginfo.bgi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/prepare-vms/azure/packer/bginfo.bgi
--------------------------------------------------------------------------------
/docs/assets/rdp-with-local-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/docs/assets/rdp-with-local-folder.png
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/Logo-Docker.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/prepare-vms/azure/packer/Logo-Docker.jpg
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/prepare-vms/azure/packer/background.jpg
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/vertical-logo-monochromatic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StefanScherer/windows-docker-workshop/HEAD/prepare-vms/azure/packer/vertical-logo-monochromatic.png
--------------------------------------------------------------------------------
/docs/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run -d -p 8000:80 -v "$(pwd):/usr/share/nginx/html" nginx
3 | # python -m SimpleHTTPServer 8000 &
4 | sleep 1
5 | /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --kiosk http://localhost:8000
6 |
--------------------------------------------------------------------------------
/prepare-vms/azure/aks/README.md:
--------------------------------------------------------------------------------
1 | # Create mixed AKS cluster
2 |
3 | https://docs.microsoft.com/en-us/azure/aks/windows-container-cli#run-the-application
4 |
5 |
6 | az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
7 |
--------------------------------------------------------------------------------
/prepare-vms/azure/README.md:
--------------------------------------------------------------------------------
1 | # Azure
2 |
3 | ## Build VM image with Packer
4 |
5 | ```
6 | cd packer
7 | packer build
8 | ```
9 |
10 | ## Create workshop VM's with Terraform
11 |
12 | ```
13 | cd terraform
14 | terraform init
15 | terraform plan
16 | terraform apply -var 'count=3'
17 | ```
18 |
19 | ## Destroy
20 |
21 | ```bash
22 | terraform destroy
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Slides for the Docker on Windows workshop
2 |
3 | ## Content
4 |
5 | - The slides are in the `Slides.md` markdown file.
6 | - Screenshots and diagrams are in the `assets` sub folder.
7 |
8 | ## Run the slides
9 |
10 | Run `start.sh` and open a browser at http://localhost:8000
11 |
12 | ## Description for Remark Markdown
13 |
14 | * https://github.com/gnab/remark/wiki/Markdown
15 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/kb4482887.ps1:
--------------------------------------------------------------------------------
1 | Write-Output "Downloading KB4482887"
2 | curl.exe -o kb4482887.msu http://download.windowsupdate.com/c/msdownload/update/software/updt/2019/02/windows10.0-kb4482887-x64_826158e9ebfcabe08b425bf2cb160cd5bc1401da.msu
3 | Write-Output "Installing KB4482887"
4 | Start-Process wusa.exe -ArgumentList ("kb4482887.msu", '/quiet', '/norestart', "/log:c:\Wusa.log") -Wait
5 | Write-Output "Done."
6 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/install-containers-feature.ps1:
--------------------------------------------------------------------------------
1 | Write-Output "Install Containers feature"
2 | Install-WindowsFeature -Name Containers
3 |
4 | if ((GWMI Win32_Processor).VirtualizationFirmwareEnabled[0] -and (GWMI Win32_Processor).SecondLevelAddressTranslationExtensions[0]) {
5 | Write-Output "Install Hyper-V feature"
6 | Install-WindowsFeature -Name Hyper-V -IncludeManagementTools
7 | } else {
8 | Write-Output "Skipping installation of Hyper-V feature"
9 | }
10 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/circle-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 | # we run in hashicorp/packer Alpine image
5 | apk update
6 | apk add jq git openssh
7 |
8 | cd prepare-vms/azure/packer || exit
9 |
10 | PACKER_VM_SIZE=${PACKER_VM_SIZE:-Standard_D4s_v3}
11 | PACKER_LOCATION=${PACKER_LOCATION:-West Europe}
12 |
13 | set -x
14 |
15 | packer build \
16 | -var "vm_size=${PACKER_VM_SIZE}" \
17 | -var "location=${PACKER_LOCATION}" \
18 | -var "image_name=${PACKER_TEMPLATE}_$CIRCLE_BUILD_NUM" \
19 | "${PACKER_TEMPLATE}.json"
20 |
--------------------------------------------------------------------------------
/prepare-vms/azure/initialize-azure.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | resource_group=windows-docker-workshop-images
3 | region="West Europe"
4 | aadClientName=windows-docker-workshop-packer
5 |
6 | echo "Creating image resource group $resource_group"
7 | az group create -n $resource_group -l "$region" | jq -r .id
8 |
9 | echo "Insert these values in CircleCI plossys-bundle environment variables"
10 | az ad sp create-for-rbac -n $aadClientName --query "{ ARM_CLIENT_ID: appId, ARM_CLIENT_SECRET: password, ARM_TENANT_ID: tenant }"
11 | az account show --query "{ ARM_SUBSCRIPTION_ID: id }"
12 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/FirstLogonCommands.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | cmd /c "copy C:\AzureData\CustomData.bin C:\provision.ps1"CopyScript
5 | 11
6 |
7 |
8 | powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\provision.ps1RunScript
10 | 12
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Stefan Scherer
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/circle-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | if [ -z "${CIRCLE_TAG}" ]; then
5 | echo "Environment variable CIRCLE_TAG must be set"
6 | exit 5
7 | fi
8 |
9 | dns_prefix=${CIRCLE_TAG%%-*}
10 | number_of_machines=${CIRCLE_TAG#*-}
11 |
12 | cd prepare-vms/azure/terraform
13 | apk add pwgen
14 |
15 | ./create-passwords.sh
16 | mkdir -p /tmp/workspace
17 | if [ -f machines.md ]; then
18 | cp machines.md /tmp/workspace
19 | fi
20 |
21 | terraform init
22 | terraform apply \
23 | -var "count=$number_of_machines" \
24 | -var "dns_prefix=$dns_prefix" \
25 | -var "group_name=${dns_prefix}-${number_of_machines}-windows-docker-workshop" \
26 | -var "account=${dns_prefix}${number_of_machines}workshop" \
27 | -auto-approve
28 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/variables.tf:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 | variable "dns_prefix" {
4 | default = "xcel"
5 | }
6 |
7 | variable "count" {
8 | default = "1"
9 | }
10 |
11 | variable "group_name" {
12 | default = "windows-docker-workshop"
13 | }
14 |
15 | variable "account" {
16 | default = "xcelerate"
17 | }
18 |
19 | variable "location" {
20 | default = "westeurope"
21 | }
22 |
23 | variable "azure_dns_suffix" {
24 | description = "Azure DNS suffix for the Public IP"
25 | default = "cloudapp.azure.com"
26 | }
27 |
28 | variable "admin_username" {
29 | default = "training"
30 | }
31 |
32 | variable "workshop_image" {
33 | default = "windows_2019_878"
34 | }
35 |
36 | variable "vm_size" {
37 | default = "Standard_D4s_v3"
38 | }
39 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/updates.ps1:
--------------------------------------------------------------------------------
1 | Install-Module -Name PSWindowsUpdate -RequiredVersion 2.1.1.2 -Force
2 | Get-Command -Module PSWindowsUpdate
3 | Write-Output Listing Windows Updates
4 | $ProgressPreference = 'SilentlyContinue'
5 | Get-WUInstall -MicrosoftUpdate -AcceptAll
6 | Write-Output Installing Windows Updates
7 | Get-WUInstall -Install -MicrosoftUpdate -AcceptAll -IgnoreReboot
8 | Write-Output Done.
9 |
10 | $procname="TiWorker"
11 |
12 | $finished = 0
13 |
14 | while ($finished -lt 3) {
15 |
16 | Start-Sleep 30
17 | Write-Output "Checking for $procname ($finished)"
18 | $output = "$(get-process -erroraction silentlycontinue $procname)"
19 | if ( $output -eq "") {
20 | $finished = $finished + 1
21 | } else {
22 | $finished = 0
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/init.tf:
--------------------------------------------------------------------------------
1 | # Configure the Microsoft Azure Provider
2 | provider "azurerm" {}
3 |
4 | # Create a resource group
5 | resource "azurerm_resource_group" "global" {
6 | location = "${var.location}"
7 | name = "${var.group_name}"
8 | }
9 |
10 | # Create a storage account
11 | resource "azurerm_storage_account" "global" {
12 | account_tier = "Standard"
13 | account_replication_type = "LRS"
14 | location = "${var.location}"
15 | name = "${var.account}"
16 | resource_group_name = "${azurerm_resource_group.global.name}"
17 | }
18 |
19 | data "azurerm_image" "workshop_image" {
20 | name = "${var.workshop_image}"
21 | resource_group_name = "windows-docker-workshop-images"
22 | }
23 |
--------------------------------------------------------------------------------
/prepare-vms/README.md:
--------------------------------------------------------------------------------
1 | # Prepare VM's for the workshop
2 |
3 | You can prepare the Windows Server 2016 VM's with one of these Terraform templates.
4 |
5 | - Azure
6 |
7 | ## Full automated build
8 |
9 | I've implemented a full automated build to run a Windows Docker Workshop very easily.
10 |
11 | 
12 |
13 | - Draft a new GitHub release
14 | - The tag name is `foo-40` to create 40 VM's with DNS prefix `foo`.
15 | - CircleCI build is triggered
16 | - A previous Packer build created a VM image with Docker and Tools.
17 | - A Terraform build creates the 40 VM's with random passwords.
18 | - A Slack notification is sent with all credentials.
19 | - Print the credentials for each attendee and run the workshop :-)
20 |
--------------------------------------------------------------------------------
/prepare-vms/azure/aks/create:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PASSWORD_WIN="XfKXhHoYYtYMh*T"
4 | resourceGroup=xfry-2-windows-docker-workshop
5 | clusterName=xfry-aks-02
6 |
7 | # create 2-node linux pool
8 | az aks create \
9 | --resource-group $resourceGroup \
10 | --name $clusterName \
11 | --node-count 2 \
12 | --enable-addons monitoring \
13 | --kubernetes-version 1.15.5 \
14 | --generate-ssh-keys \
15 | --windows-admin-password $PASSWORD_WIN \
16 | --windows-admin-username azureuser \
17 | --vm-set-type VirtualMachineScaleSets \
18 | --load-balancer-sku standard \
19 | --network-plugin azure
20 |
21 | # create 1-node windows pool
22 | az aks nodepool add \
23 | --resource-group $resourceGroup \
24 | --cluster-name $clusterName \
25 | --os-type Windows \
26 | --name npwin \
27 | --node-count 1 \
28 | --kubernetes-version 1.15.5
29 |
30 | # download kube config
31 | timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
32 | mv ~/.kube ~/.kube_$timestamp
33 | az aks get-credentials --resource-group $resourceGroup --name $clusterName
34 | mv ~/.kube home-.kube-$clusterName
35 | mv ~/.kube_$timestamp ~/.kube
36 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/README.md:
--------------------------------------------------------------------------------
1 | # Azure
2 |
3 | ## Install Terraform
4 |
5 | ```
6 | brew install terraform
7 | ```
8 |
9 | ## Secrets
10 |
11 | Get your Azure ID's and secret with `pass`
12 |
13 | ```
14 | eval $(pass azure-terraform)
15 | ```
16 |
17 | You will need these environment variables for terraform
18 |
19 | ```
20 | export ARM_SUBSCRIPTION_ID="uuid"
21 | export ARM_CLIENT_ID="uuid"
22 | export ARM_CLIENT_SECRET="secret"
23 | export ARM_TENANT_ID="uuid"
24 | ```
25 |
26 | ## Configure
27 |
28 | Adjust the file `variables.tf` to your needs to choose
29 |
30 | - location / region
31 | - DNS prefix and suffix
32 | - size of the VM's, default is `Standard_D2_v2`
33 | - username and password
34 |
35 | ## Plan
36 |
37 | ```bash
38 | terraform plan
39 | ```
40 |
41 | ## Create / Apply
42 |
43 | ```bash
44 | terraform apply
45 | ```
46 |
47 | If you want multiple machines, increase the count
48 |
49 | ```
50 | terraform apply -var count=3
51 | ```
52 |
53 | Notice: Changing the count afterwards doesn't seem to work with Azure. So be sure to create the resource group with the correct count initially.
54 |
55 | ## Destroy
56 |
57 | ```bash
58 | terraform destroy
59 | ```
60 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/create-passwords.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | password_length=12
5 | number_of_machines=${1:-60}
6 | username=training
7 | dns_prefix=wdw
8 |
9 | if [ ! -z "${CIRCLE_TAG}" ]; then
10 | dns_prefix=${CIRCLE_TAG%%-*}
11 | number_of_machines=${CIRCLE_TAG#*-}
12 | fi
13 |
14 | if [ ! -z "${PASSWORDS_TF_URL}" ]; then
15 | echo "Downloading passwords.tf"
16 | curl --fail -L -o passwords.tf "${PASSWORDS_TF_URL}"
17 | else
18 | echo "Creating passwords.tf with $number_of_machines passwords"
19 | i=0
20 | echo 'variable "admin_password" {
21 | default = [' > passwords.tf
22 |
23 | rm -f machines.md
24 | for password in $(pwgen $password_length "$number_of_machines"); do
25 | echo " \"$password\"," >> passwords.tf
26 | if [ $((i%6)) -eq 0 ]; then
27 | echo "# Accounts $((i+1)) - $((i+6))" >> machines.md
28 | echo "" >> machines.md
29 | fi
30 | # shellcheck disable=SC2129
31 | echo "| FQDN | $dns_prefix-$(printf "%02d" $((i+1))).westeurope.cloudapp.azure.com |" >> machines.md
32 | echo "|----------|-------------------------------------|" >> machines.md
33 | echo "| Username | \`$username\` |" >> machines.md
34 | echo "| Password | \`$password\` |" >> machines.md
35 | echo "" >> machines.md
36 |
37 | i=$((i+1))
38 | done
39 |
40 | echo ' "dummy"
41 | ]
42 | }' >> passwords.tf
43 | fi
44 |
45 | exists()
46 | {
47 | command -v "$1" >/dev/null 2>&1
48 | }
49 |
50 | if [ -f machines.md ]; then
51 | if exists mdpdf; then
52 | mdpdf machines.md
53 | fi
54 | fi
55 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/openssh.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
4 |
5 | Write-Output "Downloading OpenSSH"
6 | Invoke-WebRequest "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v7.9.0.0p1-Beta/OpenSSH-Win64.zip" -OutFile OpenSSH-Win64.zip -UseBasicParsing
7 |
8 | Write-Output "Expanding OpenSSH"
9 | Expand-Archive OpenSSH-Win64.zip C:\
10 | Remove-Item -Force OpenSSH-Win64.zip
11 |
12 | Write-Output "Disabling password authentication"
13 | # Add-Content C:\OpenSSH-Win64\sshd_config "`nPasswordAuthentication no"
14 | Add-Content C:\OpenSSH-Win64\sshd_config "`nUseDNS no"
15 |
16 | Push-Location C:\OpenSSH-Win64
17 |
18 | Write-Output "Installing OpenSSH"
19 | & .\install-sshd.ps1
20 |
21 | Write-Output "Generating host keys"
22 | .\ssh-keygen.exe -A
23 |
24 | Write-Output "Fixing host file permissions"
25 | & .\FixHostFilePermissions.ps1 -Confirm:$false
26 |
27 | Write-Output "Fixing user file permissions"
28 | & .\FixUserFilePermissions.ps1 -Confirm:$false
29 |
30 | Pop-Location
31 |
32 | $newPath = 'C:\OpenSSH-Win64;' + [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine)
33 | [Environment]::SetEnvironmentVariable("PATH", $newPath, [EnvironmentVariableTarget]::Machine)
34 |
35 | Write-Output "Adding public key to authorized_keys"
36 | $keyPath = "~\.ssh\authorized_keys"
37 | New-Item -Type Directory ~\.ssh > $null
38 | $sshKey | Out-File $keyPath -Encoding Ascii
39 |
40 | Write-Output "Opening firewall port 22"
41 | New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH
42 |
43 | Write-Output "Setting sshd service startup type to 'Automatic'"
44 | Set-Service sshd -StartupType Automatic
45 | Set-Service ssh-agent -StartupType Automatic
46 | Write-Output "Setting sshd service restart behavior"
47 | sc.exe failure sshd reset= 86400 actions= restart/500
48 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/provision.ps1:
--------------------------------------------------------------------------------
1 | Start-Transcript -Path C:\provision.log
2 |
3 | net user vagrant /delete
4 |
5 | Set-MpPreference -DisableRealtimeMonitoring $true
6 |
7 | New-ItemProperty -Path HKCU:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value "1" -Force
8 |
9 | function Get-HostToIP($hostname) {
10 | $result = [system.Net.Dns]::GetHostByName($hostname)
11 | $result.AddressList | ForEach-Object {$_.IPAddressToString }
12 | }
13 |
14 | Write-Output "provision.ps1"
15 | Write-Output "HostName = $($HostName)"
16 |
17 | $PublicIPAddress = Get-HostToIP($HostName)
18 |
19 | Write-Output "PublicIPAddress = $($PublicIPAddress)"
20 | Write-Output "USERPROFILE = $($env:USERPROFILE)"
21 | Write-Output "pwd = $($pwd)"
22 |
23 | Write-Output Install bginfo
24 | [Environment]::SetEnvironmentVariable('FQDN', $HostName, [EnvironmentVariableTarget]::Machine)
25 | [Environment]::SetEnvironmentVariable('PUBIP', $PublicIPAddress, [EnvironmentVariableTarget]::Machine)
26 |
27 | refreshenv
28 | $env:PATH=$env:PATH + ';C:\Program Files\Mozilla Firefox;C:\Program Files\Microsoft VS Code;C:\Program Files\Git\bin'
29 | [Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine)
30 |
31 | # Atom is installed to C:\Users\training\AppData\Local\atom so we have to do it in Terraform and not Packer
32 | choco install -y atom
33 |
34 | # Create shortcuts
35 | $WshShell = New-Object -comObject WScript.Shell
36 | $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\PowerShell.lnk")
37 | $Shortcut.TargetPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
38 | $shortcut.WorkingDirectory = "$Home"
39 | $Shortcut.Save()
40 |
41 | # Run some containers
42 | docker run --rm mcr.microsoft.com/windows/servercore:ltsc2019 cmd
43 | docker run --rm mcr.microsoft.com/windows/nanoserver:1809 cmd
44 | # docker run --rm mcr.microsoft.com/windows-insider:10.0.17744.1001 cmd
45 |
46 | Write-Output Cleaning up
47 | Remove-Item C:\provision.ps1
48 |
49 | Restart-Computer
50 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/windows_2016.json:
--------------------------------------------------------------------------------
1 | {
2 | "builders": [
3 | {
4 | "azure_tags": {
5 | "circle_build_num": "{{user `circle_build_num`}}"
6 | },
7 | "client_id": "{{user `client_id`}}",
8 | "client_secret": "{{user `client_secret`}}",
9 | "communicator": "winrm",
10 | "image_offer": "WindowsServer",
11 | "image_publisher": "MicrosoftWindowsServer",
12 | "image_sku": "2016-Datacenter-with-Containers",
13 | "location": "{{user `location`}}",
14 | "managed_image_name": "{{user `image_name`}}",
15 | "managed_image_resource_group_name": "windows-docker-workshop-images",
16 | "os_type": "Windows",
17 | "subscription_id": "{{user `subscription_id`}}",
18 | "tenant_id": "{{user `tenant_id`}}",
19 | "type": "azure-arm",
20 | "vm_size": "{{user `vm_size`}}",
21 | "winrm_insecure": "true",
22 | "winrm_timeout": "3m",
23 | "winrm_use_ssl": "true",
24 | "winrm_username": "packer"
25 | }
26 | ],
27 | "post-processors": [],
28 | "provisioners": [
29 | {
30 | "scripts": [
31 | "./openssh.ps1",
32 | "./provision.ps1"
33 | ],
34 | "type": "powershell"
35 | },
36 | {
37 | "inline": [
38 | "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
39 | "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
40 | ],
41 | "type": "powershell"
42 | }
43 | ],
44 | "variables": {
45 | "circle_build_num": "{{env `CIRCLE_BUILD_NUM`}}",
46 | "client_id": "{{env `ARM_CLIENT_ID`}}",
47 | "client_secret": "{{env `ARM_CLIENT_SECRET`}}",
48 | "image_name": "win2016",
49 | "location": "West Europe",
50 | "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
51 | "tenant_id": "{{env `ARM_TENANT_ID`}}",
52 | "vm_size": "Standard_D2_v3"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # windows-docker-workshop
2 | [](https://gitter.im/windows-docker-workshop/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3 | [](https://circleci.com/gh/StefanScherer/windows-docker-workshop)
4 |
5 | This is the material for the workshop "Getting started with Docker on Windows Server 2016" written by Stefan Scherer.
6 |
7 | ## Content
8 |
9 | - Setting up Docker Host
10 | - Learn about the base OS images and Docker fundamentals
11 | - Secure remote Docker access via TLS
12 | - Networking, Logging
13 | - Dockerfile best practices
14 | - Dockerizing a Windows application into containers
15 | - Persisting data using volumes
16 |
17 | ## Workshop slides
18 |
19 | - The slides are in the `docs` folder.
20 | - You can view the slides locally by opening `docs/index.html` in a browser.
21 | - To view them online open [https://stefanscherer.github.io/windows-docker-workshop](https://stefanscherer.github.io/windows-docker-workshop).
22 |
23 | ## Past workshops
24 |
25 | - [DevOps Gathering 2017](https://devops-gathering.io/workshops/Getting-started-with-Docker-on-Windows-Server-2016/), Bochum, Germany, 2017-03-23
26 | - [Docker Franken Meetup](https://www.meetup.com/de-DE/Docker-Bamberg/events/251391306/), Bamberg, Germany, 2018-07-05
27 | - [Chocolatey Fest 2018](https://chocolateyfest2018.sched.com/event/GBnD), San Francisco, California, 2018-10-08
28 |
29 | ## Running the workshop
30 |
31 | - Prepare a couple of Win2016 VM's in in your favorite cloud.
32 | - You can use the Terraform templates in the `prepare-vms` folder to create VM's in Azure or AWS.
33 | - Check and increase your limits two weeks before running the workshop.
34 | - Check and ask in advance if your workshop location allows outgoing RDP connections at port 3389.
35 |
36 | ## Problems? Questions? Feedback welcome
37 |
38 | - If you have problems, found a bug or have questions, don't hesitate to contact me.
39 | - Open an issue in this repo or send me a pull request.
40 |
41 | Have fun!
42 |
--------------------------------------------------------------------------------
/ABSTRACT.md:
--------------------------------------------------------------------------------
1 | # Getting started with Docker on Windows Server 2016
2 |
3 | ## Abstract
4 |
5 | In this hands-on workshop you'll learn about the new features of Windows Server 2016: Docker and Windows Containers. We will start how to install the Docker Engine on Microsoft Azure and secure it for remote access with TLS certificates.
6 |
7 | You will learn the basic Docker commands to create Docker images and push them to the Docker Hub. In order to run a Docker container you'll pull a specific Docker image from the Docker Hub and run it on your Docker Engine. With this practice you'll also learn how to configure TCP/IP ports and volumes to connect your running container to the outside world.
8 |
9 | We'll dig deep into the art of crafting Dockerfiles for Windows with utilizing PowerShell commands. This way you'll learn some best practices how to write almost perfect Dockerfiles for Windows.
10 |
11 | Finally we will take a real world application based upon microservices and dockerize it into a set of Windows containers. You'll learn how to use Docker Compose to start, orchestrate and scale the whole application.
12 |
13 | ## Agenda
14 |
15 | - Setup Docker Engine on Windows Server 2016
16 | - Learn about the base OS images
17 | - Secure remote Docker access via TLS
18 | - Networking, Logging
19 | - Persisting data using volumes
20 | - Dockerfile best practices
21 | - Dockerizing a Windows application
22 |
23 | ## What you will need
24 |
25 | - Basic Windows and Docker experience recommended, but not required
26 | - Bring your own laptop (Windows, macOS, Linux)
27 | - You'll need RDP client and Docker client on your laptop
28 |
29 | ## Bio
30 |
31 | ### Stefan Scherer
32 |
33 | Stefan is a Sr. Software Engineer at SEAL Systems. He enjoys automating things and uses Docker at work in various test environments. He is an early adopter of Docker on Windows, giving feedback and sharing his experience as a Microsoft MVP and Docker Captain. He is a maintainer of several Docker related Chocolatey packages. He also is member of the Hypriot team that gives the community a very easy experience for Docker on ARM devices.
34 |
35 | - https://avatars2.githubusercontent.com/u/207759?v=3&s=460
36 |
37 | ### Dieter Reuter
38 |
39 | Dieter Reuter is a Senior Consultant at bee42 solutions gmbh and Docker Captain at Hypriot with more than 30 years of enterprise IT experience and unquenchable curiosity. Dealing with cutting-edge cloud technologies, obsessed with the DevOps movement and its associated automation. Dieter is using Docker from the early days on and is pushing Docker and Containers to ARM based IoT devices with rocket-like speed.
40 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/windows_2019.json:
--------------------------------------------------------------------------------
1 | {
2 | "builders": [
3 | {
4 | "azure_tags": {
5 | "circle_build_num": "{{user `circle_build_num`}}"
6 | },
7 | "client_id": "{{user `client_id`}}",
8 | "client_secret": "{{user `client_secret`}}",
9 | "communicator": "winrm",
10 | "image_offer": "WindowsServer",
11 | "image_publisher": "MicrosoftWindowsServer",
12 | "image_sku": "2019-Datacenter-with-Containers",
13 | "location": "{{user `location`}}",
14 | "managed_image_name": "{{user `image_name`}}",
15 | "managed_image_resource_group_name": "windows-docker-workshop-images",
16 | "os_type": "Windows",
17 | "subscription_id": "{{user `subscription_id`}}",
18 | "tenant_id": "{{user `tenant_id`}}",
19 | "type": "azure-arm",
20 | "vm_size": "{{user `vm_size`}}",
21 | "winrm_insecure": "true",
22 | "winrm_timeout": "3m",
23 | "winrm_use_ssl": "true",
24 | "winrm_username": "packer"
25 | }
26 | ],
27 | "post-processors": [],
28 | "provisioners": [
29 | {
30 | "scripts": [
31 | "./updates.ps1",
32 | "./install-containers-feature.ps1"
33 | ],
34 | "type": "powershell"
35 | },
36 | {
37 | "restart_timeout": "{{user `restart_timeout`}}",
38 | "type": "windows-restart"
39 | },
40 | {
41 | "scripts": [
42 | "./openssh.ps1",
43 | "./updates.ps1"
44 | ],
45 | "type": "powershell"
46 | },
47 | {
48 | "restart_timeout": "{{user `restart_timeout`}}",
49 | "type": "windows-restart"
50 | },
51 | {
52 | "scripts": [
53 | "./provision.ps1"
54 | ],
55 | "type": "powershell"
56 | },
57 | {
58 | "inline": [
59 | "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
60 | "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
61 | ],
62 | "type": "powershell"
63 | }
64 | ],
65 | "variables": {
66 | "circle_build_num": "{{env `CIRCLE_BUILD_NUM`}}",
67 | "client_id": "{{env `ARM_CLIENT_ID`}}",
68 | "client_secret": "{{env `ARM_CLIENT_SECRET`}}",
69 | "image_name": "win2019",
70 | "location": "West Europe",
71 | "restart_timeout": "5m",
72 | "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
73 | "tenant_id": "{{env `ARM_TENANT_ID`}}",
74 | "vm_size": "Standard_D2_v3"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/prepare-vms/azure/terraform/windows.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_virtual_network" "windows" {
2 | name = "windows-virtnet"
3 | address_space = ["10.0.0.0/16"]
4 | location = "${var.location}"
5 | resource_group_name = "${azurerm_resource_group.global.name}"
6 | }
7 |
8 | resource "azurerm_subnet" "windows" {
9 | name = "windows-${format("%02d", count.index + 1)}-sn"
10 | resource_group_name = "${azurerm_resource_group.global.name}"
11 | virtual_network_name = "${azurerm_virtual_network.windows.name}"
12 | address_prefix = "10.0.2.0/24"
13 | }
14 |
15 | resource "azurerm_network_interface" "windows" {
16 | count = "${var.count}"
17 | name = "windows-${format("%02d", count.index + 1)}-nic"
18 | location = "${var.location}"
19 | resource_group_name = "${azurerm_resource_group.global.name}"
20 |
21 | ip_configuration {
22 | name = "testconfiguration1"
23 | subnet_id = "${azurerm_subnet.windows.id}"
24 | public_ip_address_id = "${element(azurerm_public_ip.windows.*.id, count.index)}"
25 | private_ip_address_allocation = "dynamic"
26 | }
27 | }
28 |
29 | resource "azurerm_public_ip" "windows" {
30 | count = "${var.count}"
31 | domain_name_label = "${var.dns_prefix}-${format("%02d", count.index + 1)}"
32 | idle_timeout_in_minutes = 30
33 | location = "${var.location}"
34 | name = "windows-${format("%02d", count.index + 1)}-publicip"
35 | public_ip_address_allocation = "dynamic"
36 | resource_group_name = "${azurerm_resource_group.global.name}"
37 | }
38 |
39 | resource "azurerm_storage_container" "windows" {
40 | container_access_type = "private"
41 | count = "${var.count}"
42 | name = "windows-${format("%02d", count.index + 1)}-storage"
43 | resource_group_name = "${azurerm_resource_group.global.name}"
44 | storage_account_name = "${azurerm_storage_account.global.name}"
45 | }
46 |
47 | resource "azurerm_virtual_machine" "windows" {
48 | count = "${var.count}"
49 | name = "windows-${format("%02d", count.index + 1)}-vm"
50 | location = "${var.location}"
51 | resource_group_name = "${azurerm_resource_group.global.name}"
52 | network_interface_ids = ["${element(azurerm_network_interface.windows.*.id, count.index)}"]
53 | vm_size = "${var.vm_size}"
54 |
55 | storage_image_reference {
56 | id = "${data.azurerm_image.workshop_image.id}"
57 | }
58 |
59 | storage_os_disk {
60 | name = "windows-${format("%02d", count.index + 1)}-osdisk"
61 | caching = "ReadWrite"
62 | create_option = "FromImage"
63 | managed_disk_type = "Standard_LRS"
64 | }
65 |
66 | os_profile {
67 | computer_name = "${var.dns_prefix}-${format("%02d", count.index + 1)}"
68 | admin_username = "${var.admin_username}"
69 | admin_password = "${var.admin_password[count.index]}"
70 | custom_data = "${base64encode("Param($HostName = \"${var.dns_prefix}-${format("%02d", count.index + 1)}.${var.location}.${var.azure_dns_suffix}\") ${file("./provision.ps1")}")}"
71 | }
72 |
73 | os_profile_windows_config {
74 | provision_vm_agent = true
75 | enable_automatic_upgrades = true
76 |
77 | additional_unattend_config {
78 | pass = "oobeSystem"
79 | component = "Microsoft-Windows-Shell-Setup"
80 | setting_name = "AutoLogon"
81 | content = "${var.admin_password[count.index]}true1${var.admin_username}"
82 | }
83 |
84 | additional_unattend_config {
85 | pass = "oobeSystem"
86 | component = "Microsoft-Windows-Shell-Setup"
87 | setting_name = "FirstLogonCommands"
88 | content = "${file("./FirstLogonCommands.xml")}"
89 | }
90 | }
91 |
92 | tags {
93 | environment = "training"
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | jobs:
4 | shellcheck:
5 | docker:
6 | - image: nlknguyen/alpine-shellcheck
7 | auth:
8 | username: $DOCKERHUB_USERNAME
9 | password: $DOCKERHUB_PASSWORD
10 |
11 | steps:
12 | - checkout
13 |
14 | - run:
15 | name: Run Shellcheck
16 | command: shellcheck -s bash docs/*.sh prepare-vms/azure/*.sh prepare-vms/azure/packer/*.sh
17 |
18 | psscriptanalyzer:
19 | docker:
20 | - image: microsoft/powershell
21 | auth:
22 | username: $DOCKERHUB_USERNAME
23 | password: $DOCKERHUB_PASSWORD
24 |
25 | steps:
26 | - checkout
27 |
28 | - run:
29 | name: Run PSScriptAnalyzer
30 | command: |
31 | pwsh -c 'Install-Module PSScriptAnalyzer -Force;
32 | $Results = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Error;
33 | $Results
34 | if ($Results) {
35 | Throw "Test failed. PSScriptAnalyzer found issues."
36 | }
37 | '
38 |
39 | packer-validate:
40 | docker:
41 | - image: hashicorp/packer:1.3.5
42 | auth:
43 | username: $DOCKERHUB_USERNAME
44 | password: $DOCKERHUB_PASSWORD
45 |
46 | steps:
47 | - checkout
48 |
49 | - run:
50 | name: Run Packer validate
51 | command: |
52 | cd prepare-vms/azure/packer
53 | for filename in *.json; do
54 | packer validate $filename
55 | done
56 |
57 | packer: &packer
58 | docker:
59 | - image: hashicorp/packer:1.3.5
60 | auth:
61 | username: $DOCKERHUB_USERNAME
62 | password: $DOCKERHUB_PASSWORD
63 |
64 | steps:
65 | - checkout
66 |
67 | - run:
68 | name: Run Packer build on Azure
69 | command: ./prepare-vms/azure/packer/circle-build.sh
70 | no_output_timeout: 1800
71 |
72 | terraform-validate:
73 | docker:
74 | - image: hashicorp/terraform:0.11.12
75 | auth:
76 | username: $DOCKERHUB_USERNAME
77 | password: $DOCKERHUB_PASSWORD
78 |
79 | steps:
80 | - checkout
81 |
82 | - run:
83 | name: Run Terraform validate
84 | command: |
85 | cd prepare-vms/azure/terraform
86 | apk add pwgen
87 | ./create-passwords.sh
88 | terraform init
89 | terraform validate
90 |
91 | terraform: &terraform
92 | docker:
93 | - image: hashicorp/terraform:0.11.12
94 | auth:
95 | username: $DOCKERHUB_USERNAME
96 | password: $DOCKERHUB_PASSWORD
97 |
98 | steps:
99 | - checkout
100 |
101 | - run:
102 | name: Build infrastructure
103 | command: ./prepare-vms/azure/terraform/circle-build.sh
104 |
105 | - persist_to_workspace:
106 | root: /tmp/workspace
107 | paths:
108 | - .
109 |
110 | makepdf:
111 | machine: true
112 |
113 | steps:
114 | - checkout
115 |
116 | - attach_workspace:
117 | at: /tmp/workspace
118 |
119 | - run:
120 | name: Make PDF
121 | command: |
122 | cd /tmp/workspace
123 | if [ -f machines.md ]; then
124 | npm install -g mdpdf
125 | cp machines.md ${CIRCLE_TAG}-machines.md
126 | mdpdf ${CIRCLE_TAG}-machines.md
127 | echo "Uploading ${CIRCLE_TAG}-machines.pdf to Slack"
128 | curl -F file=@${CIRCLE_TAG}-machines.pdf "https://slack.com/api/files.upload?token=${SLACK_TOKEN}&channels=%40stefanscherer&pretty=1"
129 | fi
130 |
131 | build-windows-2016:
132 | <<: *packer
133 | environment:
134 | - PACKER_TEMPLATE: "windows_2016"
135 |
136 | build-windows-2019:
137 | <<: *packer
138 | environment:
139 | - PACKER_TEMPLATE: "windows_2019"
140 |
141 | workflows:
142 | version: 2
143 |
144 | test-and-build:
145 | jobs:
146 | - shellcheck
147 | - psscriptanalyzer
148 | - packer-validate
149 | - terraform-validate
150 | - build-windows-2019:
151 | requires:
152 | - shellcheck
153 | - psscriptanalyzer
154 | - packer-validate
155 | - terraform-validate
156 | filters:
157 | tags:
158 | ignore: /.*/
159 | branches:
160 | only:
161 | - master
162 |
163 | build-infrastructure:
164 | jobs:
165 | - shellcheck:
166 | filters:
167 | branches:
168 | ignore: /.*/
169 | tags:
170 | only: /.*-.*/
171 | - psscriptanalyzer:
172 | filters:
173 | branches:
174 | ignore: /.*/
175 | tags:
176 | only: /.*-.*/
177 | - packer-validate:
178 | filters:
179 | branches:
180 | ignore: /.*/
181 | tags:
182 | only: /.*-.*/
183 | - terraform-validate:
184 | filters:
185 | branches:
186 | ignore: /.*/
187 | tags:
188 | only: /.*-.*/
189 | - terraform:
190 | requires:
191 | - shellcheck
192 | - psscriptanalyzer
193 | - packer-validate
194 | - terraform-validate
195 | filters:
196 | branches:
197 | ignore: /.*/
198 | tags:
199 | only: /.*-.*/
200 | - makepdf:
201 | requires:
202 | - terraform
203 | filters:
204 | branches:
205 | ignore: /.*/
206 | tags:
207 | only: /.*-.*/
208 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Docker on Windows workshop
7 |
176 |
177 |
178 |
180 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/prepare-vms/azure/packer/provision.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | $docker_provider = "DockerMsftProvider"
4 | $docker_version = "19.03.5"
5 |
6 | Write-Output 'Set Windows Updates to manual'
7 | Cscript $env:WinDir\System32\SCregEdit.wsf /AU 1
8 | Net stop wuauserv
9 | Net start wuauserv
10 |
11 | Write-Output 'Disable Windows Defender'
12 | Set-MpPreference -DisableRealtimeMonitoring $true
13 |
14 | Write-Output 'Do not open Server Manager at logon'
15 | New-ItemProperty -Path HKCU:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value "1" -Force
16 |
17 | Write-Output 'Install bginfo'
18 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
19 |
20 | if (!(Test-Path 'c:\Program Files\sysinternals')) {
21 | New-Item -Path 'c:\Program Files\sysinternals' -type directory -Force -ErrorAction SilentlyContinue
22 | }
23 | if (!(Test-Path 'c:\Program Files\sysinternals\bginfo.exe')) {
24 | (New-Object Net.WebClient).DownloadFile('http://live.sysinternals.com/bginfo.exe', 'c:\Program Files\sysinternals\bginfo.exe')
25 | }
26 | if (!(Test-Path 'c:\Program Files\sysinternals\bginfo.bgi')) {
27 | (New-Object Net.WebClient).DownloadFile('https://github.com/StefanScherer/windows-docker-workshop/raw/master/prepare-vms/azure/packer/bginfo.bgi', 'c:\Program Files\sysinternals\bginfo.bgi')
28 | }
29 | if (!(Test-Path 'c:\Program Files\sysinternals\background.jpg')) {
30 | (New-Object Net.WebClient).DownloadFile('https://github.com/StefanScherer/windows-docker-workshop/raw/master/prepare-vms/azure/packer/background.jpg', 'c:\Program Files\sysinternals\background.jpg')
31 | }
32 | $vbsScript = @'
33 | WScript.Sleep 2000
34 | Dim objShell
35 | Set objShell = WScript.CreateObject( "WScript.Shell" )
36 | objShell.Run("""c:\Program Files\sysinternals\bginfo.exe"" /accepteula ""c:\Program Files\sysinternals\bginfo.bgi"" /silent /timer:0")
37 | '@
38 | $vbsScript | Out-File 'c:\Program Files\sysinternals\bginfo.vbs'
39 | Set-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -Name bginfo -Value 'wscript "c:\Program Files\sysinternals\bginfo.vbs"'
40 | wscript "c:\Program Files\sysinternals\bginfo.vbs"
41 |
42 | Write-Output 'Install Chocolatey'
43 | Invoke-WebRequest 'https://chocolatey.org/install.ps1' -UseBasicParsing | Invoke-Expression
44 |
45 | Write-Output 'Install editors'
46 | choco install -y vscode
47 |
48 | Write-Output 'Install Git'
49 | choco install -y git
50 |
51 | Write-Output 'Install browsers'
52 | choco install -y googlechrome
53 | choco install -y firefox
54 |
55 | Write-Output 'Install Docker Compose'
56 | choco install -y docker-compose
57 | choco install -y kubernetes-cli
58 |
59 | choco install -y poshgit
60 | choco install -y visualstudiocode
61 |
62 | if (Test-Path $env:ProgramFiles\docker) {
63 | Write-Output Update Docker
64 | Install-Package -Name docker -ProviderName $docker_provider -Verbose -Update -RequiredVersion $docker_version -Force
65 | } else {
66 | Write-Output "Install-PackageProvider ..."
67 | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
68 | Write-Output "Install-Module $docker_provider ..."
69 | Install-Module -Name $docker_provider -Force
70 | Write-Output "Install-Package version $docker_version ..."
71 | Set-PSRepository -InstallationPolicy Trusted -Name PSGallery
72 | $ErrorActionStop = 'SilentlyContinue'
73 | Install-Package -Name docker -ProviderName $docker_provider -RequiredVersion $docker_version -Force
74 | Set-PSRepository -InstallationPolicy Untrusted -Name PSGallery
75 | $env:Path = $env:Path + ";$($env:ProgramFiles)\docker"
76 | }
77 |
78 | Write-Output 'Staring Docker service'
79 | Start-Service docker
80 |
81 | Write-Output 'Docker version'
82 | docker version
83 |
84 | $images =
85 | 'mcr.microsoft.com/windows/servercore:ltsc2019',
86 | 'mcr.microsoft.com/windows/nanoserver:1809',
87 | 'mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019',
88 | 'mcr.microsoft.com/dotnet/framework/sdk:4.7.2-20190312-windowsservercore-ltsc2019',
89 | 'mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-ltsc2019',
90 | 'mcr.microsoft.com/dotnet/framework/aspnet',
91 | 'mcr.microsoft.com/dotnet/core/runtime:3.0',
92 | 'mcr.microsoft.com/dotnet/core/sdk:3.0.100',
93 | 'mcr.microsoft.com/dotnet/core/aspnet:3.0',
94 | 'dak4dotnet/sql-server:2017',
95 | 'nats:2.1.0',
96 | 'dockersamples/aspnet-monitoring-exporter:4.7.2-windowsservercore-ltsc2019',
97 | 'dockersamples/aspnet-monitoring-grafana:5.2.1-windowsservercore-ltsc2019',
98 | 'dockersamples/aspnet-monitoring-prometheus:2.3.1-windowsservercore-ltsc2019',
99 | 'sixeyed/elasticsearch:5.6.11-windowsservercore-ltsc2019',
100 | 'sixeyed/kibana:5.6.11-windowsservercore-ltsc2019',
101 | 'traefik:1.7.18-windowsservercore-1809'
102 |
103 | Write-Output 'Pulling images'
104 | foreach ($tag in $images) {
105 | Write-Output " Pulling image $tag"
106 | & docker image pull $tag
107 | }
108 |
109 | Write-Output '* Configuring environment'
110 | refreshenv
111 | $env:PATH=$env:PATH + ';C:\Program Files\Mozilla Firefox;C:\Program Files\Git\bin'
112 | [Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine)
113 | $env:workshop='C:\scm\dak4.net'
114 | [Environment]::SetEnvironmentVariable('workshop', $env:workshop, [EnvironmentVariableTarget]::Machine)
115 |
116 | New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value "1" -Force
117 | New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager\Oobe -Name DoNotOpenInitialConfigurationTasksAtLogon -PropertyType DWORD -Value "1" -Force
118 |
119 | Write-Output '* Cloning the workshop repo'
120 | mkdir C:\scm -ErrorAction Ignore
121 | cd C:\scm
122 | git clone https://github.com/sixeyed/dak4.net.git
123 | 'master' | Out-File C:\branch.txt
124 |
125 | Write-Output 'Disable autologon'
126 | New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -PropertyType DWORD -Value "0" -Force
127 |
--------------------------------------------------------------------------------
/docs/Slides.md:
--------------------------------------------------------------------------------
1 |
2 | class: title
3 |
4 | Docker on Windows
Workshop
5 |
6 | ---
7 | background-image: url(assets/mvp_docker_captain.png)
8 |
9 | ## Intros
10 |
11 | - Hello! I am
12 | Stefan ([@stefscherer](https://twitter.com/stefscherer))
13 | - I work for [Docker](https://docker.com)
14 | - I do open source at [github.com/StefanScherer](https://github.com/StefanScherer)
15 | - I blog [stefanscherer.github.io](http://stefanscherer.github.io/)
16 |
17 | ---
18 |
19 | ## Agenda
20 |
21 |
24 |
25 | .small[
26 | - 9:00 - 12:30 hands-on workshop
27 | ]
28 |
29 |
34 |
35 | - Feel free to interrupt for questions at any time
36 |
37 | ---
38 |
39 | ## Agenda
40 |
41 | - Docker Fundamentals
42 |
43 | - Setup Docker Engine on Windows Server 2019
44 |
45 | - Learn about the base OS images
46 |
47 | - Networking
48 |
49 | - Dockerfile best practices
50 |
51 | - Persisting data using volumes
52 |
53 | - Learn basics about Docker Swarm mode
54 |
55 | ---
56 |
57 | # Pre-requirements
58 |
59 | - Computer with network connection and RDP client
60 |
61 | - on Windows, you are probably all set
62 |
63 | - on macOS, get Microsoft Remote Desktop from the App Store
64 |
65 | - on Linux, get [rdesktop](https://wiki.ubuntuusers.de/rdesktop/)
66 |
67 | - Some Docker knowledge
68 |
69 | (but that's OK if you're not a Docker expert!)
70 |
71 | ---
72 |
73 | ## Nice-to-haves
74 |
75 | - [Docker Client](https://docker.com/) if you want to remote control your Docker engine
76 |
(available with Docker Desktop for Windows and Mac)
77 |
78 | - [GitHub](https://github.com/join) account
79 |
(if you want to fork the repo)
80 |
81 | - [Docker Hub](https://hub.docker.com) account
82 |
(it's one way to distribute images on your Docker host)
83 |
84 | ---
85 |
86 | ## Hands-on sections
87 |
88 | - The whole workshop is hands-on
89 |
90 | - We will see Docker EE 18.09.3 in action
91 |
92 | - You are invited to reproduce all the demos
93 |
94 | - All hands-on sections are clearly identified, like the gray rectangle below
95 |
96 | .exercise[
97 |
98 | - This is the stuff you're supposed to do!
99 | - Go to [stefanscherer.github.io/windows-docker-workshop](https://stefanscherer.github.io/windows-docker-workshop/) to view these slides
100 |
101 | - Join the Chocolatey Slack and use `#chocolatey-fest` channel to chat
102 |
103 | ]
104 |
105 | ---
106 | background-image: url(assets/connect_rdp_docker.png)
107 |
108 | ## We will (mostly) interact with RDP only
109 |
110 | - We will work in the RDP session
111 |
112 | ---
113 | background-image: url(assets/powershell.png)
114 |
115 | ## Terminals
116 |
117 | Once in a while, the instructions will say:
118 |
"Open a new terminal."
119 |
120 | There are multiple ways to do this:
121 |
122 | - open start menu, type `powershell` and click on the PowerShell icon
123 |
124 | - press `[Windows] + R`, then enter `powershell` and press `[RETURN]`
125 |
126 | You are welcome to use the method that you feel the most comfortable with.
127 |
128 | ---
129 |
130 | ## Brand new versions!
131 |
132 | - Docker Enterprise Edition 18.09.3
133 | - Docker Compose 1.23.1
134 |
135 | .exercise[
136 | - Log into your Docker host through RDP (user and password is on your card)
137 | **`dog19-XX.westeurope.cloudapp.azure.com`**
138 |
139 | - Open a terminal
140 |
141 | - Check all installed versions:
142 | ```bash
143 | docker version
144 | ```
145 |
146 | ]
147 |
148 | ---
149 |
150 | ## Docker Fundamentals
151 |
152 | - Docker Host
153 |
154 | - Docker Engine
155 |
156 | - Docker Image
157 |
158 | - Docker Container
159 |
160 | - Docker Registry
161 |
162 | - Dockerfile
163 |
164 | ---
165 | background-image: url(assets/what_is_a_container.png)
166 |
167 | ## What is a container?
168 |
169 | - Standarized packaging for
170 | software and dependencies
171 |
172 | - Isolate apps from each other
173 |
174 | - Share the same OS kernel
175 |
176 | - Works for all major Linux
177 | distributions
178 |
179 | - Containers native to
180 | Windows Server 2019
181 |
182 | ---
183 |
184 | class: title
185 |
186 | # Setting up Docker Host
187 |
188 | ---
189 |
190 | ## Install Docker
191 |
192 | - **You can skip this step with the prepared workshop machines.**
193 |
194 | - Install the Containers feature
195 |
196 | - Install and start the Docker service
197 |
198 | .exercise[
199 | - Install Docker and feature with Microsoft's package:
200 | ```powershell
201 | Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
202 | Find-Package -ProviderName DockerMsftProvider -AllVersions
203 | Install-Package -Name docker -ProviderName DockerMsftProvider
204 | Restart-Computer -Force
205 | ```
206 |
207 | ]
208 |
209 | https://store.docker.com/editions/enterprise/docker-ee-server-windows
210 | https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/
211 |
212 | ---
213 |
214 | ## Update your Host
215 |
216 | - Install Windows updates for best container experience
217 |
218 | .exercise[
219 | - Run Server Configuration:
220 | ```powershell
221 | sconfig
222 | ```
223 |
224 | - Choose option >> `6` << to Download and Install Updates
225 |
226 | - Choose option >> `A` << to download all updates
227 |
228 | ]
229 |
230 | ---
231 |
232 | ## Check what you have done
233 |
234 | - Check Docker Installation
235 |
236 | .exercise[
237 | - Get version and basic information:
238 | ```powershell
239 | docker version
240 | docker info
241 | ```
242 |
243 | - Troubleshooting:
244 | ```powershell
245 | iwr https://aka.ms/Debug-ContainerHost.ps1 -UseBasicParsing | iex
246 | ```
247 | ]
248 |
249 | https://github.com/Microsoft/Virtualization-Documentation
250 |
251 | ---
252 |
253 | ## Update Docker Engine
254 |
255 | - If there is a new version of Docker Engine available
256 |
257 | .exercise[
258 | - Update to latest Docker Engine EE version:
259 | ```powershell
260 | Install-Package -Name docker -ProviderName DockerMsftProvider -Update -Force
261 | Start-Service docker
262 | docker version
263 | ```
264 | ]
265 |
266 | ---
267 |
268 | ## Add tab completion to PowerShell
269 |
270 | - There is a PowerShell module [`DockerCompletion`](https://github.com/matt9ucci/DockerCompletion) to add tab completion for docker commands.
271 |
272 | .exercise[
273 |
274 | - Install the `DockerCompletion` module and edit your `$PROFILE`
275 | ```powershell
276 | Install-Module DockerCompletion -Scope CurrentUser
277 | notepad $PROFILE
278 | ```
279 | - Add the module to the `$PROFILE` and save the file
280 | ```powershell
281 | Import-Module DockerCompletion
282 | ```
283 | - Open a new PowerShell terminal
284 | ]
285 |
286 |
287 | ---
288 |
289 | class: title
290 |
291 | # Docker Images
292 |
293 | ---
294 |
295 | background-image: url(assets/base_images_2019.png)
296 | # Windows Server 2019 base OS images
297 |
298 | ## FROM mcr.microsoft.com/windows:1809
299 | * full Win32 compatible
300 | * 3,5 GByte
301 |
302 | ## FROM mcr.microsoft.com/windows/servercore:ltsc2019
303 | * nearly full Win32 compatible
304 | * 1,5 GByte
305 |
306 | ## FROM mcr.microsoft.com/windows/nanoserver:1809
307 | * 94 MByte
308 | * No 32bit, no MSI, no PowerShell
309 |
310 | ---
311 |
312 | ## Base OS images
313 |
314 | - Provided by Microsoft through the Docker Hub
315 |
316 | - All Windows Docker images are based on one of these two OS images
317 |
318 | .exercise[
319 | - Pull or update to latest Windows base OS images:
320 | ```powershell
321 | docker image ls
322 | docker image pull mcr.microsoft.com/windows/nanoserver:1809
323 | docker image pull mcr.microsoft.com/windows/servercore:ltsc2019
324 | ```
325 | ]
326 |
327 | ---
328 |
329 | ## Working with images
330 |
331 | .exercise[
332 | - Inspect an image:
333 | ```powershell
334 | docker image inspect mcr.microsoft.com/windows/servercore:ltsc2019
335 | ```
336 |
337 | - Tag an image:
338 | ```powershell
339 | docker image tag mcr.microsoft.com/windows/servercore:ltsc2019 myimage
340 | docker image tag mcr.microsoft.com/windows/servercore:ltsc2019 myimage:1.0
341 | docker image ls
342 | ```
343 | ]
344 |
345 | ---
346 |
347 | class: title
348 |
349 | # Containers
350 |
351 | ---
352 |
353 | # Docker Image vs. Container
354 |
355 | ## Image
356 |
357 | - Static snapshot of the filesystem and registry
358 |
359 | ## Container
360 |
361 | - Runtime environment for processes based on an image
362 |
363 | .exercise[
364 | ```powershell
365 | docker image --help
366 | docker container --help
367 | ```
368 | ]
369 |
370 | ---
371 |
372 | ## Run your first container
373 |
374 | - Each container has its own environment
375 | - Host name
376 | - IP address
377 | - Environment variables
378 | - Current directory
379 |
380 | .exercise[
381 |
382 | ```powershell
383 | docker container run mcr.microsoft.com/windows/nanoserver:1809 hostname
384 | docker container run mcr.microsoft.com/windows/nanoserver:1809 ipconfig
385 | docker container run mcr.microsoft.com/windows/nanoserver:1809 cmd /c set
386 | docker container run mcr.microsoft.com/windows/nanoserver:1809 cmd /c cd
387 | ```
388 | ]
389 |
390 | ---
391 |
392 | ## How many containers have you run?
393 |
394 | --
395 | - Answer: 4 (at least)
396 |
397 | ---
398 |
399 | ## Listing containers
400 |
401 | - Each container has a container ID
402 | - You can give them a name
403 | - You can see if a container is running
404 | - You can see the exit code of a container
405 |
406 | .exercise[
407 |
408 | - List running containers
409 |
410 | ```powershell
411 | docker container ls
412 | ```
413 |
414 | - List also exited containers
415 |
416 | ```powershell
417 | docker container ls -a
418 | ```
419 | ]
420 |
421 | ---
422 |
423 | ## View the logs of containers
424 |
425 | - You can see the logs, even after container has exited
426 |
427 | .exercise[
428 |
429 | - Get container ID of last container
430 |
431 | ```powershell
432 | docker container ls -lq
433 | ```
434 |
435 | - Show output of last container
436 |
437 | ```powershell
438 | docker container logs $(docker container ls -lq)
439 | ```
440 |
441 | ]
442 |
443 | ---
444 |
445 | ## Modifying files in containers
446 |
447 | - You can see what has changed in the filesystem
448 |
449 | .exercise[
450 |
451 | - Run a container that creates a file `test1.txt`
452 |
453 | ```powershell
454 | docker container run mcr.microsoft.com/windows/servercore:ltsc2019 powershell `
455 | -command Out-File test1.txt
456 | ```
457 |
458 | - Show the differences between the container and the image
459 |
460 | ```powershell
461 | docker container diff $(docker container ls -lq)
462 | ```
463 |
464 | ]
465 |
466 | ---
467 |
468 | ## Analyzing the diff
469 |
470 | - What are all the other file differences?
471 | --
472 |
473 | - Windows processes write into files and registry
474 | - Other Windows services are running
475 |
476 | - Have you created the file `test1.txt` on your Docker Host?
477 | --
478 |
479 | - No, only inside that single container
480 |
481 | .exercise[
482 |
483 | - List current dir and `C:\` on your Docker Host
484 |
485 | ```powershell
486 | dir
487 | dir C:\
488 | ```
489 |
490 | ]
491 |
492 | ---
493 |
494 | ## Longer running processes
495 |
496 | .exercise[
497 |
498 | - Run a container with a longer running process
499 |
500 | ```powershell
501 | docker container run mcr.microsoft.com/windows/nanoserver:1809 ping -n 30 google.de
502 | ```
503 |
504 | - Try to abort the container with `[CTRL] + C` and list containers
505 |
506 | ```powershell
507 | docker container ls
508 | ```
509 |
510 | - You only aborted the Docker client, not the container
511 |
512 | ]
513 |
514 | ---
515 |
516 | ## Interacting with containers
517 |
518 | - Use `-it` to interact with the process in the container
519 |
520 | .exercise[
521 |
522 | - Run a container with a longer running process
523 |
524 | ```powershell
525 | docker container run -it mcr.microsoft.com/windows/nanoserver:1809 ping -n 30 google.de
526 | ```
527 |
528 | - Try to abort the container with `[CTRL] + C` and list containers
529 |
530 | ```powershell
531 | docker container ls
532 | ```
533 |
534 | ]
535 |
536 | ---
537 |
538 | ## Run an interactive container
539 |
540 | - You also can work interactively inside a container
541 |
542 | .exercise[
543 |
544 | - Run a shell inside a container
545 |
546 | ```powershell
547 | docker container run -it mcr.microsoft.com/windows/servercore:ltsc2019 powershell
548 | ls
549 | cd Users
550 | exit
551 | ```
552 |
553 | ]
554 |
555 | ---
556 |
557 | ## Run containers in the background
558 |
559 | - Use `-d` to run longer running services in the background
560 |
561 | .exercise[
562 |
563 | - Run a detached "ping service" container
564 |
565 | ```powershell
566 | docker container run -d mcr.microsoft.com/windows/servercore:ltsc2019 powershell ping -n 300 google.de
567 | ```
568 |
569 | - Now list, log or kill the container
570 |
571 | ```powershell
572 | docker container ls
573 | docker container logs $(docker container ls -lq)
574 | docker container kill $(docker container ls -lq)
575 | ```
576 |
577 | ]
578 |
579 | ---
580 |
581 | ## Cleaning up your containers
582 |
583 |
584 | .exercise[
585 |
586 | - You can automatically remove containers after exit
587 |
588 | ```powershell
589 | docker container run --rm mcr.microsoft.com/windows/nanoserver:1809 ping google.de
590 | ```
591 |
592 | - You can remove containers manually by their names or IDs
593 |
594 | ```powershell
595 | docker container rm $(docker container ls -lq)
596 | ```
597 |
598 | - You can remove all stopped containers
599 |
600 | ```powershell
601 | docker container prune
602 | ```
603 |
604 | ]
605 |
606 | ---
607 |
608 | class: title
609 |
610 | # Docker Registry
611 |
612 | ---
613 |
614 | ## Re-use the work of others
615 |
616 | - The Docker Hub is a public registry for Docker images
617 |
618 | .exercise[
619 |
620 | - Pull the IIS image from the MCR - Microsoft Container Registry
621 |
622 | ```powershell
623 | docker image pull mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
624 | ```
625 |
626 | - Go to https://hub.docker.com and search for images, look for microsoft/iis
627 |
628 | ]
629 |
630 | ---
631 |
632 | ## Run images from Docker Hub
633 |
634 | - Docker Hub is a place for Linux, Intel, ARM, Windows, ...
635 |
636 | .exercise[
637 |
638 | - Try to run this Linux image
639 |
640 | ```powershell
641 | docker container run -it ubuntu
642 | ```
643 |
644 | ]
645 |
646 | --
647 |
648 | - Only Windows containers can be run on Windows Docker Hosts
649 | - Only Linux containers can be run on Linux Docker Hosts
650 | - But there will be LCOW to run Linux containers on Windows
651 |
652 | ---
653 |
654 | ## Run IIS web server
655 |
656 | - Microsoft has some Windows application images
657 |
658 | .exercise[
659 |
660 | - Try to run this PowerShell
661 |
662 | ```powershell
663 | docker container run -d --name iis -p 80:80 `
664 | mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
665 | ```
666 |
667 | - Now **on your local computer**, open a browser, IIS is reachable from the internet
668 |
669 | - http://dog19-XX.westeurope.cloudapp.azure.com
670 |
671 | ```powershell
672 | start http://$env:FQDN
673 | ```
674 | ]
675 |
676 | ---
677 |
678 | ## Windows Containers now does loopback
679 |
680 | - With Windows Server 2019 we can use `localhost` to access published ports.
681 |
682 | .exercise[
683 |
684 | - Open the web site **from the Docker Host**
685 |
686 | ```powershell
687 | start http://localhost
688 | ```
689 |
690 | ]
691 |
692 | Feature parity with Linux. The previous Windows Server 2016 couldn't do that.
693 |
694 | ---
695 |
696 | ## Kill the container again
697 |
698 | - Do some housekeeping and kill the container again
699 |
700 | .exercise[
701 |
702 | - Kill and remove the container
703 |
704 | ```powershell
705 | docker container kill iis
706 | docker container rm iis
707 | ```
708 | ]
709 |
710 | ---
711 |
712 | class: title
713 |
714 | # Dockerfile
715 |
716 | ---
717 |
718 | ## Describe how to build Docker images
719 |
720 | - A `Dockerfile` is a text file with the description how to build a specific Docker image.
721 |
722 | - Put into source code version control.
723 |
724 | - Make the result repeatable by others or your CI pipeline.
725 |
726 | ---
727 |
728 | ## The first own static website
729 |
730 | Let's create a own static website and serve it with IIS.
731 |
732 | .exercise[
733 |
734 | - Create a folder `website` on the Desktop.
735 |
736 | - Open an editor create a file `iisstart.htm` in that folder.
737 |
738 | ```
739 | Hello from Windows container
740 | ```
741 |
742 | ]
743 |
744 | ---
745 |
746 | ## Build your first Dockerfile
747 |
748 | - Now write a `Dockerfile` for the our website image
749 |
750 | .exercise[
751 |
752 | - Open an editor and create a `Dockerfile`
753 |
754 | ```Dockerfile
755 | FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
756 | COPY iisstart.htm C:\inetpub\wwwroot
757 | ```
758 |
759 | - Now build a new Docker image
760 |
761 | ```powershell
762 | docker image build -t mywebsite .
763 | ```
764 |
765 | - Oops, something went wrong...
766 |
767 | ]
768 |
769 | ---
770 |
771 | ## Escape the backslash problem
772 |
773 | - In a `Dockerfile` you can use a `\` backslash for line continuation.
774 | - To produce a real backslash we have to use two `\\` backslashes.
775 | - A better way is to switch to the PowerShell escape sign backtick.
776 | - This is done with a comment in the first line.
777 |
778 | .exercise[
779 |
780 | - Open an editor and edit the `Dockerfile`
781 |
782 | ```Dockerfile
783 | # escape=`
784 | FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
785 | COPY iisstart.htm C:\inetpub\wwwroot
786 | ```
787 |
788 | ]
789 |
790 | https://docs.docker.com/engine/reference/builder/#escape
791 |
792 | ---
793 |
794 | ## Check what you have built
795 |
796 | - Run a new IIS container with the new image
797 |
798 | .exercise[
799 |
800 | - Run your better website
801 |
802 | ```powershell
803 | docker container run -d -p 80:80 --name web mywebsite
804 | ```
805 |
806 | - Check the web site in your browser http://localhost
807 |
808 | ]
809 |
810 | ---
811 |
812 | class: title
813 |
814 | # Add a management UI
815 |
816 | ---
817 |
818 | # Portainer
819 |
820 | - Portainer is an open source Docker management UI - https://portainer.io
821 |
822 | - It uses the Docker API to visualize and manage containers and lot more.
823 |
824 | - In Linux the Docker API is reachable with a unix socket `/var/run/docker.sock`
825 |
826 | ## Named Pipes
827 |
828 | - In Windows the Docker API is reachable with a named pipe `//./pipe/docker_engine`
829 |
830 | - Windows Server 2019 can bind mount named pipes into Windows containers.
831 |
832 | - Feature parity with Linux. In Windows Server 2016 this wasn't possible.
833 |
834 | ---
835 |
836 | ## Run Portainer
837 |
838 | .exercise[
839 |
840 |
841 | - Run Portainer in a Windows container
842 |
843 | - Map host port 9000 to the container port 9000
844 |
845 | - Map the named pipe into the container
846 |
847 | ```powershell
848 | docker run -d -p 9000:9000 --name portainer --restart always `
849 | -v //./pipe/docker_engine://./pipe/docker_engine `
850 | portainer/portainer:1.20.1
851 | ```
852 |
853 | - Open the browser at http://localhost:9000
854 |
855 | - Create a password, and connect to the local endpoint.
856 | ]
857 |
858 | ---
859 |
860 | class: title
861 |
862 | # Networking
863 |
864 | ---
865 |
866 | ## Listing networks
867 |
868 | - Default network is `nat`, there is also a `none` network.
869 |
870 | .exercise[
871 |
872 | - List all networks on the host
873 |
874 | ```powershell
875 | ipconfig
876 | ```
877 |
878 | - The `vEthernet (nat)` ethernet adapter is used by Docker containers
879 |
880 | - List all container networks
881 |
882 | ```powershell
883 | docker network ls
884 | ```
885 |
886 | ]
887 |
888 | ---
889 |
890 | ## Networking modes
891 |
892 | .exercise[
893 |
894 | - Run a container with network
895 |
896 | ```powershell
897 | docker container run mcr.microsoft.com/windows/nanoserver:1809 ipconfig
898 | ```
899 |
900 | - Run a container without a network
901 |
902 | ```powershell
903 | docker container run --network none mcr.microsoft.com/windows/nanoserver:1809 ipconfig
904 | ```
905 |
906 | ]
907 |
908 | ---
909 |
910 | ## DNS in Container network
911 |
912 | - Container can lookup each other with DNS
913 |
914 | .exercise[
915 |
916 | - Run IIS again, as well as an interactive container
917 |
918 | ```powershell
919 | docker container run -d --name web -p 80:80 -d mywebsite
920 | docker container run -it mcr.microsoft.com/windows/servercore:ltsc2019 powershell
921 | ```
922 |
923 | - Now inside the container, try to access the IIS web server by its DNS name
924 |
925 |
926 | ```powershell
927 | Invoke-WebRequest http://web -UseBasicParsing
928 | ```
929 |
930 | ]
931 |
932 | - Don't forget to kill and remove the website container again.
933 |
934 | ---
935 | background-image: url(assets/compose.png)
936 |
937 | ## Using Docker Compose
938 |
939 | - A tool from Docker
940 | - Define and run multi-container applications
941 |
942 | ---
943 |
944 | ## Installing Docker Compose
945 |
946 | - Docker Desktop for Mac and Windows already have Docker Compose installed
947 | - Installation on Windows Server 2019
948 |
949 | .exercise[
950 |
951 | - If you have [Chocolatey](https://chocolatey.org/) package manager
952 |
953 | ```powershell
954 | choco install docker-compose
955 | ```
956 |
957 | - Otherwise download binary manually from https://github.com/docker/compose/releases
958 |
959 | ]
960 |
961 | ---
962 |
963 | ## The Compose file
964 |
965 | - Docker Compose uses a `docker-compose.yml` file to define multiple services
966 |
967 | - Define services in a Compose file
968 | ```
969 | version: '2.1'
970 | services:
971 | web:
972 | image: mywebsite
973 | ports:
974 | - 80:80
975 | ```
976 |
977 | - Always append this to use the default nat network
978 | ```
979 | networks:
980 | default:
981 | external:
982 | name: nat
983 | ```
984 |
985 | ---
986 |
987 | ## Building images with Compose
988 |
989 | - Docker Compose can use a `Dockerfile` per service to build an image locally
990 |
991 | - Use `build:` instead of `image:`
992 | ```
993 | version: '2.1'
994 | services:
995 | web:
996 | build: .
997 | ports:
998 | - 80:80
999 | ```
1000 |
1001 | ---
1002 |
1003 | ## Networking with Compose
1004 |
1005 | - The service names can be used to lookup them with DNS
1006 | ```
1007 | services:
1008 | web:
1009 | image: mywebsite
1010 | ports:
1011 | - 80:80
1012 |
1013 | client:
1014 | image: mcr.microsoft.com/windows/nanoserver:1809
1015 | command: curl.exe http://web
1016 | ```
1017 |
1018 | ---
1019 |
1020 | ## Practice DNS lookups with Compose
1021 |
1022 | - We replay the manual test of IIS and a client container with Compose.
1023 |
1024 | .exercise[
1025 |
1026 | - Create a new folder
1027 | ```powershell
1028 | mkdir dnstest
1029 | cd dnstest
1030 | ```
1031 |
1032 | - Create a `docker-compose.yml` file to test DNS lookups
1033 |
1034 | ```powershell
1035 | notepad docker-compose.yml
1036 | ```
1037 |
1038 | ]
1039 |
1040 | ---
1041 |
1042 | .exercise[
1043 |
1044 | - Create the `docker-compose.yml` with these two services
1045 |
1046 | ```powershell
1047 | version: '2.1'
1048 | services:
1049 | web:
1050 | image: mywebsite
1051 | ports:
1052 | - 80:80
1053 |
1054 | client:
1055 | image: mcr.microsoft.com/windows/nanoserver:1809
1056 | command: curl.exe http://web
1057 | depends_on:
1058 | - web
1059 |
1060 | networks:
1061 | default:
1062 | external:
1063 | name: nat
1064 | ```
1065 |
1066 | ]
1067 |
1068 | ---
1069 |
1070 | ## Run the first containers with Compose
1071 |
1072 | - Compose can run all containers defined with one command
1073 |
1074 | .exercise[
1075 |
1076 | - Check the usage of `docker-compose`
1077 | ```powershell
1078 | docker-compose --help
1079 | ```
1080 |
1081 | - Run all containers
1082 | ```powershell
1083 | docker-compose up
1084 | ```
1085 |
1086 | - Press `[CTRL] + C` to stop all containers
1087 | - If client could not invoke the web request, try it again.
1088 | ]
1089 |
1090 | ---
1091 |
1092 | ## Run containers in the background
1093 |
1094 | - Compose can run containers in detached mode in background
1095 |
1096 | .exercise[
1097 |
1098 | - Run all containers in the background
1099 | ```powershell
1100 | docker-compose up -d
1101 | ```
1102 |
1103 | - Check which containers are running
1104 | ```powershell
1105 | docker-compose ps
1106 | ```
1107 |
1108 | - Check the output of the client in its logs
1109 | ```powershell
1110 | docker-compose logs -t client
1111 | ```
1112 |
1113 | ]
1114 |
1115 | ---
1116 |
1117 | ## Networking resources
1118 |
1119 | - [Container Networking](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-networking)
1120 |
1121 | - [Use Docker Compose and Service Discovery on Windows](https://blogs.technet.microsoft.com/virtualization/2016/10/18/use-docker-compose-and-service-discovery-on-windows-to-scale-out-your-multi-service-container-application/)
1122 |
1123 | - [Overlay Network Driver with Support for Docker Swarm Mode on Windows 10](https://blogs.technet.microsoft.com/virtualization/2017/02/09/overlay-network-driver-with-support-for-docker-swarm-mode-now-available-to-windows-insiders-on-windows-10/)
1124 | ---
1125 |
1126 | class: title
1127 |
1128 | # Dockerfile best practices
1129 |
1130 | ---
1131 |
1132 | ## Use single backslash
1133 |
1134 | - Use the `escape` comment
1135 |
1136 | ```Dockerfile
1137 | # escape=`
1138 | FROM chocolateyfest/iis
1139 | COPY iisstart.htm C:\inetpub\wwwroot
1140 | ```
1141 |
1142 | - The `CMD` instruction is JSON formatted. You still need double backslash there.
1143 |
1144 | - Alternative: Use "unix" slashes where ever you can.
1145 | A lot of programs can handle it.
1146 |
1147 | ---
1148 |
1149 | ## Use PowerShell
1150 |
1151 | - The default shell in Windows containers is `cmd.exe`.
1152 |
1153 | - Using PowerShell gives you much more features eg. porting Linux Dockerfiles to Windows
1154 | - Download files from the Internet
1155 | - Extract ZIP files
1156 | - Calculate checksums
1157 |
1158 | - Example
1159 | ```Dockerfile
1160 | # escape=`
1161 | FROM mcr.microsoft.com/windows/servercore:ltsc2019
1162 | RUN powershell -Command Invoke-WebRequest 'http://foo.com/bar.zip' `
1163 | -OutFile 'bar.zip' -UseBasicParsing
1164 | RUN powershell -Command Expand-Archive bar.zip -DestinationPath C:\
1165 | ```
1166 |
1167 | ---
1168 |
1169 | ## Switch to PowerShell
1170 |
1171 | - Use the `SHELL` instruction to set PowerShell as default shell.
1172 | - So you don't have to write `powershell -Command` in each `RUN` instruction.
1173 |
1174 | - Use `$ErrorActionPreference = 'Stop'` to abort on first error.
1175 |
1176 | - Use `$ProgressPreference = 'SilentlyContinue'` to improve download speed.
1177 |
1178 | ```Dockerfile
1179 | FROM mcr.microsoft.com/windows/servercore:ltsc2019
1180 |
1181 | SHELL ["powershell", "-Command", `
1182 | "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
1183 | ```
1184 |
1185 | ---
1186 |
1187 | ## Using PowerShell in Dockerfile
1188 |
1189 | - A full example to use PowerShell by default.
1190 |
1191 | ```Dockerfile
1192 | # escape=`
1193 | FROM mcr.microsoft.com/windows/servercore:ltsc2019
1194 |
1195 | SHELL ["powershell", "-Command", `
1196 | "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
1197 |
1198 | RUN Invoke-WebRequest 'http://foo.com/bar.zip' -OutFile 'bar.zip' -UseBasicParsing
1199 | RUN Expand-Archive bar.zip -DestinationPath C:\
1200 | RUN Remove-Item bar.zip
1201 | ```
1202 |
1203 | --
1204 | - What's wrong with it?
1205 |
1206 | ---
1207 |
1208 | ## Download a temporary file
1209 |
1210 | - Each `RUN` instruction builds one layer of your image.
1211 |
1212 | --
1213 |
1214 | - Removing a file of a previous layer does not shrink your final Docker image.
1215 |
1216 | --
1217 |
1218 | - Combine multiple commands to have an atomic build step for eg.
1219 | - Download - Extract - Remove
1220 |
1221 | ```Dockerfile
1222 | # escape=`
1223 | FROM mcr.microsoft.com/windows/servercore:ltsc2019
1224 |
1225 | SHELL ["powershell", "-Command", `
1226 | "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
1227 |
1228 | RUN iwr 'http://foo.com/bar.zip' -OutFile 'bar.zip' -UseBasicParsing ; `
1229 | Expand-Archive bar.zip -DestinationPath C:\ ; `
1230 | Remove-Item bar.zip
1231 | ```
1232 |
1233 | ---
1234 |
1235 | ## But ...
1236 |
1237 | - Use multiple `RUN` instructions while developing a Docker image
1238 |
1239 | - You can benefit of layer caching.
1240 |
1241 | - Downloading 1GB ZIP and doing the extract command wrong is painful.
1242 |
1243 | ---
1244 |
1245 | ## Better: Use multi-stage builds
1246 |
1247 | - A `Dockerfile` with multiple `FROM` instructions
1248 |
1249 | - Each `FROM` starts a new stage
1250 |
1251 | - Only the final stage will make it into your Docker image.
1252 |
1253 | - Use earlier stages to build your app, eg. with a compiler
1254 |
1255 | - Remove the compiler dependencies with a final `FROM`
1256 |
1257 | https://stefanscherer.github.io/use-multi-stage-builds-for-smaller-windows-images/
1258 |
1259 | ---
1260 |
1261 | ## Resources for Dockerfile on Windows
1262 |
1263 | - [Best practises for writing Dockerfiles](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run)
1264 |
1265 | - [Dockerfile on Windows](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-docker/manage-windows-dockerfile)
1266 |
1267 | - [Optimize Windows Dockerfiles](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-docker/optimize-windows-dockerfile)
1268 |
1269 | ---
1270 |
1271 | class: title
1272 |
1273 | # Dockerize .NET applications
1274 |
1275 | ---
1276 |
1277 | # .NET images
1278 |
1279 | - Microsoft has several .NET Core and ASP .NET images. See the overview on the Docker Hub https://hub.docker.com/_/microsoft-dotnet-core
1280 |
1281 | - Multi-stage builds are used to reduce the image size for the final application image.
1282 |
1283 | ## Tutorial
1284 | - We will walk through this small tutorial
1285 | - https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/aspnet-docker-dev-in-container.md
1286 |
1287 | ---
1288 |
1289 | # Clone the dotnet-docker sample repo
1290 |
1291 | .exercise[
1292 |
1293 | - Clone the repo in the toplevel directory
1294 | ```
1295 | cd C:\
1296 | git clone https://github.com/dotnet/dotnet-docker
1297 | ```
1298 |
1299 | - Go into the ASP .NET sample folder
1300 | ```
1301 | cd C:\dotnet-docker\samples\aspnetapp
1302 | ```
1303 | ]
1304 |
1305 | ---
1306 |
1307 | # Run the ASP .NET application from source
1308 |
1309 | .exercise[
1310 |
1311 | - Use a volume to run the app in a container with the sources from the host.
1312 | ```
1313 | docker run --rm -it -p 8000:80 `
1314 | -v "$(pwd):c:\app\" -w \app\aspnetapp `
1315 | --name aspnetappsample mcr.microsoft.com/dotnet/core/sdk:2.2 `
1316 | dotnet watch run
1317 | ```
1318 |
1319 | - Open the browser http://localhost:8000
1320 |
1321 | - With the full SDK image we can easily run the application from source code.
1322 | ]
1323 | ---
1324 | # Build a Docker images of the ASP .NET application
1325 |
1326 | - Multi-stage build Dockerfiles have multiple `FROM` instructions.
1327 |
1328 | - First stage with SDK
1329 | ```
1330 | FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
1331 | WORKDIR /app
1332 |
1333 | # copy csproj and restore as distinct layers
1334 | COPY *.sln .
1335 | COPY aspnetapp/*.csproj ./aspnetapp/
1336 | RUN dotnet restore
1337 |
1338 | # copy everything else and build app
1339 | COPY aspnetapp/. ./aspnetapp/
1340 | WORKDIR /app/aspnetapp
1341 | RUN dotnet publish -c Release -o out
1342 | ```
1343 |
1344 | ---
1345 |
1346 | # Build a Docker image of the ASP .NET application
1347 |
1348 | - Final stage with only the runtime
1349 | ```
1350 | FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
1351 | WORKDIR /app
1352 | COPY --from=build /app/aspnetapp/out ./
1353 | ENTRYPOINT ["dotnet", "aspnetapp.dll"]
1354 | ```
1355 |
1356 | - Notice: This is still part of the same `Dockerfile` as in the previous slide.
1357 |
1358 | - The `COPY` instruction copies the output of the `build` stage into the `runtime` stage.
1359 |
1360 | ---
1361 |
1362 | # Build a Docker image of the ASP .NET application
1363 |
1364 | .exercise[
1365 |
1366 | - Now build the image
1367 | ```
1368 | docker build -t aspnetapp .
1369 | ```
1370 |
1371 | - Check the Docker images sizes
1372 | ```
1373 | docker image ls
1374 | ```
1375 | ]
1376 |
1377 | ---
1378 |
1379 | class: title
1380 |
1381 | # Manifest lists
1382 |
1383 | ---
1384 |
1385 | # Windows OS version must match base image version
1386 |
1387 | - For `process` isolation on Windows only images that have the same kernel version as the Windows host OS can be started.
1388 |
1389 | - But Windows has another isolation mode: `hyperv`
1390 |
1391 | - With `hyperv` isolation also older images than the kernel version can be started.
1392 |
1393 | - Let's check some images on the Docker Hub ...
1394 |
1395 | ---
1396 |
1397 | # Manifest query tool
1398 |
1399 | - There is a multi-arch image `mplatform/mquery` from Docker Captain Phil Estes
1400 | - It lists all supported platforms of an image.
1401 | - Several images for different platforms can be combined to a manifest list.
1402 |
1403 | .exercise[
1404 |
1405 | - Check which platforms are supported for the `golang` image.
1406 |
1407 | ```
1408 | docker run mplatform/mquery golang
1409 | ```
1410 |
1411 | - Oh it doesn't work. Run it in Hyper-V isolation mode
1412 |
1413 | ```
1414 | docker run --isolation=hyperv mplatform/mquery golang
1415 | ```
1416 |
1417 | - Check which platforms are supported by the `mplatform/mquery` tool itself.
1418 | ]
1419 |
1420 | ---
1421 |
1422 | # Windows OS versions
1423 |
1424 | - 10.0.**14393** = Windows Server 2016 LTSC
1425 |
1426 | - 10.0.**16299** = Windows Server 2016, version 1709 SAC
1427 |
1428 | - 10.0.**17134** = Windows Server 2016, version 1803 SAC
1429 |
1430 | - 10.0.**17763** = Windows Server 2019 LTSC
1431 |
1432 | - So the `mplatform/mquery` tool only has the 2016 base image in the manifest list. That's why we can run it with `--isolation=hyperv` mode.
1433 |
1434 | ---
1435 |
1436 | class: title
1437 |
1438 | # Persisting data using volumes
1439 |
1440 | ---
1441 |
1442 | ## Using volumes
1443 |
1444 | - A container is not able to persist data for you.
1445 |
1446 | - If you kill a container and run a new container it starts in a fresh environment.
1447 |
1448 | - Volumes can be used to persist data outside of containers.
1449 |
1450 | .exercise[
1451 |
1452 | - Write a small Dockerfile that reads and writes a file at runtime.
1453 | ```Dockerfile
1454 | FROM mcr.microsoft.com/windows/nanoserver:1809
1455 | CMD cmd /c dir content.txt & echo hello >content.txt
1456 | ```
1457 |
1458 | - Build and run the container. Run it again.
1459 | ```powershell
1460 | docker build -t content .
1461 | docker run content
1462 | ```
1463 |
1464 | ]
1465 |
1466 | ---
1467 |
1468 | ## Prepare the image to have a volume mount point
1469 |
1470 | - Now we prepare the Dockerfile with a workdir to add a volume at runtime.
1471 |
1472 | .exercise[
1473 |
1474 | - Add a `WORKDIR` to have an empty folder inside the container.
1475 | ```Dockerfile
1476 | FROM mcr.microsoft.com/windows/nanoserver:1809
1477 | WORKDIR /data
1478 | USER ContainerAdministrator
1479 | CMD cmd /c dir content.txt & echo hello >content.txt
1480 | ```
1481 |
1482 | - Build and run the container. Run it again.
1483 | ```powershell
1484 | docker build -t content .
1485 | docker run content
1486 | ```
1487 |
1488 | ]
1489 |
1490 | ---
1491 |
1492 | ## Adding a volume from host
1493 |
1494 | - The file `content.txt` is still not persisted.
1495 |
1496 | - Now add a volume from the host with the `-v` option.
1497 |
1498 | .exercise[
1499 |
1500 | - Run the container with a volume mount point.
1501 | ```powershell
1502 | docker run -v "$(pwd):C:\data" content
1503 | ```
1504 |
1505 | - It shows the same output, but look at the host directory. Run another container.
1506 | ```powershell
1507 | dir
1508 | docker run -v "$(pwd):C:\data" content
1509 | ```
1510 |
1511 | ]
1512 |
1513 | ---
1514 |
1515 | ## Windows volumes in practice
1516 |
1517 | ## Empty directory
1518 |
1519 | - You can mount a volume only into an empty directory.
1520 |
1521 | - Windows Server 2019 also allows mapping into non-empty directories.
1522 |
1523 | - Feature parity with Linux. This wasn't possible with Windows Server 2016.
1524 |
1525 | ---
1526 |
1527 | ## Orchestrators
1528 |
1529 | - Docker Swarm, Docker EE / UCP
1530 |
1531 | Work in progress:
1532 |
1533 | - Kubernetes, currently in beta, just landed in 1.14
1534 |
1535 | - Docker EE / UCP with Kubernetes and Windows in a next release
1536 |
1537 | ...
1538 |
1539 | ---
1540 |
1541 | class: title
1542 |
1543 | # Docker Swarm mode
1544 |
1545 | ---
1546 |
1547 | ## Docker Swarm
1548 |
1549 | - Use the current RDP session as manager node
1550 |
1551 | - Create a Docker Swarm cluster
1552 |
1553 | .exercise[
1554 |
1555 | - Lookup the IP address of the manager node
1556 | ```powershell
1557 | ipconfig | sls IPv4
1558 | ```
1559 |
1560 | - Initialize a Docker Swarm single node
1561 | ```powershell
1562 | docker swarm init --advertise-addr 10.0.xx.xx
1563 | ```
1564 |
1565 | - Notice: There is a short interruption, the RDP should automatically reconnect.
1566 |
1567 | ]
1568 |
1569 | ---
1570 |
1571 | ## Swarm mode firewall exceptions
1572 |
1573 | .exercise[
1574 |
1575 | - Open the ports for Docker Swarm mode on the manager node and the worker node(s)
1576 | ```
1577 | New-NetFirewallRule -Protocol TCP -LocalPort 2377 -Direction Inbound `
1578 | -Action Allow -DisplayName "Docker swarm-mode cluster management TCP"
1579 |
1580 | New-NetFirewallRule -Protocol TCP -LocalPort 7946 -Direction Inbound `
1581 | -Action Allow -DisplayName "Docker swarm-mode node communication TCP"
1582 |
1583 | New-NetFirewallRule -Protocol UDP -LocalPort 7946 -Direction Inbound `
1584 | -Action Allow -DisplayName "Docker swarm-mode node communication TCP"
1585 |
1586 | New-NetFirewallRule -Protocol UDP -LocalPort 4789 -Direction Inbound `
1587 | -Action Allow -DisplayName "Docker swarm-mode overlay network UDP"
1588 | ```
1589 |
1590 | ]
1591 |
1592 | ---
1593 |
1594 | ## Docker Swarm
1595 |
1596 | - Add your second machine as worker node
1597 |
1598 | .exercise[
1599 |
1600 | - Copy the docker swarm join command and paste it into second node
1601 | ```powershell
1602 | docker swarm join ...
1603 | ```
1604 |
1605 | ]
1606 |
1607 | ---
1608 |
1609 | ## Docker Swarm
1610 |
1611 | - Go back to the manager node
1612 |
1613 | .exercise[
1614 |
1615 | - List your available nodes in the Docker Swarm cluster
1616 | ```powershell
1617 | docker node ls
1618 | ```
1619 |
1620 | ]
1621 |
1622 | ---
1623 |
1624 | ## Appetizer app: The Stack file
1625 |
1626 | - Docker Stack uses a `docker-compose.yml` file to define multiple services
1627 |
1628 | - Define services in a Compose file
1629 | ```
1630 | version: "3.2"
1631 | services:
1632 | chocolate:
1633 | image: chocolateyfest/appetizer:1.0.0
1634 | ports:
1635 | - 8080:8080
1636 | deploy:
1637 | placement:
1638 | constraints:
1639 | - node.platform.os == windows
1640 | ```
1641 |
1642 | ---
1643 |
1644 | ## Deploy the stack
1645 |
1646 | - Go back to the manager node
1647 |
1648 | .exercise[
1649 |
1650 | - Now deploy the Stack to you Docker Swarm cluster
1651 | ```powershell
1652 | docker stack deploy -c docker-compose.yml appetizer
1653 | ```
1654 |
1655 | ]
1656 |
1657 | ---
1658 |
1659 | ## Open the browser
1660 |
1661 | - Swarm services cannot be access by localhost.
1662 |
1663 | - Use the external name of the Docker Swarm node to access published ports
1664 |
1665 | .exercise[
1666 |
1667 | - Open a browser
1668 | ```powershell
1669 | start http://$($env:FQDN):8080
1670 | ```
1671 |
1672 | - Reload the page in the browser
1673 |
1674 | ]
1675 |
1676 | ---
1677 |
1678 | ## Scale it up
1679 |
1680 | - Scale the service to have more than one replica
1681 |
1682 | .exercise[
1683 |
1684 | - List your deployed services in the Swarm cluster
1685 | ```powershell
1686 | docker service ls
1687 | ```
1688 |
1689 | - Scale the service up
1690 | ```powershell
1691 | docker service scale xxx_appetizer=8
1692 | ```
1693 |
1694 | - Reload the page in the browser
1695 |
1696 | ]
1697 |
1698 | ---
1699 |
1700 | ## Workhop done. Now where to build Windows containers?
1701 |
1702 | - Windows 10
1703 | - Docker Desktop (using Hyper-V)
1704 |
1705 | - Azure
1706 | - Windows Server 2016, 1709, 1803, **2019**
1707 | - Azure Pipeline
1708 |
1709 | - AppVeyor CI
1710 | - [github.com/StefanScherer/dockerfiles-windows](https://github.com/StefanScherer/dockerfiles-windows/pull/344) - collection
1711 | - [github.com/StefanScherer/whoami](https://github.com/StefanScherer/whoami) - Multi-arch images
1712 |
1713 | - In a local VM on Linux/Mac/Windows
1714 | - [github.com/StefanScherer/windows-docker-machine](https://github.com/StefanScherer/windows-docker-machine)
1715 |
1716 | ---
1717 |
1718 | ## Further Windows related resources
1719 |
1720 | - Follow Elton Stoneman on Twitter [@EltonStoneman](https://twitter.com/EltonStoneman)
1721 |
1722 | - Elton Stoneman's Docker on Windows workshop
1723 | - [https://dwwx.space/](https://dwwx.space/)
1724 | - will be updated to Windows Server 2019 soon
1725 |
1726 | - Windows related Docker Labs
1727 | - [https://github.com/docker/labs/tree/master/windows](https://github.com/docker/labs/tree/master/windows)
1728 |
1729 | ---
1730 | background-image: url(assets/book.png)
1731 |
1732 | ## Docker on Windows book, **Second edition**
1733 |
1734 | - Buy Elton Stoneman's book.
1735 | - It's extraordinary.
1736 | - Fully updated to Windows Server 2019
1737 | - Elasticsearch, Kibana
1738 | - Traefik
1739 | - Prometheus
1740 | - Jenkins
1741 | - Git server
1742 | - ...
1743 |
1744 | Docker on Windows: From 101 to production
1745 | with Docker on Windows, 2nd Edition
1746 |
1747 | ---
1748 |
1749 | class: title
1750 |
1751 | # Thanks!
1752 | Questions?
1753 |
1754 | ## [@stefscherer](https://twitter.com/stefscherer)
1755 |
1756 | ## [github.com/StefanScherer](https://github.com/StefanScherer)
1757 |
--------------------------------------------------------------------------------