TL;DR
- Docker for WindowsをWSL(旧Bash on Ubuntu on Windows)から利用する際、バインドマウントする場合は注意が必要。
- Cドライブを共有に設定をしていれば、ホスト側ファイルシステムの
/c
と/C
にマウントされているので、/c/Users/hoge/fuga
の形式で参照しよう。 - WSL内のディレクトリ(
/home/hoge/fuga
など)は使えない。
環境
- Windows 10 Pro Fall Creators Update
- Docker for Windows Version 17.09.0-ce-win33 (13620)
- WSL:Ubuntu 16.04 LTS + docker 17.09.0-ce + docker-compose version 1.8.0
注意
- DockerのVolume周りの知識のある方は読んでも時間の無駄だと思います。
- 間違ってる箇所があればごめんなさい。
1. Docker for WindowsをWSLで使う方法
このあたりを参考に
WSL(Bash on Windows)でDockerを使用する - Qiita
ひょっとするとこれでも良いかもしれませんがw
【小ネタ】Docker for WindowsをWSLで使う一番楽な方法 - Qiita
2. 【復習】PowerShell(またはコマンドライン)でローカルディレクトリをマウントする場合
まずはWSLでなく単純にDocker for WindowsをコマンドラインやPowerShellから普通に使う場合の確認。
例えばMySQLのデータディレクトリ/var/lib/mysql
をC:\hoge\fuga
に保存する(Bind mountする)には、以下のように指定する。
2-1. docker run -vオプションの場合
-v (ローカルディレクトリ):(コンテナのディレクトリ)
で指定する。
PS C:\hoge> docker run -v C:\hoge\fuga:/var/lib/mysql -e "MYSQL_RANDOM_ROOT_PASSWORD=yes;MYSQL_DATABASE=hogedb;MYSQL_USER=admin;MYSQL_PASSWORD=pass" -p 3306:3306 mysql
2-2. docker-composeの場合
docker-composeを使う場合は、volumes:
に記述する。
version: '2'
services:
mysql:
image: mysql
volumes:
- C:\hoge\fuga:/var/lib/mysql
ports:
- 3306:3306
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=yes
- MYSQL_DATABASE=hogedb
- MYSQL_USER=admin
- MYSQL_PASSWORD=pass
PS C:\hoge> docker-compose up
これでC:\hoge\fuga
が/var/lib/mysql
にマウントされる。
基本ですね。
2-3. 共有設定について
マウントする際の注意点として、マウントしたいディレクトリは、事前に設定画面の[Shared Drives]でチェックを入れてドライブレベルで共有設定をしておく必要がある。
共有していないドライブのディレクトリをマウントしようとすると、以下の画面が出て設定を促される。(Y:\hoge\fuga
を指定してみた)
3. WSLでローカルディレクトリをマウントする場合
ここから本題。
3-1. docker run -vオプションの場合
WSLのBashで同じことをしようとした場合、Windowsのパス区切り文字である\
がBashのエスケープ文字のため、\\
にする必要がある。
$ docker run -v C:\\hoge\\fuga:/var/lib/mysql -e "MYSQL_RANDOM_ROOT_PASSWORD=yes;MYSQL_DATABASE=hogedb;MYSQL_USER=admin;MYSQL_PASSWORD=pass" -p 3306:3306 mysql
これで問題なし。
ただし、エスケープが面倒なら/c/hoge/fuga
でもOK。(理由は後述)
3-2. docker-composeの場合
Windowsのパス形式は使えない。
docker-compose.yml
にWindowsのパス形式で記述しているとエラーになる。
$ docker-compose up
ERROR: Named volume "C:\hoge\fuga:/var/lib/mysql" is used in service "mysql" but no declaration was found in the volumes section.
エラーメッセージを見ると、C:\hoge\fuga
がNamed volume(docker volume createコマンドで作成するvolume)として認識されてしまっている。
これを回避するには、docker-compose.ymlを以下のように修正する必要がある。
version: '2'
services:
mysql:
image: mysql
volumes:
- - C:\hoge\fuga:/var/lib/mysql
+ - /C/hoge/fuga:/var/lib/mysql
ports:
- 3306:3306
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=yes
- MYSQL_DATABASE=hogedb
- MYSQL_USER=admin
- MYSQL_PASSWORD=pass
Linuxのdocker-composeがWindowsのパス形式を理解できないのが原因。
docker run -v
の場合と同様、/c/hoge/fuga
の形式なら利用できるので、変換しておく必要がある。
WindowsのDocker ToolboxやDocker Machineなら環境変数COMPOSE_CONVERT_WINDOWS_PATHS=1
をセットすれば内部的に/c/hoge/fuga
のように変換して解釈してくれたが、残念ながらこの方法も使えなかった。
Docker for Windowsのデーモンはdocker run -v
の指定に対してはこの変換を行うものの、docker-composeには対応していないらしい。(たぶん)
なお、C:\\hoge\\fuga
のようにエスケープしてみても無理だった。
4. WSL内のディレクトリの扱い
4-1. WSL内のディレクトリは使えない
・WSL内のディレクトリを直接指定した場合
例えばWSL内の/home/hoge/fuga
をマウントしたい場合、そのまま指定すると
$ pwd
/home/hoge
$ docker run -v $PWD/fuga:/var/lib/mysql -e "MYSQL_RANDOM_ROOT_PASSWORD=yes;MYSQL_DATABASE=hogedb;MYSQL_USER=admin;MYSQL_PASSWORD=pass" -p 3306:3306 mysql
で、一見するとちゃんと動くように見える。
docker inspect
で確認すると、/home/hoge/fuga
がバインドされている。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4884a258cd99 mysql "docker-entrypoint..." 12 seconds ago Up 9 seconds 0.0.0.0:3306->3306/tcp nervous_banach
$ docker inspect nervous_banach
[
{
(省略)
"HostConfig": {
"Binds": [
"/home/hoge/fuga:/var/lib/mysql"
],
(以下略)
ただし、この/home/hoge/fuga
はホスト(仮想マシン)側のディレクトリなので、WSL側の/home/hoge/fuga
にはデータは作成されていない。
(MySQLは起動するだけで様々なファイルが作成されるので、起動後に実際にディレクトリ内の確認するとわかるが、空のまま)
・DrvFSの階層を指定した場合
WSLの/home/hoge/fuga
は、Windowsのディレクトリ構造上は
C:\Users\hoge\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\hoge\fuga
なので、そもそも階層が違う。(Ubuntuの場合)
このパスをWSLから参照する場合、
/mnt/c/Users/hoge/AppData/Local/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/home/hoge/fuga
になる。
この/mnt
からWindows上のドライブが参照できる仕組みはDrvFSというファイルシステムで実現されている。
そこで、これを使って
$ docker run -v /mnt/c/Users/hoge/AppData/Local/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/home/hoge/fuga:/var/lib/mysql -e "MYSQL_RANDOM_ROOT_PASSWORD=yes;MYSQL_DATABASE=hogedb;MYSQL_USER=admin;MYSQL_PASSWORD=pass" -p 3306:3306 mysql
としてみても、このパスはdocker側には理解できないため、やっぱりコンテナはホスト側のディレクトリを参照してしまう。
・VolFSの階層を指定した場合
WSLの/home/hoge/fuga
が
C:\Users\hoge\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\hoge\fuga
であるなら、
/c/Users/hoge/AppData/Local/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/home/hoge/fuga
を指定すれば使えるか?と思いきや、これも上手く行かない。
dockerホスト側はちゃんと
/c/Users/hoge/AppData/Local/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/home/hoge/fuga
に書き込みに行くが、そもそもこの階層はWindowsで直接扱えないため、ネットワーク共有越しにも書き込めない。
そのためか、ホスト上のこの階層にファイルは作られるものの、これらはホスト上にのみ存在することになり、クライアント側には書き込まれない。
正直この辺は調査不足で、具体的にどういう理由でこういう動きをするのかはよくわからない
ただ、仮に書き込めたとしても、DockerのサービスがWindows上で上記のフォルダにファイルを作成することになるため、WSL側で読むことができないハズ。
参考:Windows Subsystem for Linuxのファイルシステムにおける注意点 - Qiita
…というか、そもそもこんな深い階層を指定したくないわなw
4-2. docker-composeで相対パスを指定すると意図しないパスに変換される
上記の通りWSLのディレクトリ構造は使えないのであまり意味はないが、docker-compose.yml
で相対パスを指定するとおかしな変換がかかる。
version: '2'
services:
mysql:
image: mysql
volumes:
- ./fuga:/var/lib/mysql
ports:
- 3306:3306
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=yes
- MYSQL_DATABASE=hogedb
- MYSQL_USER=admin
- MYSQL_PASSWORD=pass
$ pwd
/home/hoge
$ docker-compose up -d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9872e250c33d mysql "docker-entrypoint..." About a minute ago Up About a minute 0.0.0.0:3306->3306/tcp hoge_mysql_1
$ docker inspect hoge_mysql_1
[
{
(省略)
"HostConfig": {
"Binds": [
"/mnt/c/hoge/fuga:/var/lib/mysql:rw"
],
(以下略)
.
が/mnt/c/hoge
に変換されてしまう。
/home
を/mnt/c
と解釈するみたいだが、理由は謎だし調べる気も起きない。
5. そもそもマウントとは
ここまで色々試してみたが、マウントの仕組みを理解していれば結構当たり前の内容かもしれない。
Dockerにおけるマウントは、基本的に以下の三種類ある。
- volume
- bind mount
- tmpfs mount
(Manage data in Docker | Docker Documentationより引用)
細かい説明は省くとして、図で明らかなように、Volumeをマウントするという行為は全てホスト側のファイルシステム内の話。
Docker for Windowsでdocker run -v C:\hoge:/var/hoge ...
しか使った事がないと、何か不思議な力でローカルのC:\hoge
にコンテナが直接読み書きを行ってくれているように感じるが、dockerのデーモン的にはホスト内の指定されたディレクトリを直接読み書きしているだけで、その場所がたまたまネットワーク越しにマウントされていた、と考えるとわかりやすい。
実感を得るためホストである仮想マシンのMobyLinuxVMの中身を確認してみると、2-3. 共有設定についてで共有にしたドライブがホストの/
にマウントされていることが確認できる。
(hostenterについてはこちら)
$ docker run -it --privileged --pid=host hostenter sh
/ # ls -l
total 56
drwxr-xr-x 2 root root 8192 Nov 28 01:36 C
drwxr-xr-x 2 root root 16384 Nov 27 13:31 D
drwxrwxrwt 6 root root 140 Nov 27 13:02 Database
drwxr-xr-x 2 root root 1680 Sep 27 00:15 bin
drwxr-xr-x 2 root root 8192 Nov 28 01:36 c
drwxrwxr-x 4 dockrema dockrema 80 Sep 27 00:37 containers
drwxr-xr-x 2 root root 16384 Nov 27 13:31 d
drwxr-xr-x 12 root root 2920 Nov 27 13:02 dev
drwxrwxr-x 25 dockrema dockrema 1320 Nov 27 13:02 etc
drwxr-xr-x 3 root root 60 Nov 28 06:35 home
-rwxrwxr-x 1 root root 878 Sep 27 00:15 init
drwxr-xr-x 7 dockrema dockrema 700 Sep 27 00:15 lib
drwxr-xr-x 5 root root 100 Sep 27 00:15 media
drwxr-xr-x 2 root root 40 Nov 28 08:32 mnt
drwxrwxrwx 1 docker docker 0 May 4 2006 port
dr-xr-xr-x 145 root root 0 Nov 27 13:02 proc
drwx------ 2 root root 60 Nov 28 00:27 root
drwxr-xr-x 9 root root 460 Nov 27 13:02 run
drwxrwxr-x 2 dockrema dockrema 2040 Sep 27 00:15 sbin
drwxr-xr-x 2 root root 40 Dec 26 2016 srv
dr-xr-xr-x 13 root root 0 Nov 27 13:02 sys
drwxrwxrwt 5 root root 100 Nov 29 01:34 tmp
drwxrwxr-x 9 dockrema dockrema 180 Sep 27 00:15 usr
drwxr-xr-x 11 root root 4096 Oct 19 07:05 var
/ # ls -l /c
total 7260145
-rwxr-xr-x 1 root root 1 Jul 16 2016 BOOTNXT
drwxr-xr-x 2 root root 4096 Oct 19 06:11 Documents and Settings
drwxr-xr-x 2 root root 0 Nov 16 2016 Intel
drwxr-xr-x 2 root root 0 Feb 15 2017 OneDriveTemp
drwxr-xr-x 2 root root 0 Sep 29 13:46 PerfLogs
dr-xr-xr-x 2 root root 0 Nov 21 13:02 Program Files
dr-xr-xr-x 2 root root 0 Nov 15 13:09 Program Files (x86)
drwxr-xr-x 2 root root 0 Nov 15 09:09 ProgramData
drwxr-xr-x 2 root root 0 Oct 19 06:24 Recovery
drwxr-xr-x 2 root root 0 Oct 26 01:08 System Volume Information
dr-xr-xr-x 2 root root 0 Oct 19 06:11 Users
drwxr-xr-x 2 root root 0 Nov 27 13:31 Windows
drwxr-xr-x 2 root root 0 Nov 27 13:00 _MEI134002
-r-xr-xr-x 1 root root 384322 Jul 16 2016 bootmgr
-rwxr-xr-x 1 root root 41388 Oct 19 05:44 downlevel_2017_10_19_13_17_17_700.log
-rwxr-xr-x 1 root root 6746087424 Nov 27 12:59 hiberfil.sys
drwxr-xr-x 2 root root 0 Oct 19 05:55 inetpub
-rwxr-xr-x 1 root root 419430400 Oct 19 05:46 pagefile.sys
-rwxr-xr-x 1 root root 268435456 Nov 27 12:59 swapfile.sys
/ # cat /etc/mtab
(略)
//10.0.75.1/C /c cifs rw,relatime,vers=3.02,sec=ntlmssp,cache=strict,username=hoge,domain=MyPC,uid=0,noforceuid,gid=0,noforcegid,addr=10.0.75.1,file_mode=0755,dir_mode=0755,iocharset=utf8,nounix,mapposix,nobrl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1 0 0
//10.0.75.1/C /C cifs rw,relatime,vers=3.02,sec=ntlmssp,cache=strict,username=hoge,domain=MyPC,uid=0,noforceuid,gid=0,noforcegid,addr=10.0.75.1,file_mode=0755,dir_mode=0755,iocharset=utf8,nounix,mapposix,nobrl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1 0 0
//10.0.75.1/D /d cifs rw,relatime,vers=3.02,sec=ntlmssp,cache=strict,username=hoge,domain=MyPC,uid=0,noforceuid,gid=0,noforcegid,addr=10.0.75.1,file_mode=0755,dir_mode=0755,iocharset=utf8,nounix,serverino,mapposix,nobrl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1 0 0
//10.0.75.1/D /D cifs rw,relatime,vers=3.02,sec=ntlmssp,cache=strict,username=hoge,domain=MyPC,uid=0,noforceuid,gid=0,noforcegid,addr=10.0.75.1,file_mode=0755,dir_mode=0755,iocharset=utf8,nounix,serverino,mapposix,nobrl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1 0 0
CドライブとDドライブを共有設定しているので、/c
,/C
,/d
,/D
がそれぞれマウントされている。
個人的にはドライブレターを大文字小文字を区別しないのが不思議だったが、割りと単純な話だった…。
Conclusion
- Docker for WindowsをWSLで使いたい場合、Bind mountできるのは通常のWindowsディレクトリ(VolFSはダメ)
-
C:\hoge\fuga
=/c/hoge/fuga
だという事を意識して、常に後者の指定をしておけばまぁ間違いない - WSL上で作業していると
C:\hoge\fuga
=/mnt/c/hoge/fuga
なのでややこしい 素直にPowerShell使えば良いんじゃ…
TIL
- 散々言及してきた
docker run
の-v
オプションは推奨されておらず、今は--mount
オプションが推奨らしい…。 -
-v
だと存在しないディレクトリ・ファイルは勝手に作るが--mount
ではエラーにするそうだ。確かにその方が良いな。
(2019/08/21 追記)
上記の記述はv17.09のドキュメントに書いてあった以下のTipを根拠にしていた。
Tip: New users should use the --mount syntax. Experienced users may be more familiar with the -v or --volume syntax, but are encouraged to use --mount, because research has shown it to be easier to use.
現時点の最新であるv19.07のドキュメントでは以下の記載に変わっていて、かなりトーンダウンしているみたい。
New users should try --mount syntax which is simpler than --volume syntax.
そのため、今は「-v
オプションは推奨されておらず」とまでは言えない気がする。
まぁDocker Hubなんかのコンテナの説明文でも-v
を使う例ばっかりだし…。