Řešení problému s Docker orphan layers na Windows Server 2019
Úvod
Při práci s Dockerem na Windows Server 2019 se můžete setkat s problémem, kdy nevyužívané vrstvy kontejnerů zůstávají na disku a způsobují jeho plnění. Tento článek poskytuje řešení tohoto problému pomocí PowerShell skriptů.
Identifikace Problému
Problém se obvykle projevuje rychlým snižováním volného místa na disku. To je způsobeno tím, že Docker neodstraňuje vrstvy, které již nejsou využívány žádnými kontejnery nebo obrazy.
Úprava daemon.json
Doporučuje se upravit C:\ProgramData\docker\config\daemon.json
a to konkrétně přidat "shutdown-timeout": 900
, což prodlouží dobu, kterou má Docker na ukončení – a tím i promazání orphan layers.
C:\ProgramData\docker\config\daemon.json
{ "shutdown-timeout": 900 }
Použité Skripty
Pro řešení tohoto problému byly použity dva PowerShell skripty:
1. Skript pro identifikaci a přejmenování orphan layers
První skript prochází existující Docker image a kontejnery a shromažďuje informace o jejich vrstvách. Poté porovnává tyto vrstvy s vrstvami uloženými na disku a identifikuje ty, které nejsou spojeny s žádným známým imagem nebo kontejnerem. Tyto osiřelé vrstvy mohou být poté přejmenovány pro snadnější odstranění.
param ( [switch]$RenameOrphanLayers ) If ($RenameOrphanLayers) { Write-Warning "$($env:COMPUTERNAME) -RenameOrphanLayers option enabled, will rename all orphan layers" } # Get known layers on Docker images [array]$ImageDetails += docker images -q | ForEach { docker inspect $_ | ConvertFrom-Json } ForEach ($Image in $ImageDetails) { $ImageLayer = $Image.GraphDriver.Data.dir [array]$ImageLayers += $ImageLayer $LayerChain = Get-Content "$ImageLayer\layerchain.json" If ($LayerChainFileContent -ne "null") { [array]$ImageParentLayers += $LayerChain | ConvertFrom-Json } } # Get known layes on Docker containers [array]$ContainerDetails = docker ps -a -q | ForEach { docker inspect $_ | ConvertFrom-Json} ForEach ($Container in $ContainerDetails) { [array]$ContainerLayers += $Container.GraphDriver.Data.dir } # Get layers on disk $LayersOnDisk = (Get-ChildItem -Path C:\ProgramData\Docker\windowsfilter -Directory).FullName $ImageLayers += $ImageParentLayers $UniqueImageLayers = $ImageLayers | Select-Object -Unique [array]$KnownLayers = $UniqueImageLayers $KnownLayers += $ContainerLayers # Find orphan layers $OrphanLayersTotal = 0 ForEach ($Layer in $LayersOnDisk) { If ($KnownLayers -notcontains $Layer) { [array]$OrphanLayer += $Layer $LayerSize = (Get-ChildItem -Path $Layer -Recurse -ErrorAction:SilentlyContinue | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum $OrphanLayersTotal += $LayerSize Write-Warning "$($env:COMPUTERNAME) - Found orphan layer: $($Layer -Replace '\r\n','') with size: $(($LayerSize -Replace '\r\n','') / 1MB) MB" If (($RenameOrphanLayers) -and ($Layer -notlike "*-removing")) { $LayerNewPath = $Layer + "-removing" Rename-Item -Path $Layer -NewName $LayerNewPath } } } Write-Host "$($env:COMPUTERNAME) - Layers on disk: $($LayersOnDisk.count)" Write-Host "$($env:COMPUTERNAME) - Image layers: $($UniqueImageLayers.count)" Write-Host "$($env:COMPUTERNAME) - Container layers: $($ContainerLayers.count)" $OrphanLayersTotalMB = $OrphanLayersTotal / 1MB Write-Warning "$($env:COMPUTERNAME) - Found $($OrphanLayer.count) orphan layers with total size $OrphanLayersTotalMB MB"
2. Restartovací skript Dockeru
Druhý skript zastavuje všechny služby a procesy Dockeru a poté je restartuje. Tento krok je nezbytný pro odstranění orphan layers, které byly identifikovány a přejmenovány prvním skriptem.
Write-Output "$((Get-Date).ToString("HH:mm:ss")) - Restarting docker" foreach($svc in (Get-Service | Where-Object {$_.name -ilike "*docker*" -and $_.Status -ieq "Running"})) { $svc | Stop-Service -ErrorAction Continue -Confirm:$false -Force $svc.WaitForStatus('Stopped','00:00:20') } Get-Process | Where-Object {$_.Name -ilike "*docker*"} | Stop-Process -ErrorAction Continue -Confirm:$false -Force foreach($svc in (Get-Service | Where-Object {$_.name -ilike "*docker*" -and $_.Status -ieq "Stopped"} )) { $svc | Start-Service $svc.WaitForStatus('Running','00:00:20') } Write-Output "$((Get-Date).ToString("HH:mm:ss")) - Starting Docker" & "C:\Program Files\Docker\docker.exe" $startTimeout = [DateTime]::Now.AddSeconds(90) $timeoutHit = $true while ((Get-Date) -le $startTimeout) { Start-Sleep -Seconds 10 $ErrorActionPreference = 'Continue' try { $info = (docker info) Write-Verbose "$((Get-Date).ToString("HH:mm:ss")) - `tDocker info executed. Is Error?: $($info -ilike "*error*"). Result was: $info" if ($info -ilike "*error*") { Write-Verbose "$((Get-Date).ToString("HH:mm:ss")) - `tDocker info had an error. throwing..." throw "Error running info command $info" } $timeoutHit = $false break } catch { if (($_ -ilike "*error during connect*") -or ($_ -ilike "*errors pretty printing info*") -or ($_ -ilike "*Error running info command*")) { Write-Output "$((Get-Date).ToString("HH:mm:ss")) -`t Docker startup not yet completed, waiting and checking again" } else { Write-Output "Unexpected Error: `n $_" return } } $ErrorActionPreference = 'Stop' } if ($timeoutHit -eq $true) { throw "Timeout hit waiting for docker to startup" } Write-Output "$((Get-Date).ToString("HH:mm:ss")) - Docker restarted"
Implementace Skriptů
Bezpečnostní Opatření
- Před spuštěním skriptů je důležité provést zálohu systému.
- Doporučuje se nejprve otestovat skripty v kontrolovaném prostředí.
- Po restartu Dockeru monitorujte systém, abyste ověřili jeho správnou funkčnost.
Automatizace a Monitoring
- Skripty mohou být spuštěny manuálně nebo automatizovány pomocí naplánovaných úloh.
- Doporučuje se implementovat monitoring využití diskového prostoru pro pravidelné sledování stavu systému.
Závěr
Použitím těchto PowerShell skriptů můžete efektivně řešit problém s osiřelými vrstvami Dockeru na Windows Server 2019, což vede k uvolnění diskového prostoru a zlepšení výkonu systému.