🤕

Docker Desktopのvhdxが肥大化して縮まない時の対処法

に公開

導入

Docker Desktop(WSL2バックエンド)のvhdxファイルは、コンテナ内のデータを削除しても自動では縮小しない。
本記事では、diskpart単体では効かない理由と、ゼロ埋め→compactの2段階で確実に縮小させる手順を解説する。

問題

Docker Desktop(WSL2バックエンド)の仮想ディスク docker_data.vhdx は、コンテナ内のデータが増えると自動で膨張するが、データを削除しても自動では縮小しない

たとえば以下のような状況がこれにあたる:

  • 大量のデータを投入後に削除・ロールバックしたが、vhdxが縮まない
  • OPTIMIZE TABLE 等でDB内部の断片化を解消したのに、ファイルサイズが変わらない
  • Dドライブの空き容量が想定より少なく、vhdxが原因と判明した

今回のケース:

  • MySQL内部データ: 72GB
  • vhdxファイル: 169.3GB(約97GBが無駄)
  • 原因: 大量INSERT → 容量不足で強制終了 → ロールバック → OPTIMIZE TABLE実行後も未使用領域がvhdx内に残存

なぜ diskpart compact だけでは縮まないのか

diskpartの compact vdiskゼロ埋めされたブロックしか解放できない。
WSL2内のext4ファイルシステムは、ファイル削除時にブロックをゼロクリアしないみたい。
結果、diskpartから見ると「まだ使用中」に見え、compactが空振りする。

解決手順

前提

  • Windows 11
  • Docker Desktop(WSL2バックエンド)
  • vhdxの場所を把握していること(デフォルト: %LOCALAPPDATA%\Docker\wsl\disk\docker_data.vhdx

1. コンテナ内から未使用領域をゼロ埋め

docker compose exec <サービス名> bash -c "dd if=/dev/zero of=/tmp/zerofill bs=1M || true; rm -f /tmp/zerofill; sync"

未使用領域をゼロで埋めたファイルを作り、即削除する。
ディスクの空き容量分の書き込みが発生するため時間がかかる(数分〜数十分)。

fstrim について: ゼロ埋めより高速なため先に試す価値はあるが、WSL2環境では the discard operation is not supported となって使えないことが多い。使えなかった場合は上記の dd による力技が確実。

2. Docker・WSLを完全停止

docker compose down
wsl --shutdown

3. diskpart compactを実行

管理者PowerShellで:

diskpart
select vdisk file="<vhdxのフルパス>"
attach vdisk readonly
compact vdisk
detach vdisk
exit

4. 結果確認

Get-ChildItem "<vhdxのフルパス>" | Select-Object @{N='Size_GB';E={[math]::Round($_.Length/1GB,1)}}

今回の結果

項目 Before After
MySQL内部データ 72GB 72GB
vhdxファイル 169.3GB 76.2GB
回収した容量 93.1GB

まとめ

  • vhdxは膨張するが自動で縮小しない
  • diskpart compact 単体では効かない(ゼロ埋めされていないため)
  • ゼロ埋め → compact の2段階が必要
  • fstrim が使えない場合は dd if=/dev/zero で力技のゼロ埋めが確実

Discussion