Private/DockerHelper.ps1
class DockerHealthcheck { [string] $Test [string] $Interval [string] $Timeout [string] $Retries [string] $StartPeriod DockerHealthcheck() { } DockerHealthcheck([string] $Test) { $this.Test = $Test $this.Interval = "5s" $this.Timeout = "2s" $this.Retries = "5" $this.StartPeriod = "1s" } DockerHealthcheck([DockerHealthcheck] $other) { $this.Test = $other.Test $this.Interval = $other.Interval $this.Timeout = $other.Timeout $this.Retries = $other.Retries $this.StartPeriod = $other.StartPeriod } } class DockerLogging { [string] $Driver [Hashtable] $Options DockerLogging() { } DockerLogging([string] $SyslogAddress) { $this.Driver = "syslog" $this.Options = [ordered]@{ 'syslog-format' = 'rfc5424' 'syslog-facility' = 'daemon' 'syslog-address' = $SyslogAddress } } DockerLogging([DockerLogging] $other) { $this.Driver = $other.Driver if ($other.Options) { $this.Options = $other.Options.Clone() } } } class DockerService { [string] $Image [string] $Platform [string] $Isolation [string] $ContainerName [string] $RestartPolicy [bool] $External [string[]] $DependsOn [string[]] $Networks [Hashtable] $Environment [string[]] $Volumes [string] $Command [Int32[]] $TargetPorts [bool] $PublishAll [DockerHealthcheck] $Healthcheck [DockerLogging] $Logging DockerService() { } DockerService([DockerService] $other) { $this.Image = $other.Image $this.Platform = $other.Platform $this.Isolation = $other.Isolation $this.ContainerName = $other.ContainerName $this.RestartPolicy = $other.RestartPolicy $this.External = $other.External if ($other.DependsOn) { $this.DependsOn = $other.DependsOn.Clone() } if ($other.Networks) { $this.Networks = $other.Networks.Clone() } if ($other.Environment) { $this.Environment = $other.Environment.Clone() } if ($other.Volumes) { $this.Volumes = $other.Volumes.Clone() } $this.Command = $other.Command if ($other.TargetPorts) { $this.TargetPorts = $other.TargetPorts.Clone() } $this.PublishAll = $other.PublishAll if ($other.Healthcheck) { $this.Healthcheck = [DockerHealthcheck]::new($other.Healthcheck) } if ($other.Logging) { $this.Logging = [DockerLogging]::new($other.Logging) } } } function Start-DockerService { [CmdletBinding()] param( [DockerService] $Service, [switch] $Remove ) if ($Service.External) { return # service should already be running } if (Get-ContainerExists -Name $Service.ContainerName) { if (Get-ContainerIsRunning -Name $Service.ContainerName) { Stop-Container -Name $Service.ContainerName } if ($Remove) { Remove-Container -Name $Service.ContainerName } } $RunCommand = (Get-DockerRunCommand -Service $Service) -Join " " Write-Host "Starting $($Service.ContainerName)" Write-Verbose $RunCommand $id = Invoke-Expression $RunCommand if ($Service.Healthcheck) { Wait-ContainerHealthy -Name $Service.ContainerName | Out-Null } if (Get-ContainerIsRunning -Name $Service.ContainerName) { Write-Host "$($Service.ContainerName) successfully started" } else { throw "Error starting $($Service.ContainerName)" } } function Get-ContainerExists { param( [string] $Name ) $exists = $(docker ps -aqf "name=$Name") return ![string]::IsNullOrEmpty($exists) } function Get-ContainerIsRunning { param( [string] $Name ) $running = $(docker inspect -f '{{.State.Running}}' $Name) return $running -Match 'true' } function Get-ContainerIsHealthy { param( [string] $Name ) $healthy = $(docker inspect -f '{{.State.Health.Status}}' $Name) return $healthy -Match 'healthy' } function Wait-ContainerHealthy { param( [Parameter(Mandatory=$true)] [string] $Name ) $seconds = 0 $timeout = 15 $interval = 1 while (($seconds -lt $timeout) -And !(Get-ContainerIsHealthy -Name:$Name) -And (Get-ContainerIsRunning -Name:$Name)) { Start-Sleep -Seconds $interval $seconds += $interval } return (Get-ContainerIsHealthy -Name:$Name) } function Stop-Container { param( [Parameter(Mandatory=$true)] [string] $Name, [switch] $Quiet ) $CmdArgs = @('docker', 'stop') $CmdArgs += $Name $cmd = $CmdArgs -Join " " if (-Not $Quiet) { Write-Host $cmd } Invoke-Expression $cmd | Out-Null } function Remove-Container { param( [Parameter(Mandatory=$true)] [string] $Name, [switch] $Quiet, [switch] $Force ) $CmdArgs = @('docker', 'rm') if ($Force) { $CmdArgs += '-f' } $CmdArgs += $Name $cmd = $CmdArgs -Join " " if (-Not $Quiet) { Write-Host $cmd } Invoke-Expression $cmd | Out-Null } function Get-DockerNetworkExists { param( [Parameter(Mandatory=$true)] [string] $Name ) $exists = $(docker network ls -qf "name=$Name") return ![string]::IsNullOrEmpty($exists) } function New-DockerNetwork { param( [Parameter(Mandatory=$true)] [string] $Name, [string] $Platform, [switch] $Force ) if (!(Get-DockerNetworkExists -Name:$Name)) { $cmd = @('network', 'create') if ($Platform -eq 'windows') { $cmd += @('-d', 'nat') } $cmd += $Name # network name $Id = docker $cmd } } function New-DockerVolume { param( [Parameter(Mandatory=$true)] [string] $Name, [switch] $Force ) $output = $(docker volume ls -qf "name=$Name") if ([string]::IsNullOrEmpty($output)) { docker volume create $Name | Out-Null } } function Request-ContainerImage() { param( [Parameter(Mandatory=$true)] [string] $Name, [switch] $Quiet ) $CmdArgs = @('docker', 'pull') if ($Quiet) { $CmdArgs += '-q' } $CmdArgs += $Name $cmd = $CmdArgs -Join " " if (-Not $Quiet) { Write-Host $cmd } Invoke-Expression $cmd | Out-Null } function Get-ContainerImageId() { param( [Parameter(Mandatory=$true)] [string] $Name ) if ($Name.StartsWith("library/")) { $Name = $Name -Replace "library/", "" } $CmdArgs = @('docker', 'images', '-q') $CmdArgs += $Name $cmd = $CmdArgs -Join " " $Id = Invoke-Expression $cmd return $Id } function Test-DockerHost { [CmdletBinding()] param() if (Get-IsWindows) { $DnsServers = Get-DnsClientServerAddress -AddressFamily IPv4 | ` Select-Object -Unique -ExpandProperty ServerAddresses if ($DnsServers -Contains '127.0.0.1') { Write-Warning "A DNS server with address 127.0.0.1 is configured on the host." Write-Warning "This is known to cause DNS resolution issues inside containers." Write-Warning "Please use the host IP address from the host network instead." } $SEP = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | ` ForEach-Object { Get-ItemProperty $_.PSPath } | ` Where-Object { $_ -Match 'Symantec Endpoint Protection' } if ($SEP) { Write-Warning "Symantec Endpoint Protection (SEP) has been detected." Write-Warning "It is known to cause several issues with Docker for Windows." Write-Warning "Removing the 'Application and Device Control' (ADC) component is recommended." Write-Warning "Please refer to the following article for the relevant exclusions:" Write-Warning "https://knowledge.broadcom.com/external/article?legacyId=TECH246815" Write-Warning "You should also add %ProgramData%\docker to the exclusion list:" Write-Warning "https://docs.docker.com/engine/security/antivirus/" Write-Warning "At last, you can refer to the following blog article for further guidance:" Write-Warning "https://mdaslam.wordpress.com/2017/05/23/docker-container-windows-2016-server-with-sep-symantec-endpoint-protection/" } } } function Get-DockerRunCommand { [OutputType('string[]')] param( [DockerService] $Service ) $cmd = @('docker', 'run') $cmd += @('--name', $Service.ContainerName) $cmd += "-d" # detached if ($Service.Platform -eq 'windows') { if ($Service.Isolation -eq 'hyperv') { $cmd += "--isolation=$($Service.Isolation)" } } if ($Service.RestartPolicy) { $cmd += "--restart=$($Service.RestartPolicy)" } if ($Service.Networks) { foreach ($Network in $Service.Networks) { $cmd += "--network=$Network" } } if ($Service.Environment) { $Service.Environment.GetEnumerator() | foreach { $key = $_.Key $val = $_.Value $cmd += @("-e", "`"$key=$val`"") } } if ($Service.Volumes) { foreach ($Volume in $Service.Volumes) { $cmd += @("-v", "`"$Volume`"") } } if ($Service.PublishAll) { foreach ($TargetPort in $Service.TargetPorts) { $cmd += @("-p", "$TargetPort`:$TargetPort") } } if ($Service.Healthcheck) { $Healthcheck = $Service.Healthcheck if (![string]::IsNullOrEmpty($Healthcheck.Interval)) { $cmd += "--health-interval=" + $Healthcheck.Interval } if (![string]::IsNullOrEmpty($Healthcheck.Timeout)) { $cmd += "--health-timeout=" + $Healthcheck.Timeout } if (![string]::IsNullOrEmpty($Healthcheck.Retries)) { $cmd += "--health-retries=" + $Healthcheck.Retries } if (![string]::IsNullOrEmpty($Healthcheck.StartPeriod)) { $cmd += "--health-start-period=" + $Healthcheck.StartPeriod } $cmd += $("--health-cmd=`'" + $Healthcheck.Test + "`'") } if ($Service.Logging) { $Logging = $Service.Logging $cmd += '--log-driver=' + $Logging.Driver $options = @() $Logging.Options.GetEnumerator() | foreach { $key = $_.Key $val = $_.Value $options += "$key=$val" } $options = $options -Join "," $cmd += "--log-opt=" + $options } $cmd += $Service.Image $cmd += $Service.Command return $cmd } |