├── .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 | ![Build pipeline to create 40 VM's in Azure with a CircleCI build using Packer and Terraform](images/pipeline.png) 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 | [![Join the chat at https://gitter.im/windows-docker-workshop/Lobby](https://badges.gitter.im/windows-docker-workshop/Lobby.svg)](https://gitter.im/windows-docker-workshop/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | [![CircleCI](https://circleci.com/gh/StefanScherer/windows-docker-workshop.svg?style=svg)](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 | --------------------------------------------------------------------------------