今までdockerをなんとなくで触ってきていましたが,最近触ることがありその中で気づいたことがあるのでメモがてら書いておきます.
マウントされたファイルシステムにアクセスするときのパーミッションエラー問題
調べるといろいろ出てくると思いますが,コンテナにファイルシステムをマウント*1したとき,ホストマシンとコンテナ間でuidやgidがそのまま使われてしまうことによっていざアプリケーションを動かそうとしたりコンテナが書き込んだデータがroot所有になっていたりする,という問題がlinuxでは生じます*2.
この問題に遭遇した時,様々な解決策が講じられていますが,とりあえずそれらは置いておいて,なぜこのような現象が起きるのかを理解するためにいくつか実験を行います.
実験その1 〜特に何も考えず書き込む〜
まずは特に何も考えずbind mountを行ってコンテナ内でマウントされたディレクトリに書き込みを行います.
以下のようなDockerfileを用意して
FROM debian:11-slim CMD ["touch", "/hogehoge/test.txt"]
koutarou@koutarou-desktop/INS> docker run --rm -v $(pwd)/hogehoge:/hogehoge docker-permission-test koutarou@koutarou-desktop/INS> ls Dockerfile hogehoge koutarou@koutarou-desktop/INS> 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 koutarou@koutarou-desktop/INS> 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
ディレクトリがある状態で走らせるとどうでしょうか?
koutarou@koutarou-desktop/INS> mkdir hogehoge koutarou@koutarou-desktop/INS> docker run --rm -v $(pwd)/hogehoge:/hogehoge docker-permission-test koutarou@koutarou-desktop/INS> 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 koutarou@koutarou-desktop/INS> 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
する時にユーザー指定してみるとどうなるでしょうか?
koutarou@koutarou-desktop/INS> mkdir hogehoge koutarou@koutarou-desktop/INS> chmod 777 hogehoge koutarou@koutarou-desktop/INS> docker run --rm -u=1001:1001 -v $(pwd)/hogehoge:/hogehoge docker-permission-test koutarou@koutarou-desktop/INS> 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 koutarou@koutarou-desktop/INS> 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のユーザー・グループが存在していないため数字での指定となっています. これを存在する番号で指定すると下のように名前で表示してくれるようになります.
koutarou@koutarou-desktop/INS> docker run --rm -u=117:124 -v $(pwd)/hogehoge:/hogehoge docker-permission-test koutarou@koutarou-desktop/INS> ls -l hogehoge 合計 0 -rw-r--r-- 1 pulse pulse 0 6月 16 21:41 test.txt
ここで注意してほしいのは,コンテナには117番のユーザーや124番のグループは存在していないのにホストマシンでlsした結果適切な名前で表示されているということです. つまり,コンテナは指定されたら指定の通りの実行ユーザーでファイルシステムにアクセスを行いますが,そのアクセスはホストマシンのファイルシステムにもそのまま適用されるということです. よって,dockerはコンテナとホストマシンの間で特にユーザー・グループの変換を行わず,単純にファイルシステムに番号だけを書き残すということが分かります.
なお,ユーザーの指定はdocker run
するときだけでなく,下のようにDockerfileにも書くことができます*3.
FROM debian:11-slim USER 1001:1001 CMD ["touch", "/hogehoge/test.txt"]
実験その3 〜mkdirせずにマウントしたディレクトリに対してユーザー指定したコンテナでアクセスする〜
実験2では手動でmkdirしていましたが,これをdockerに任せるとどうなるでしょうか?ユーザー指定したのでそのパーミッションで作ってくれるのでしょうか?
koutarou@koutarou-desktop/INS> docker run --rm -u=1001:1001 -v $(pwd)/hogehoge:/hogehoge docker-permission-test touch: cannot touch '/hogehoge/test.txt': Permission denied koutarou@koutarou-desktop/INS> 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に書いてイメージ自体に実行ユーザーを設定することもできますし((というかdocker run
のuオプション自体それを上書きするという挙動です)),存在しない番号を指定するみたいなこともできてしまうのでdocker engineに任せるというのが自然ですね.