TL; DR;
- コンテナは指定されたユーザー番号(デフォルトでは0番のroot)でコンテナ内のアプリケーションを実行する。このユーザーはコンテナ・ホスト共に存在しないものを指定してもよい。
- マウントされたホスト上のファイルシステムに対してコンテナ内で書き込むと、コンテナが書き込み時に使用したユーザー番号がそのままホスト上でも使われる
- bind mountで存在しないディレクトリを指定するとdocker engineが勝手に作ってくれ、その所有者はdocker engineの実行ユーザー(多くの場合root)となる
マウントされたファイルシステムにアクセスするときのパーミッションエラー問題
調べるといろいろ出てくると思いますが、コンテナにファイルシステムをマウント1したとき、ホストマシンとコンテナ間でuidやgidがそのまま使われてしまうことによっていざアプリケーションを動かそうとしたりコンテナが書き込んだデータがroot所有になっていたりする、という問題がlinuxでは生じます2。
この問題に遭遇した時、様々な解決策が講じられていますが、とりあえずそれらは置いておいて、なぜこのような現象が起きるのかを理解するためにいくつか実験を行います。
実験その1 〜特に何も考えず書き込む〜
まずは特に何も考えずbind mountを行ってコンテナ内でマウントされたディレクトリに書き込みを行います。
以下のようなDockerfileを用意して
FROM debian:11-slim
CMD ["touch", "/hogehoge/test.txt"]
$ docker run --rm -v $(pwd)/hogehoge:/hogehoge docker-permission-test
$ ls
Dockerfile hogehoge
$ ls -l
合計 8
-rw-rw-r-- 1 koutarou koutarou 57 6月 16 21:26 Dockerfile
drwxr-xr-x 2 root root 4096 6月 16 21:26 hogehoge
$ ls hogehoge -l
合計 0
-rw-r--r-- 1 root root 0 6月 16 21:26 test.txt
マウントした hogehoge
ディレクトリにtest.txt
というファイルを作るだけです。
hogehoge
ディレクトリは走らせた時点で存在していないので勝手に(root:rootで)作られ、その中にはこれまたroot:rootなtest.txt
ができています。
それでは、予めhogehoge
ディレクトリがある状態で走らせるとどうでしょうか?
$ mkdir hogehoge
$ docker run --rm -v $(pwd)/hogehoge:/hogehoge docker-permission-test
$ ls -l
合計 8
-rw-rw-r-- 1 koutarou koutarou 57 6月 16 21:26 Dockerfile
drwxrwxr-x 2 koutarou koutarou 4096 6月 16 21:30 hogehoge
$ ls hogehoge -l
合計 0
-rw-r--r-- 1 root root 0 6月 16 21:30 test.txt
hogehoge
がroot:rootではなくmkdirしたユーザー・グループ所有のものになっています。
実験その2 〜コンテナで実行するユーザーを指定してみる〜
先程はdocker run
するときに特にユーザーを意識しませんでしたが、公式ドキュメントにはコンテナ内で実行されるプロセスの実行ユーザーを指定することができると書いてあります。
先ほどと同じDockerfileでdocker run
する時にユーザー指定してみるとどうなるでしょうか?
$ mkdir hogehoge
$ chmod 777 hogehoge
$ docker run --rm -u=1001:1001 -v $(pwd)/hogehoge:/hogehoge docker-permission-test
$ ls -l
合計 8
-rw-rw-r-- 1 koutarou koutarou 57 6月 16 21:26 Dockerfile
drwxrwxrwx 2 koutarou koutarou 4096 6月 16 21:37 hogehoge
$ ls -l hogehoge
合計 0
-rw-r--r-- 1 1001 1001 0 6月 16 21:37 test.txt
docker run
するときに1001:1001というユーザー・グループで実行するように指定しました。
その結果、test.txt
は1001:1001所有になりました。
ドキュメントにはデフォルトではidが0のユーザー(多くの場合はroot)が使われると書いてあります。そのため実験1ではroot:root所有になったと考えられます。 そして指定すると指定通りになりました。 なお、私のホストマシンにはidが1001のユーザー・グループが存在していないため数字での指定となっています。 これを存在する番号で指定すると下のように名前で表示してくれるようになります。
$ docker run --rm -u=117:124 -v $(pwd)/hogehoge:/hogehoge docker-permission-test
$ ls -l hogehoge
合計 0
-rw-r--r-- 1 pulse pulse 0 6月 16 21:41 test.txt
ここで注意してほしいのは、コンテナには117番のユーザーや124番のグループは存在していないのにホストマシンでlsした結果適切な名前で表示されているということです。 つまり、コンテナは指定されたら指定の通りの実行ユーザーでファイルシステムにアクセスを行いますが、そのアクセスはホストマシンのファイルシステムにもそのまま適用されるということです。 よって、dockerはコンテナとホストマシンの間で特にユーザー・グループの変換を行わず、単純にファイルシステムに番号だけを書き残すということが分かります。
なお、ユーザーの指定はdocker run
するときだけでなく、下のようにDockerfileにも書くことができます。
FROM debian:11-slim
USER 1001:1001
CMD ["touch", "/hogehoge/test.txt"]
実験その3 〜mkdirせずにマウントしたディレクトリに対してユーザー指定したコンテナでアクセスする〜
実験2では手動でmkdirしていましたが、これをdockerに任せるとどうなるでしょうか?ユーザー指定したのでそのパーミッションで作ってくれるのでしょうか?
$ docker run --rm -u=1001:1001 -v $(pwd)/hogehoge:/hogehoge docker-permission-test
touch: cannot touch '/hogehoge/test.txt': Permission denied
$ ls -l
合計 8
-rw-rw-r-- 1 koutarou koutarou 57 6月 16 21:57 Dockerfile
drwxr-xr-x 2 root root 4096 6月 16 21:57 hogehoge
permission deniedエラーが出てコンテナ実行はエラーに終わりました。
また、hogehoge
ディレクトリはroot:root所有となっています。
実験2までの知見から、「1001番のユーザー」(名前を意識していないのであえてこういう表記にしています)がroot:rootで766なディレクトリに対してファイルを作ろうとしたのでエラーが出たのだ、ということが推測され、恐らく実際その通りです。
ここまでは割と自明なのですが、当初の希望的観測では、ユーザー指定したらそのパーミッションでディレクトリを作ってくれるのではとしていました。 どうもこれは成り立たなかったようです。
公式ドキュメントによると、マウントするディレクトリが存在しなかった場合にはその都度作られる、としています。 誰が作っているかは書いてなさそうでしたが恐らくdockerの中核であるdocker engineだと思われます。 docker engine自体はrootで動いているので新しく作られたディレクトリはroot:root所有になるということです。
勝手にユーザーを推定してくれても親切じゃないかとも思いますが、よく考えてみるとdocker run
で指定しないでもDockerfileに書いてイメージ自体に実行ユーザーを設定することもできますし3、存在しない番号を指定するみたいなこともできてしまうのでdocker engineに任せるというのが自然ですね。