💭

なぜDocker in Dockerで親Dockerの下位のフォルダに子Dockerのボリュームマウントができないのか

に公開

Docker in Dockerで親Dockerの下層のフォルダに子Dockerのvolumeをマウントしようとしてうまくいかなかった理由を記載します。この問題に当たった時はDevcontainer in Devcontainerでしたが、根本はDockerの制約であると理解しています。

問題が発生したとき

Docker環境1からさらにDocker環境2を開いて、Docker環境2のボリュームマウントをDocker環境1の下位として見えているフォルダとして、フォルダを共通化しようとした。

問題が発生した時の環境構造

WSL → Docker環境1 → Docker環境2

この3層構造で、Docker環境2のボリュームマウントがDocker環境1内のフォルダを参照しようとしている状況

原因

この問題は、Dockerのアーキテクチャ上の制約であり、ネストした環境でのファイルシステム参照の複雑性が原因です。絶対パスでも相対パスでも、パス解決の基準点が異なるため、同じ問題が発生する。

この問題のわかりにくさ

Dockerは存在しないパスを指定された場合、自動的に空のディレクトリを作成してマウントします。これにより:

  • エラーは出ないが、期待したデータは見えない
  • 一見マウントは成功しているように見える
  • 実際には意図しない場所(通常はDocker環境1のルートファイルシステム)に空のディレクトリが作成される

筆者はまんまとこの罠にかかり、マウントできているのにファイルが参照できないという事態に苦しみました。

詳細な原因

1. Docker-in-Docker (DinD) の制約

Docker環境1内でDocker環境2を起動する際、Docker環境2のDockerデーモンは以下のいずれかの方法で動作します:

  • Docker環境1のDockerデーモンを共有(Docker socket共有)
  • Docker環境1内で独立したDockerデーモンを実行

どちらの場合も、ボリュームマウントのパス解決に問題が発生します。

2. パス解決の問題

絶対パスの場合:

bash

# Docker環境2で指定
docker run -v /path/to/folder:/container/path image

この/path/to/folderは、Docker環境2から見たファイルシステムのルートから解決されますが、実際にはWSLまたはDocker環境1のファイルシステムを参照する必要があります。

相対パスの場合:

bash

# Docker環境2で指定
docker run -v ./folder:/container/path image

相対パスは、Docker環境2のワーキングディレクトリから解決されますが、これもDocker環境1内のファイルシステムとは異なる場合があります。

3. ファイルシステムの階層化

  • WSLのファイルシステム
  • Docker環境1のファイルシステム(コンテナ内)
  • Docker環境2のファイルシステム(さらにネストしたコンテナ内)

各層で異なるファイルシステムビューを持つため、パスが正しく解決されません。
この問題は、Dockerのアーキテクチャ上の制約であり、ネストした環境でのファイルシステム参照の複雑性が原因です。絶対パスでも相対パスでも、パス解決の基準点が異なるため、同じ問題が発生します。

解決策

Claudeに解決策を探ってもらった所、以下の解決策がでました。
絶対パスを用いることで解決しましたが、Windows 環境(devcontainer with wsl2 + docker Desktop)ではLinux/macOS表記のパスではなく、Windows形式のパスによる指定が必要でした。

1. WSLレベルでのパス統一

bash

# WSLで共有ディレクトリを作成
mkdir -p /mnt/c/shared

# Docker環境1起動時
docker run -v /mnt/c/shared:/app/shared docker1-image

# Docker環境1内でDocker環境2起動時
docker run -v /mnt/c/shared:/container/data docker2-image

2. Docker Volumeの使用

bash

# WSLでボリューム作成
docker volume create shared-data

# Docker環境1
docker run -v shared-data:/app/shared docker1-image

# Docker環境2
docker run -v shared-data:/container/data docker2-image

Discussion