CUBE SUGAR CONTAINER

技術系のこと書きます。

Docker コンテナ内で Docker ホストと同じユーザを使う

Docker コンテナで Docker ホストのボリュームをマウントすると、パーミッションの問題が生じることがある。 これは、ホストで使っているユーザとコンテナで使っているユーザで UID と GID の不一致が起こるため。

今回は、それらの問題を解決する方法の一つとして Docker コンテナとホストで同じユーザを使う方法を試してみる。 具体的には、Docker ホストの /etc/passwd/etc/group をコンテナからマウントしてしまう。 そのため、このやり方は Docker for Mac や Docker for Windows では使えない。

典型的なユースケースは、パッケージやライブラリといった実行環境はコンテナ内に隠蔽しつつ、その他はホストの環境をなるべく使い回したいというものだろう。

使った環境は次の通り。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
$ uname -r
4.15.0-62-generic

コンテナとホストでユーザが異なる場合の問題点について

まずはコンテナとホストでユーザの UID と GID が異なる場合の問題点について見ておく。

例えば Docker ホストでは次のユーザを使っているとする。

$ id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)

試しに、カレントディレクトリにファイルを作っておこう。

$ echo "Hello, World" > greet.txt
$ ls -lF
total 4
-rw-rw-r-- 1 vagrant vagrant 13 Sep 19 09:10 greet.txt

続いて Docker コンテナを起動する。 カレントワーキングディレクトリを /mnt/working にマウントしておく。

$ sudo docker run \
    --rm \
    -v $(pwd):/mnt/working \
    -it busybox

この状態ではコンテナに root ユーザでログインしている。

# whoami
root
# id
uid=0(root) gid=0(root) groups=10(wheel)

マウントしたディレクトリに移動してファイルを作ってみる。

# cd /mnt/working/
# echo "Hello, World" > greet-docker-root.txt

ディレクトリにあるファイルを確認すると、次のようになっている。 自分で作ったファイルは root:root の権限がついている。 先ほど Docker ホストから作ったファイルは 1000:1000 という UID と GID がそのまま表示されている。 これは Docker コンテナ側で、それらの ID を持ったユーザが存在しないために起きている。

# ls -lF
total 8
-rw-r--r--    1 root     root            13 Sep 19 09:14 greet-docker-root.txt
-rw-rw-r--    1 1000     1000            13 Sep 19 09:10 greet.txt

Docker ホスト側でも同様に ls してみた結果は次の通り。 こちらでも Docker コンテナで作ったファイルは root:root となっている。 ホストで作ったファイルは現在のユーザの権限がついている。

$ ls -lF
total 8
-rw-r--r-- 1 root    root    13 Sep 19 09:14 greet-docker-root.txt
-rw-rw-r-- 1 vagrant vagrant 13 Sep 19 09:10 greet.txt

念のため、root 以外でも異なる UID と GID を持ったユーザを作って確認しておこう。 次の Dockerfile では example ユーザが 10000:10000 という UID と GID を持つようにしてある。

$ cat << 'EOF' > Dockerfile
FROM ubuntu:18.04

# Non administrative username
ENV USERNAME=example

# Add non administrative user
RUN useradd -m ${USERNAME}

# Add example group
RUN groupadd -g 10000 example-users

# Change user id and group id
RUN usermod -u 10000 -g 10000 ${USERNAME}

# Change user
USER ${USERNAME}

# Boot initial process
CMD bash

EOF

イメージのビルドとコンテナの起動のために、次のように Docker Compose 用の設定ファイルも用意しておく。

$ cat << 'EOF' > docker-compose.yml
version: "3"
services:
  example:
    build:
      context: .
    image: example/permission
    volumes:
      - ./:/mnt/working
EOF

まずはイメージをビルドする。

$ sudo docker-compose build

続いてコンテナを起動する。

$ sudo docker-compose run --rm example

起動したコンテナでは、次の通り 10000:10000 の UID と GID を持ったユーザでログインしている。

$ whoami
example
$ id
uid=10000(example) gid=10000(example-users) groups=10000(example-users)

マウントしたディレクトリに移動してファイルを作ってみよう。 すると、ディレクトリに権限がないため書き込むことができない。

$ cd /mnt/working/
$ echo "Hello, World" > greet-docker.txt
bash: greet-docker.txt: Permission denied

毎回 sudo するという手もなくはないけど、めんどくさい。

コンテナで /etc/passwd/etc/group をマウントしてみる

先ほどの問題を解決すべく、コンテナから /etc/passwd/etc/group をマウントしてみる。 コンテナ側からは書き換えできないように、念のため ro (Read Only) フラグをつけておく。 /home についてはお好みで。

$ cat << 'EOF' > docker-compose.yml
version: "3"
services:
  example:
    image: ubuntu:18.04
    volumes:
      - /etc/passwd:/etc/passwd:ro
      - /etc/group:/etc/group:ro
      - /home:/home
      - ./:/mnt/working
EOF

起動時に -u オプションで実行時のユーザの UID と GID を指定できる。 次のようにすれば現在のユーザをそのまま引き継げる。

$ sudo docker-compose run \
    -u "$(id -u $USER):$(id -g $USER)" \
    --rm \
    example

コンテナ内でも、ホスト側と同じユーザとして見えることがわかる。

$ whoami
vagrant
$ id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)

これは、ユーザとグループに関する情報がコンテナとホストで共有されているため。

$ grep vagrant /etc/passwd 
vagrant:x:1000:1000:,,,:/home/vagrant:/bin/bash

先ほどと同じようにマウントされているディレクトリに移動してファイルを作ってみよう。 コンテナとホストで UID と GID が一致しているため、ちゃんと書き込むことができている。

$ cd /mnt/working/
$ echo "Hello, World" > greet-docker.txt
$ ls -lF 
total 20
-rw-rw-r-- 1 vagrant vagrant 324 Sep 19 09:18 Dockerfile
-rw-rw-r-- 1 vagrant vagrant 183 Sep 19 11:08 docker-compose.yml
-rw-r--r-- 1 root    root     13 Sep 19 09:14 greet-docker-root.txt
-rw-rw-r-- 1 vagrant vagrant  13 Sep 19 11:11 greet-docker.txt
-rw-rw-r-- 1 vagrant vagrant  13 Sep 19 09:10 greet.txt

今回のやり方では、コンテナのプラットフォーム間でのポータビリティは低下するというデメリットがある。 もし、プラットフォーム間のポータビリティも落としたくない場合は、ENTRYPOINT のスクリプトでユーザを編集する必要があるだろう。 とはいえ、そこを割り切って手っ取り早く解決する方法の一つとしては使い所を選べばありだと感じた。

参考

qiita.com