Docker for WindowsとWSLを併用するときのパーミッションとファイルユーザ

5 min読了の目安(約5300字TECH技術記事

Web開発をWindowsからやってもいいよね、特にフロントエンド。
みんなが使ってるWindowsのブラウザでの動作って気になるじゃん。
というわけで、Docker for WindowsとWSLの併用を始めることにしました。
Git for Windowsはちょっと大変そう、一方でWSLはDockerで動かしたWebコンテンツをWindowsのブラウザで見るのが大変そう。
なので、WSLでGitを使い、Docker for Windowsで開発に必要なプロセスを起動する、という変態チックなことをすることにしました。
ちなみにエディタはWindows版VSCodeです。

何が問題になるのか?

パーミッション管理がまともに出来ないという問題があります。ファイルオーナーとグループの設定が変に見えやすいです。
特にDockerコンテナの中で作ったファイルをWSLから見ると、なんだこれ?って感じになります。
そして、WSLから編集ができないのです。
実例を挙げると、以下のような感じです。
これは空のディレクトリにDockerfileとdocker-composeを作り、そこでDockerコンテナに入ってから npm install zenn-cli を実行しただけの状態です。

rotelstift@in_docker_container:~$ ls -la
total 320
drwxrwxrwx 1 root       root          512 Oct  3 16:18 .
drwxr-xr-x 1 root       root         4096 Oct  3 14:04 ..
drwx------ 1 rotelstift rotelstift    512 Oct  3 16:16 .config
drwxr-xr-x 1 rotelstift rotelstift    512 Oct  3 16:18 .npm
-rwxrwxrwx 1 root       root          260 Oct  3 16:10 Dockerfile
-rwxrwxrwx 1 root       root          159 Oct  3 16:15 docker-compose.yml
drwxr-xr-x 1 rotelstift rotelstift    512 Oct  3 16:18 node_modules
-rw-r--r-- 1 rotelstift rotelstift 320621 Oct  3 16:18 package-lock.json

このディレクトリをWSLの側から見てみます。

rotelstift@in_WSL:FullPathTo/test$ ls -la
total 316
drwxrwxrwx 1 rotelstift rotelstift    512 Oct  4 01:18 .
drwxrwxrwx 1 rotelstift rotelstift    512 Oct  4 00:57 ..
drwx------ 1       1001 docker        512 Oct  4 01:16 .config
drwxr-xr-x 1       1001 docker        512 Oct  4 01:18 .npm
-rwxrwxrwx 1 rotelstift rotelstift    260 Oct  4 01:10 Dockerfile
-rwxrwxrwx 1 rotelstift rotelstift    159 Oct  4 01:15 docker-compose.yml
drwxr-xr-x 1       1001 docker        512 Oct  4 01:18 node_modules
-rw-r--r-- 1       1001 docker     320621 Oct  4 01:18 package-lock.json

一見すると、何がどうなっているのかわかりません。
これを読み解いていきます。

前提条件

まず、WSL2ではUbuntuをインストールし、普段はrotelstiftというユーザで利用しています。
また、Dockerfileとdocker-compose.ymlは以下のようにして、やはりrotelstiftというユーザで使うようにしています。

FROM node:latest

RUN apt-get update
RUN apt-get install sudo

ENV USER rotelstift
ENV HOME /home/${USER}

RUN useradd -m ${USER}
RUN gpasswd -a ${USER} sudo
RUN echo "${USER}:passwd" | chpasswd

USER ${USER}
WORKDIR ${HOME}

CMD ["/bin/bash"]
version: '3'
services:
  test:
    build: .
    image: test
    ports:
    - "8080:8000"
    volumes:
    - C:\Users\rotel\src\test:/home/rotelstift

問題整理

WSLの側から見て、不明なユーザ1001によって作られたファイルが編集できないのが一番の問題です。
何故かVSCodeからも編集出来ないことがあります。
ちなみにDocker for Macではこのような問題は起きません。
では、これから分かったことをまとめていきます。

WSLでユーザ1001、グループDockerになるファイル

この所有権がついてしまうファイルは、Dockerの中ではユーザrotelstift、グループrotelstiftになっているファイルです。
つまり、Dockerコンテナの中で作られたファイルになります。

WSLでユーザrotelstift、グループrotelstiftになるファイル

この所有権が(ついでにパーミッション777が)ついてしまうファイルは、Dockerの中ではユーザroot、グループrootになっているファイルです。
このファイルはDockerコンテナの外、WindowsかWSLで作られたファイルになります。

一方通行の所有権管理

ここでDocker内とWSLの側で id コマンドを使ってユーザID、グループIDを調べてみましょう。

rotelstift@in_docker_container:~$ id
uid=1001(rotelstift) gid=1001(rotelstift) groups=1001(rotelstift),27(sudo)
rotelstift@in_WSL:FullPathTo/test$ id
uid=1000(rotelstift) gid=1000(rotelstift) groups=1000(rotelstift),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),1001(docker)

当たり前の話ですが、DockerコンテナとWSLの側でrotelstiftのユーザIDが違っています。
Dockerコンテナ内でのrotelstiftは uid=1001(rotelstift) gid=1001(rotelstift) です。
ただ、ここで着目したいのはDockerコンテナ内では gid=1001(rotelstift) に対し、WSLでは groups=1001(docker) になっていることです。
そして、Dockerコンテナ内で作られたファイルはユーザ1001、グループdockerだったことを思い出すと、Dockerコンテナ内で作られたファイルの所有権IDはWSL内でも引き継がれるのではないかという仮説が立てられます。
試しにWSLで次のコマンドを打ってみると、 cat /etc/passwd | grep uid=1001 結果は出てきません。
つまり、Dockerコンテナ内で作られたファイルの所有権はコンテナの外でも同じなのです。ただ、コンテナの外、WSLの側にその所有権IDに対応するユーザやグループがあったりなかったりする、という話なのです。

一方でその逆はないように見えます。
WSLの側でuid=1000 gid=1000で作られたファイルは、すべてrootとしてマウントされます。ちなみにrootはDockerコンテナ内でもWSLでも uid=0(root) gid=0(root) groups=0(root) です。
ここで試しにDockerコンテナ内で sudo touch page とやってみましょう。
コンテナ内では

rotelstift@in_docker_container:~$ ls -la page
-rw-r--r-- 1 root       root            0 Oct  4 03:33 page

となります。これをWSLから見たときに、ユーザrotelstift、グループrotelstiftになっているでしょうか?

rotelstift@in_WSL:FullPathTo/test$ ls -la page
-rw-r--r-- 1 root root 0 Oct  4 12:33 page

なっていません。
やはり、Dockerコンテナ内で作られたファイルの所有権IDはWSL内でも引き継がれるけれども、その逆はないことがわかります。

一応、その逆も試してみましょう。WSL内で touch hoge して、それをDockerコンテナの中からみたらどうなるでしょうか?

rotelstift@in_WSL:FullPathTo/test$ touch hoge
rotelstift@in_WSL:FullPathTo/test$ ls -la hoge
-rwxrwxrwx 1 rotelstift rotelstift 12 Oct  4 12:02 hoge
rotelstift@3eb35cef6f47:~$ ls -la hoge
-rwxrwxrwx 1 root root 12 Oct  4 03:02 hoge

やはりWSL内で作られたファイルはrootの所有(ただし777)でマウントされます。

Dockerコンテナ内で作られたファイルをどうすれば編集できるようになるのか

これに関しては答えが明白です。
Dockerコンテナ内で作られた全てのファイルはWindowsで編集するべきです。
いちいちパーミッションを直すのは面倒すぎます。そして、WSLはパーミッションを守りますが、Windowsは無視します。
とは言え、Windows版のVSCodeでも編集ができないことがありました。けれどもそのからくりは簡単で、VSCodeからremote-WSLに接続して文書を編集する機能があるのですが、それを使っているとWSLの制約を受けるのです。
なので、その機能を使わないで本当にWindowsの側だけでファイルを編集するといろいろと悩まなくて済みます。

感想

最初はWindows側ならパーミッションを無視することに気づかず、どうにかしてDockerコンテナ内で作られたファイルの所有権IDとWSL内の所有権IDを一致させられないかと頑張ってみましたが、最終的にはWSL側のrotelstiftのuidを変更しなければならないというところに来てしまって、そんな怖いこと出来んわ、ということで諦めました。
諦めたところでWindows側はパーミッションをさくっと無視することに気が付いて、井上陽水の夢の中への歌詞を思い出したりしました。
remote-WSLに接続していないとVSCode内でGitが使えなかったりしますが、ちゃんとコマンドラインからGitを使えばいいだけの話なのでそのデメリットは甘受する、といった感じです。