Dockerfile内でchownが効かない場合に疑うべき環境について

5 min read読了の目安(約5300字

該当する方は誰もいない可能性がありますが、もし同じことで困っている人が今後現れたときの助けになればと思って、失敗談を残しておきます。

何が起こったか

Dockerfile 内で任意のディレクトリやファイルに対してchownコマンドでコンテナ内に存在するどのユーザーの権限にもならない。

RUN useradd app && mkdir /app && chown app /app のような記述でイメージのビルドをすると、エラーにならずビルドは正常終了しているのに、コンテナ内のオーナーは root のままになってしまう。

環境

$ docker -v
Docker version 20.10.5, build 55c4c88
$ docker-compose -v
docker-compose version 1.27.4, build 40524192
$ uname -a
Linux 5.4.0-66-generic #74~18.04.2-Ubuntu SMP Fri Feb 5 11:17:31 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

これ以外の重要な情報として、
「Dockerのデータディレクトリ (data-root) を、OSが起動しているディスクとは 別の マウントしたディスクに指定している」
ことが前提となります。

「data-rootを別のディスクに指定している」とは

ここでは仮に以下のような構成だとします。

  • SSDのディスクが2つ存在して、OSから認識されている。
  • ディスクsdaにOSがインストールされている
  • ディスクsdbが /docker にマウントされている

dockerの設定

/etc/docker/daemon.json の内容は以下のようになっています。

{
  "data-root": "/docker",
  "userns-remap": "1000:1000"
}

data-root

$ sudo ls -al /docker/
drwx-----x  5 root root 4096  311 23:12 .
drwxr-xr-x 72 root root 4096  311 18:12 ..
drwx-----x 13 root root 4096  311 23:12 1000.1000

Dockerfile の記述

RUN useradd -m app
RUN mkdir -p /app && chown -R app /app

RUN apt-get update -y \
    && apt-get install -y libssl-dev pkg-config locales \
    && locale-gen ja_JP.UTF-8

RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"

RUN apt-get update \
  && apt-get install -y -q \
     ca-certificates \
     locales \
     git \
     wget \
     default-libmysqlclient-dev \
     curl \
     gnupg \
     apt-transport-https\
     libssl-dev \
     pkg-config \
     python3 \
     python3-pip \
  && echo "ja_JP UTF-8" > /etc/locale.gen \
  && locale-gen

USER app

WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install --upgrade pip && pip3 install -r requirements.txt

ビルドすると以下のようなエラーが出力されます。

Step 14/22 : COPY requirements.txt requirements.txt
 ---> a78fdd6ebc1b
Step 15/22 : RUN pip3 install --upgrade pip && pip3 install -r requirements.txt
 ---> Running in ad84b43d16f3
The directory '/home/app/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/app/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting pip
  Downloading https://files.pythonhosted.org/packages/fe/ef/60d7ba03b5c442309ef42e7d69959f73aacccd0d86008362a681c4698e83/pip-21.0.1-py3-none-any.whl (1.5MB)
Installing collected packages: pip
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/home/app/.local'
Check the permissions.

/home/app 以下のパーミッションはどうなってるんだ、となるので pip3 installの上の行に
RUN ls -al /home/app を追記してビルドしてみると

Step 14/23 : RUN ls -al /home/app
 ---> Running in e20f0febf9a8
total 20
drwxr-xr-x 2 root root 4096  311 14:50 .
drwxr-xr-x 1 root root 4096  311 14:50 ..
-rw-r--r-- 1 root root  220  418  2019 .bash_logout
-rw-r--r-- 1 root root 3526  418  2019 .bashrc
-rw-r--r-- 1 root root  807  418  2019 .profile

root になってますね。

RUN ls -al /home/app の上の行に以下を追記してビルドしてみます。

USER root
RUN chown -R app /home/app

すると、

Step 14/25 : USER root
 ---> Running in 3e9229fd60a8
Removing intermediate container 3e9229fd60a8
 ---> 341a4cba25d9
Step 15/25 : RUN chown -R app /home/app
 ---> Running in a71e943328d2
Removing intermediate container a71e943328d2
 ---> 28db48b6506b
Step 16/25 : RUN ls -al /home/app
 ---> Running in f93a22e927a6
total 20
drwxr-xr-x 1 root root 4096  311 14:50 .
drwxr-xr-x 1 root root 4096  311 14:50 ..
-rw-r--r-- 1 root root  220  418  2019 .bash_logout
-rw-r--r-- 1 root root 3526  418  2019 .bashrc
-rw-r--r-- 1 root root  807  418  2019 .profile
Removing intermediate container f93a22e927a6
 ---> 8c296aecc075

エラーにならず正常に終了していますが、 ls -al の出力を見るとやはり root のままになっています。

参考にした記事

https://dreamlab.net/en/blog/post/user-namespace-remapping-an-advanced-feature-to-protect-your-docker-environments/

下の方にある

Limitations

The following standard Docker features are incompatible with running a Docker daemon with user namespaces enabled:

  • Sharing PID or NET namespaces with the host (--pid=host or --network=host).
  • External (volume or storage) drivers which are unaware or incapable of using daemon user mappings.
  • Using the --privileged mode flag on docker run without also specifying --userns=host.

External (volume or storage) drivers which are unaware or incapable of using daemon user mappings. (デーモンユーザーマッピングを認識しない、または使用できない外部(ボリュームまたはストレージ)ドライバー。)

この1行を見たときに「あれ・・・もしかして」と気づいたのは、調査を開始してから3時間経ったときでした。。

解決

/etc/docker/daemon.json の内容は以下のように変更して dockerd を再起動すると、上記のような不思議な挙動はなくなりました。

{
  "data-root": "/var/lib/docker",
  "userns-remap": "1000:1000"
}

または、 data-root を指定しないことで、デフォルトの /var/lib/docker 以下にイメージが置かれます。

{
  "userns-remap": "1000:1000"
}

そもそも、なんで別のディスクに指定していたの??

dockerってめちゃくちゃディスクの容量を逼迫しません??
ちまちまとイメージを消したりファイルの整理をしたりゴミ箱を空にしたり、やっててもすぐに「ディスクがいっぱいです」ってなりません??
なので 2TB の SSD を鼻息荒く購入したんですよ。
2TB のディスクに docker のお世話をしてもらいたかったんです・・