AUTOR MORITZ STRÜBE

 

Wer mit Linux und Docker unter Windows arbeitet, kommt um das WSL2 (Windows-Subsystem für Linux) nicht herum. Man merkt jedoch, dass es bei der Entwicklung erst einmal darum ging Linux unter Windows überhaupt zum Laufen zu bekommen. Wer mit vielen und größeren Docker-Images arbeitet wird schnell merken, dass der Arbeitsspeicher durch den Datei-Cache schnell gefüllt wird und dann die I/O-Leistung massiv einbricht. Ein zweites Problem ist auch, dass die Festplatte recht schnell voll läuft. Dies geht so weit, dass noch 0 Byte frei sind und verschiedene Anwendungen - inklusive des Docker-Daemons - den Dienst verweigern. Wenn sich die Platte langsam füllt, hilft unter Linux ein docker system prune. Unter Windows hat dies nur wenig Effekt: Trotz gelöschter Docker-Images steht nicht mehr Plattenplatz zur Verfügung.

Im Folgenden werde ich die beiden Probleme "Langsamer Dateizugriff" und "Nicht freigegebener Festplattenspeicher" kurz andiskutieren und entsprechende Lösungsmöglichkeiten vorstellen.

 

Langsamer Dateizugriff

Das WSL2 verwendet einen speziell angepassten Linux-Kernel. Linux verwendet normalerweise allen freien Speicher als Dateisystem-Cache. Solange keine größere Speichermenge, zum Beispiel durch einen Prozess, freigeben wird, ist immer nur ein kleiner Teil des Arbeitsspeichers ungenutzt. Dank den Virtualisierungsschnittstellen kann Linux zwar Speicher nachfordern, wird es aber im Normalfall nicht wieder an das Host-System zurückgeben. Da das WSL allerdings nicht nur auf Servern und für Dienste verwendet werden soll, sondern auch lokal auf Desktops, ist dieses Verhalten alles andere als optimal. Wer will schon, dass das WSL dauerhaft den halben Arbeitsspeicher reserviert, nur weil man zuvor mal ein Docker-Image gebaut hat.

 

Um den Speicher auch wieder zurück an Windows zu geben, wurde der Linux-Kernel entsprechend erweitert. Wenn der Kernel nichts zu tun hat, wird der Datei-Cache freigegeben und der Speicher an Windows zurückgegeben. Diese Änderung scheint einen unerwünschten Nebeneffekt zu haben: Wenn der Speicher einen bestimmten Füllstand erreicht hat, sollen eigentlich alte Daten aus dem Datei-Cache geworfen werden. Stattdessen werden keine neuen eingelagert. Möglicherweise fängt der Windows-Host an den Arbeitsspeicher des Linux-Gasts auszulagern - auch wenn dies eigentlich nicht passieren sollte. Wie dem auch sei: Das System wird langsam, da immer auf die Platte durchgegriffen werden muss. Die Lösung ist das Ausführen von sudo bash -c "echo 1 > /proc/sys/vm/drop_caches" (Doku). Hierdurch wird der Datei-Cache verworfen und die Systemleistung geht wieder hoch. Baut man größere Docker-Images, kann dies mehrmals während Bauprozesses nötig sein. Damit man das nicht manuell machen muss, gibt es folgendes kleines Skript, dass man in dem WSL2 als root im Hintergrund laufen lassen kann:

 

#!/bin/bash

#Acquire lock
touch /var/run/lock/wslmemwatch
exec {FD}< /var/run/lock/wslmemwatch

if ! flock -x -n $FD; then
  echo "wslmemwatch is already running"
  exit 1
fi

echo "wslmemwatch started"

COUNTER=0

(
    trap ':' HUP
    while :
    do
        freemem=`free -m | head -n2 | tail -n1  | awk '{print $4}'`
        if (( $freemem < 1500 )); then
            let COUNTER++
            if (( $COUNTER < 10 )) ; then
                echo Dropping Pagecache
                echo 1 > /proc/sys/vm/drop_caches
            else 
                echo Drepping Pagecache and 
                echo 3 > /proc/sys/vm/drop_caches
				COUNTER=0
            fi

        fi
        sleep 2
    done
)

 

Das Skript gibt den Datei-Cache frei, wenn nur noch 1,5 GB zur Verfügung stehen. Wir haben festgestellt, dass man hier ein wenig experimentieren muss und sich jedes System anders verhält. Der Wert muss also gegebenenfalls angepasst werden. Zusätzlich wird alle 10 Freigaben auch noch der Dateisystem-Cache gelöscht.

 

Legt man dieses Skript nun in dem WSL unter /usr/local/bin/wslmemwatch.sh ab, kann man es mit wsl -u root wslmemwatsh.sh aus Windows starten. Dies kann man dann zum Beispiel über den Aufgabenplanung automatisch beim Start des Systems machen. Das Starten über ein WSL-Initsystem scheint aktuell nicht möglich zu sein. Hinweise sind aber willkommen.

 

Festplattenspeicher freigeben

Zunächst sollte man mit docker system df oder, etwas detaillierter, mit docker system df -v schauen wodurch der Speicher tatsächlich belegt wird. Will man bis auf die Docker-Volumes alles verwerfen, räumt docker system prune das System auf.

 

Um die Docker-Images zu bereinigen, ist die Docker Desktop GUI inzwischen ganz brauchbar. Hier kann man mit der Clean up-Funktion recht gut aufräumen:

 

WSL2 1

 

WSL2 2

 

Den Buildcache kann man mit docker builder prune leeren.

 

Das Löschen der Volumes sollte mit Vorsicht gemacht werden. Immerhin liegen hier ja Daten drin, die man gegebenenfalls behalten will. Aus diesem Grund werden die Volumes, im Gegensatz zu den Images, bei einem docker system prune nicht gelöscht. Den verbrauchten Speicher der Volumes und deren Ids bekommt man mit docker system df -v und kann sie dann mit docker volume rm <ID> löschen.

 

Freigegebenen Speicher an das Windowssystem zurückgeben

Wer jetzt erwartet, dass man wieder mehr Platz auf seiner Festplatte hat, wird enttäuscht sein. Der WSL2 baut auf Hyper-V auf. Daher liegen die Daten auf einer virtuellen Festplatte. Werden Daten auf die virtuelle Festplatte geschrieben wächst diese automatisch mit. Allerdings muss man diese, während Hyper-V gerade nicht darauf zugreift, manuell komprimieren, um die Speicher an das Host-System zurückzugeben.

 

Das folgende Powershell-Skript sucht nach allen virtuellen Festplatten im VHD-Format im lokalen Appdata-Pfad, beendet den WSL2 und komprimiert die gefundenen virtuellen Festplatten. Werden hierbei andere virtuelle Festplatten gefunden, ist es sicher kein Schaden diese auch zu komprimieren. Sollten diese gerade in Verwendung sein, zum Beispiel durch eine andere virtuelle Maschine, schlägt der Kompressionsvorgang fehl und das Skript macht mit der nächsten virtuellen Festplatte weiter.

 

$confirmation = Read-Host "This will restart Docker and WSL. Press y to proceed"
if ($confirmation -ne 'y') {
    exit
}


Write-Host "=> Searching for *.vhdx in ${env:LOCALAPPDATA}"
 
$aVhdx = Get-Childitem -Path ${env:LOCALAPPDATA} -Recurse -Include "*.vhdx"  -Force -ErrorAction SilentlyContinue -Attributes !Hidden,!System,!ReparsePoint | Select-Object FullName, @{Name="Size (MB)";Expression={$_.Length / 1MB}}

if( $a.GetType().Name -ne "Object[]" ) {
    # Seems like it is not possible to continue searching if Get-Childitem gets an UnauthorizedAccessException. 
    # Feel free to serach for lots of unhelpful hints using "Get-Childitem SilentlyContinue UnauthorizedAccessException"
    # Hints on how to fix this are welcome.
    Write-Host "Get-Childitem failed. You might need to reboot.";
    exit
}
 
$aVhdx | Format-Table
 
Write-Host "`n=> Pruning and shutting down docker"

&docker stop @(docker ps -a -q)
docker system prune

Write-Host "`n=> Trimming and shutting down wsl to release vhdx"
 
wsl -u root fstrim /
wsl --shutdown
 
# Seems like some things need to shut down
Start-Sleep -s 2
 
foreach ($vhdx in $aVhdx){
    Write-Host "`n=> Compressing $($vhdx.FullName)"
    Mount-VHD -Path $vhdx.FullName -ReadOnly
    Optimize-VHD -Path $vhdx.FullName -Mode Full
    Dismount-VHD $vhdx.FullName
}

$ErrorActionPreference = "silentlycontinue"
$aVhdx += Get-Childitem -Path ${env:LOCALAPPDATA} -Recurse -Include "*.vhdx"  -Force -ErrorAction SilentlyContinue | Select-Object FullName, @{Name="Size (MB)";Expression={$_.Length / 1MB}}
$ErrorActionPreference = Inquire
 
 
$aVhdx | Format-Table

 

Am Ende werden die alten und neuen Größen der virtuellen Festplatten angezeigt. Diese könnte man sicher schöner gestalten, würde aber das Skript unnötig kompliziert machen.

Fazit

Mit dem WSL2 erhält man eine Umgebung, in der die Grenzen zwischen Windows und Linux verschwimmen. Die Möglichkeit in dem WSL2 auch eine fast vollwertige Linux-Docker-Umgebung zu haben, ist hier sicher eher als schöner Nebeneffekt des Linux-Kernels in dem WSL2 zu sehen. Auch kann man sicher davon ausgehen, dass das Verschieben großer Datenmengen sicher nicht die Standardanwendung ist und es noch ein Weilchen dauern kann, bis die Probleme behoben werden. Bis dahin helfen die vorgestellten Skripte.

  

 

Über den Autor

 

Denise Fritsch BlogMoritz Strübe 

Embedded Software Developer

Moritz "Morty" Strübe arbeitet als Embedded-Software-Entwickler bei MATHEMA. Am liebsten entwickelt er Bare-Metal-Software in C++. Daneben beschäftigt er sich unter anderem mit den Themen Software-Architektur und DevOps im Embedded-Umfeld.

This email address is being protected from spambots. You need JavaScript enabled to view it.

 

 

news

News

contact

Kontakt

jobs

Jobs