1. はじめに
Dockerコンテナからボリュームマウントを通じてファイルを書き出す場合、ユーザID/グループID(以下、UID/GID)を指定しないとファイルのオーナがroot
(UID0
)になってしまい、困ることがあります。
docker container run
コマンドの--user
オプションでUID/GIDを指定することができますが、Linux版DockerとDocker Desktop for Macでは挙動に差があります。
これらの挙動について自分なりに整理するために、いろいろ実験しました。
なお、Dockerfile
のUSER
命令を使用してUID/GIDを指定する方法もありますが、今回はそちらについては触れていませんのでご了承ください。
余裕があれば、Rootlessモード編、userns-remapモード編も書きたいと思います。というか、そちらが本命なのですが。
2. Linux版 通常モード
まずは、Linux版Dockerの通常モード(Rootlessモードでも、userns-remapモードでもない、一般的なモード)での挙動を調査しました。
2.1. 環境
調査した環境は以下の通りです。
- ハードウェア: Raspberry Pi 4 2GB版
- OS: Ubuntu Server 20.04 LTS ARM64(64ビット)版
- Docker Engine: 19.03.8
ubuntu$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
ubuntu$ uname -a
Linux ubuntu 5.4.0-1008-raspi #8-Ubuntu SMP Wed Apr 8 11:13:06 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
ubuntu$ sudo docker version
Client:
Version: 19.03.8
API version: 1.40
Go version: go1.13.8
Git commit: afacb8b7f0
Built: Wed Mar 11 23:43:15 2020
OS/Arch: linux/arm64
Experimental: false
Server:
Engine:
Version: 19.03.8
API version: 1.40 (minimum version 1.12)
Go version: go1.13.8
Git commit: afacb8b7f0
Built: Wed Mar 11 22:48:33 2020
OS/Arch: linux/arm64
Experimental: false
containerd:
Version: 1.3.3-0ubuntu2
GitCommit:
runc:
Version: spec: 1.0.1-dev
GitCommit:
docker-init:
Version: 0.18.0
GitCommit:
ubuntu$ sudo docker info
...
Security Options:
apparmor
seccomp
Profile: default
...
なお、以下のコマンドは標準のubuntu
ユーザ(UID1000
、GID1000
)で実行しています。
ubuntu
ユーザはdocker
グループには意図的に入れていないため、docker
コマンドの実行にはsudo
を利用しています。
Rootlessモード、userns-remapモードは使用していませんが、念のため/etc/subuid
、/etc/subgid
の内容も示します。
ubuntu$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),115(netdev),118(lxd)
ubuntu$ cat /etc/subuid
ubuntu:100000:65536
ubuntu$ cat /etc/subgid
ubuntu:100000:65536
2.2. 実験
実験は以下の通りです。インラインで説明を記載しています。
なお、ファイルの読み込み権限を分かりやすくするためにumask
でパーミッションのマスクを設定しています。
また、今回の実験ではUIDとGIDに同一の値を設定していますが、もちろん別々の値でも構いません。
# 実験用のディレクトリを作成します。(他ユーザでも書き込める必要があります)
ubuntu$ mkdir -p -m 777 /tmp/docker/normal
# UID/GIDを指定せず、idコマンドを実行してUID/GIDを確認します。
# →UID/GIDは0:0です。
ubuntu$ sudo docker container run --tty --rm ubuntu:20.04 id
uid=0(root) gid=0(root) groups=0(root)
# UID/GIDを指定せず、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは0:0です。
ubuntu$ sudo docker container run --tty --rm --volume /tmp/docker/normal:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/without_user && ls -ln /out/without_user"
-rw------- 1 0 0 0 May 18 15:53 /out/without_user
# UID/GIDとして1000:1000を指定し、idコマンドを実行してUID/GIDを確認します。
# →UID/GIDは1000:1000です。
ubuntu$ sudo docker container run --tty --user 1000:1000 ubuntu:20.04 id
uid=1000 gid=1000 groups=1000
# UID/GIDとして1000:1000を指定し、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは1000:1000です。
ubuntu$ sudo docker container run --tty --user 1000:1000 --volume /tmp/docker/normal:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/with_user_1000 && ls -ln /out/with_user_1000"
-rw------- 1 1000 1000 0 May 18 15:54 /out/with_user_1000
# UID/GIDとして2000:2000を指定し、idコマンドを実行してUID/GIDを確認します。
# →UIG/GIDは2000:2000です。
ubuntu$ sudo docker container run --tty --user 2000:2000 ubuntu:20.04 id
uid=2000 gid=2000 groups=2000
# UID/GIDとして2000:2000を指定し、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは2000:2000です。
ubuntu$ sudo docker container run --tty --user 2000:2000 --volume /tmp/docker/normal:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/with_user_2000 && ls -ln /out/with_user_2000"
-rw------- 1 2000 2000 0 May 18 15:54 /out/with_user_2000
# Dockerホスト側のファイルのUID/GIDを確認します。
# →UID/GIDはDockerコンテナ内と同一です。
ubuntu$ ls -ln /tmp/docker/normal/
total 0
-rw------- 1 0 0 0 May 18 15:53 without_user
-rw------- 1 1000 1000 0 May 18 15:54 with_user_1000
-rw------- 1 2000 2000 0 May 18 15:54 with_user_2000
# Dockerホスト側でファイルを読めるかどうか確認します。
# →ubuntuユーザでは0:0のファイルを読むことができません。
ubuntu$ cat /tmp/docker/normal/without_user
cat: /tmp/docker/normal/without_user: Permission denied
# →ubuntuユーザでは1000:1000のファイルを読むことができます。
ubuntu$ cat /tmp/docker/normal/with_user_1000
# →ubuntuユーザでは2000:2000のファイルを読むことができません。
ubuntu$ cat /tmp/docker/normal/with_user_2000
cat: /tmp/docker/normal/with_user_2000: Permission denied
Linux版Dockerの通常モードでは、割と素直な結果になりました。整理すると以下の通りです。
-
--user
オプションを指定しない場合:- Dockerコンテナ内での実行UID/GIDは
0:0
となり、ファイルのUID/GIDも同一となる。 - また、Dockerホスト側から見てもファイルのUID/GIDはDockerコンテナ内と同一となる。
- Dockerコンテナ内での実行UID/GIDは
-
--user
オプションを指定した場合:- Dockerコンテナ内での実行UID/GIDは
--user
オプションで指定した値となり、ファイルのUID/GIDも同一となる。 - また、Dockerホスト側から見てもファイルのUID/GIDはDockerコンテナ内と同一となる。
- Dockerコンテナ内での実行UID/GIDは
3. Docker Desktop for Mac
続いてmacOS上で動作する「Docker Desktop for Mac」での挙動を調査しました。
3.1. 環境
調査した環境は以下の通りです。
- ハードウェア: MacBook Pro 2018
- OS: macOS 10.14.6(Mojave)
- Docker Desktop for Mac: 2.3.0.2
- Docker Engine: 19.03.8
mac$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G103
mac$ docker version
Client: Docker Engine - Community
Version: 19.03.8
API version: 1.40
Go version: go1.12.17
Git commit: afacb8b
Built: Wed Mar 11 01:21:11 2020
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.8
API version: 1.40 (minimum version 1.12)
Go version: go1.12.17
Git commit: afacb8b
Built: Wed Mar 11 01:29:16 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
mac$ docker info
...
Security Options:
seccomp
Profile: default
...
なお、以下のコマンドは作業用のユーザ(UID501
、GID20
)で実行しています。
mac$ id -u
501
mac$ id -g
20
3.2. 実験
実験は以下の通りです。インラインで説明を記載しています。
# 実験用のディレクトリを作成します。(他ユーザでも書き込める必要があります)
mac$ mkdir -p -m 777 /tmp/docker/mac
# UID/GIDを指定せず、idコマンドを実行してUID/GIDを確認します。
# →UID/GIDは0:0です。
mac$ docker container run --tty --rm ubuntu:20.04 id
uid=0(root) gid=0(root) groups=0(root)
# UID/GIDを指定せず、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは0:0です。
mac$ docker container run --tty --rm --volume /tmp/docker/mac:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/without_user && ls -ln /out/without_user"
-rw------- 1 0 0 0 May 18 15:23 /out/without_user
# UID/GIDとして1000:1000を指定し、idコマンドを実行してUID/GIDを確認します。
# →UID/GIDは1000:1000です。
mac$ docker container run --tty --user 1000:1000 ubuntu:20.04 id
uid=1000 gid=1000 groups=1000
# UID/GIDとして1000:1000を指定し、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは1000:1000です。
mac$ docker container run --tty --user 1000:1000 --volume /tmp/docker/mac:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/with_user_1000 && ls -ln /out/with_user_1000"
-rw------- 1 1000 1000 0 May 18 15:24 /out/with_user_1000
# UID/GIDとして2000:2000を指定し、idコマンドを実行してUID/GIDを確認します。
# →UIG/GIDは2000:2000です。
mac$ docker container run --tty --user 2000:2000 ubuntu:20.04 id
uid=2000 gid=2000 groups=2000
# UID/GIDとして2000:2000を指定し、touchコマンドで空ファイルを生成して、ファイルのUID/GIDを確認します。
# →ファイルのUID/GIDは2000:2000です。
mac$ docker container run --tty --user 2000:2000 --volume /tmp/docker/mac:/out ubuntu:20.04 /bin/bash -c "umask 0077 && touch /out/with_user_2000 && ls -ln /out/with_user_2000"
-rw------- 1 2000 2000 0 May 18 15:24 /out/with_user_2000
# Dockerホスト側のファイルのUID/GIDを確認します。
# →UID/GIDはDockerコンテナ内と異なり、UIDはすべてmacOS側の実行ユーザ、GIDはすべて0です。
mac$ ls -ln /tmp/docker/mac/
total 0
-rw------- 1 501 0 0 May 19 00:24 with_user_1000
-rw------- 1 501 0 0 May 19 00:24 with_user_2000
-rw------- 1 501 0 0 May 19 00:23 without_user
# Dockerホスト側でファイルを読めるかどうか確認します。
# →UIDが同一のため、すべてのファイルはエラーなく読むことができます。
mac$ cat /tmp/docker/mac/without_user
mac$ cat /tmp/docker/mac/with_user_1000
mac$ cat /tmp/docker/mac/with_user_2000
Docker Desktop for Macでは、Linux版Dockerの通常モードとは異なる結果になりました。整理すると以下の通りです。
-
--user
オプションを指定しない場合:- Dockerコンテナ内での実行UID/GIDは
0:0
となり、ファイルのUID/GIDも同一となる。(Linux版通常モードと同一の挙動) - ただし、Dockerホスト側から見たファイルのUID/GIDは
macOS側のUID:0
となる。(Linux版通常モードと異なる挙動)
- Dockerコンテナ内での実行UID/GIDは
-
--user
オプションを指定した場合:- Dockerコンテナ内での実行UID/GIDは指定した値となり、ファイルのUID/GIDも同一となる。(Linux版通常モードと同一の挙動)
- ただし、Dockerホスト側から見たファイルのUID/GIDは
macOS側のUID:0
となる。(Linux版通常モードと異なる挙動)
4. 最後に
本命はRootlessモード、userns-remapモードの挙動の調査なので、頑張って続きを書きたいと思います。