Docker

Docker Desktop for Mac は、コンテナ・イメージ・ボリュームをまとめて Docker.raw という 1 ファイルに入れています。更新前にこのファイルを APFS のクローン(cp -c)で複製しておけば、143GB あっても一瞬で、追加のディスク容量もほぼゼロで退避できます。4.3.2 から 4.87.0 へ 5 年分をまとめて更新する際に、この方法でローカルの全プロジェクトの DB を守った手順です。

普通のコピーでは入らない

メジャーバージョンを大きく跨ぐ更新では VM 形式の移行が入り、失敗すればボリュームごと消えます。ローカルで動いている全プロジェクトの DB がこの 1 ファイルの中にあるので、被害はプロジェクト数分です。一方で Docker.raw は実サイズ 143GB(見かけ上は 168GB)あり、空き容量 50GB のディスクには普通のコピーが入りません。ここで使えるのが APFS のクローンです。

手順1: Docker Desktop を終了してクローンする

先に Docker Desktop を終了します。VM が止まった状態で複製すると、書き込み途中のない整合したスナップショットになります。

cd ~/Library/Containers/com.docker.docker/Data/vms/0/data/
cp -c Docker.raw Docker.raw.backup
df -h /System/Volumes/Data

cp -c は APFS の clonefile を使うコピーで、実測 0.005 秒で終わり、df の空き容量は実行前後とも 50Gi のまま変わりませんでした。2 つのファイル名が同じデータブロックを共有し、元ファイルが書き換わった分だけ容量を消費し始める仕組みです。

手順2: DB の論理ダンプも取る

クローンは同じバージョンの Docker Desktop でしか戻せません。旧バージョンを入れ直す状況を避けるために、各 DB コンテナから論理ダンプも取ります。root パスワードはコンテナの環境変数から拾えます。

PW=$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' project_db_1 | grep '^MYSQL_ROOT_PASSWORD=' | cut -d= -f2)
docker exec project_db_1 sh -c "exec mysqldump -uroot -p'$PW' --all-databases --single-transaction" | gzip > /path/to/backup/project_db.sql.gz
gunzip -t /path/to/backup/project_db.sql.gz

PostgreSQL は POSTGRES_USER を読んで pg_dumpall -U そのユーザー です。存在しない postgres ユーザーで実行すると 20 バイトの空 gzip だけが出来るので、ファイルサイズも見てください。

手順3: 旧アプリ本体もクローンする

cp -cR /Applications/Docker.app ~/Downloads/Docker-old-backup.app

1.5GB のアプリも一瞬です。「旧アプリ + 旧 VM ディスク」が揃うので、更新が失敗しても完全に元の状態へ戻せます。

更新後の確認と symlink の罠

新しい Desktop を起動したら docker version を見ます。私の環境では Server が 29.7.2(Desktop 4.87.0)なのに Client が 20.10.11 のままでした。readlink /usr/local/bin/docker で追うと、リンク先が手順 3 で作った Downloads 内のバックアップアプリになっていました。原因は特定できていませんが、旧アプリの複製を同じマシンに置いた状態で入れ替えると起きます。

ln -sf /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker
ln -sf /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose /usr/local/bin/docker-compose

新しい Desktop は docker-composeResources/bin ではなく Resources/cli-plugins に置いているので、旧リンクのパスを機械的に置換するだけでは死んだリンクが残ります。ls -la /usr/local/bin | grep docker で全件の行き先を確認してください。

最後に各 DB コンテナで information_schema.tables の件数を数え、更新前の数と一致することを確かめて完了です。数日安定したら Docker.raw.backup と旧アプリのクローンを削除します。使い込むほど元ファイルとの差分が実容量を食い始めるので、放置はしないでください。