RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Dockerのvolumeでpermission deniedが発生した場合の解決法

f:id:dd_fortran:20200825135120p:plain

はじめに

こんにちは。dd_fortです。
前回に引き続き、Dockerについての話になります。
Dockerの学習中に詰まった権限についての問題と、その解決法を紹介します。

ボリューム(Data Volume)とは

ボリュームとは、データを永続化できる場所を指します。
Dockerのコンテナ内部のデータはコンテナ破棄をすると消えてしまうため、 データを永続化させるための手段として ボリューム(volume) が存在します。

コンテナ内部の永続化の詳しい内容については、こちらの記事を参照ください。

tech-blog.rakus.co.jp

permission denied が発生する問題

Linux上でDockerコンテナ内でボリュームをマウントした際に、ホストからアクセスした場合に パーミッションの問題が発生することがあります。

ホストOSで使っているユーザとコンテナ内で使っているユーザのUIDとGIDが不一致になることが原因のようです。 そのため、Docker for Mac/Windowsではほとんど発生しません。

$ id
uid=1000(test) gid=1000(test) groups=0(test)

# (コンテナ内)
# id
uid=0(root) gid=0(root) groups=0(root)

発生する環境

解決法

解決法1:マウントしたボリュームの権限を書き換える

permission deniedが発生したディレクトリ、ファイルの権限を直接書き換えることで解決することができます。

$ chmod 777 /var/project

しかし、permission denied が発生しているファイル、フォルダのすべての権限を書き換えないといけないため、根本的な解決にはなりません。

解決法2:ユーザ情報の書かれたファイルを読み込み専用でマウントする

コンテナ内のUIDとGIDがホストOSと同じになるように、コンテナ起動時にUIDとGIDを指定します。
また、/etc/passwdとの不整合が起きないようにホストOSの/etc/passwdをマウントする必要があります。
最後に、コンテナから/etc/passwdを書き換えないようにread only (:ro) でマウントします。

$docker run \
    -u "$(id -u $USER):$(id -g $USER)" \
    -v /etc/passwd:/etc/passwd:ro \
    -v /etc/group:/etc/group:ro \
    -it ubuntu

実行結果

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

# (コンテナ内)
test@hogehoge:~$ id
uid=1000(test) gid=1000(test) groups=1000(test)

問題なくコンテナ内とホストOSでユーザ情報が共通化されていることが確認できました。
ただしこの方法のデメリットとして、別プラットフォーム間ではDockerファイル等を共有することができなくなります。

解決法3:コンテナ作成時にユーザとグループを追加する

ホストOSのユーザ(UID)、グループ(GID)を環境変数として渡し、コンテナ内でユーザとグループを追加します。

# Dockerfile

FROM ubuntu:latest

RUN apt update \
    && apt -yq dist-upgrade \
    && apt install -yq --no-install-recommends \
    sudo 

COPY entrypoint.sh /var/tmp
CMD bash -E /var/tmp/entrypoint.sh && /bin/bash

コンテナが終了しないように/bin/bashで対話モードで動かし続けます。

# docker-compose.yml

version: "3"
  
services:
  override:
    image: example/override-ids
    build: .
    container_name: override
    environment:
      - USER_NAME
      - USER_ID
      - GROUP_NAME
      - GROUP_ID
    volumes:
      - ./:/mnt/working
    tty: true

環境変数で、ユーザ(UID)、グループ(GID)を渡すように設定します。

# entrypoint.sh

useradd -s /bin/bash -m ${USER_NAME}
export HOME=/home/${USER_NAME}
usermod -u ${USER_ID} ${USER_NAME}
groupadd -g ${GROUP_ID} ${GROUP_NAME} 
usermod -g ${GROUP_NAME} ${USER_NAME}

useraddgroupaddでユーザとグループを追加。 usermodでユーザとグループを設定。

コンテナをビルドして実行します。

$ docker-compose build

$ USER_NAME=$(id -un) \ USER_ID=$(id -u) \
GROUP_NAME=$(id -gn) \ GROUP_ID=$(id -g) \
sudo -E docker-compose up -d

実行結果

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

$ docker exec -it override su - $(id -un)
test@hogehoge:~$ id
uid=1000(test) gid=1000(test) groups=1000(test)

まとめ

Dockerのコンテナ内にvolumeをマウントする際は、UID/GIDを正しく設定しなければなりません。
簡単な手段としての解決法は1,2ですが、解決法3を使うとほとんどの問題を解決することができます。
Dockerではまだまだ勉強を始めたばかりなので学習を進めていこうと考えています。

Copyright © RAKUS Co., Ltd. All rights reserved.