Dockerコンテナと共有するファイルのオーナー問題
LinuxなホストとDockerコンテナとのファイル共有について。
- Linuxのプロセスは、特定のユーザID(uid)と特定のグループID(gid)に紐づく
- プロセスが生成するファイルのオーナーは、プロセスに紐付いたuid/gidで決まる
- Dockerコンテナ内のuid/gid体系は、ホスト側とは別物
以上のことから、コンテナとファイル共有するとき、コンテナ側で作成したファイルにホスト側からアクセスできない、といった問題が生じる。
解決策の1つとして、「コンテナを起動するたびに、自分と同じuid/gidを持ったアカウントを生成する」という作戦があるので、まとめておく。
概要
- コンテナは
root
で開始 - シェルスクリプトを使って新しいアカウントを生成し、そいつへ
su
する - 新しいアカウントには、ホスト側のアカウントと同じuid/gidをセットする
uid/gid
ホスト側のuid/gidなどをコンテナ側へ伝える手段として、環境変数がパッと思いつく。
HOST_GID=`id -g`
HOST_UID=`id -u`
uidに比べるとgidは融通がきくので、専用のグループ(docker
とか)を作っても良いだろう。
HOST_GID=`getent group docker |cut -d : -f 3`
しかしもっと簡単なのは、共有するディレクトリのオーナー情報を使う方法だ。共有するディレクトリはホスト側が作ったものなので、たとえコンテナ側から見たときでも、それはホスト側のuid/gidになる。仮に/tmp/share
をホストと共有するとしたら、こんな感じ:
SHARED_DIR=/tmp/share
HOST_UID=$(stat -c %u $SHARED_DIR)
HOST_GID=$(stat -c %g $SHARED_DIR)
%U
や%G
を使えば、ユーザ名やグループ名も分かる。もしUNKNOWN
なら、それはホスト側にしか存在しないということになる。UNKNOWN
以外だったら、ホスト側と同じuid/gidが、(偶然、)コンテナ側にも存在するということなので、新規に作る必要は無い。
シェルスクリプト
ユーザ名やグループ名までホスト側と合わせる必要な無いと思われる。もし合わせたい場合は、環境変数経由で渡してもらえばいい。
またAlpine linuxでは、新規ユーザ/新規グループを作るコマンドが特殊だ。
dk_user.sh(alpine以外)
#!/bin/bash
if [ -z $SHARED_DIR ]; then
SHARED_DIR=$PWD
fi
HOST_UID=$(stat -c %u $SHARED_DIR)
HOST_GID=$(stat -c %g $SHARED_DIR)
DK_GROUP=$(stat -c %G $SHARED_DIR)
if [ $DK_GROUP = UNKNOWN ]; then
DK_GROUP=dk_group
groupadd --gid $HOST_GID $DK_GROUP
fi
DK_USER=$(stat -c %U $SHARED_DIR)
if [ $DK_USER = UNKNOWN ]; then
DK_USER=dk_user
useradd --home-dir /home/$DK_USER --gid $HOST_GID \
--uid $HOST_UID --shell /bin/bash $DK_USER
fi
su $DK_USER
dk_user.sh(alpine)
#!/bin/sh
if [ -z $SHARED_DIR ]; then
SHARED_DIR=$PWD
fi
HOST_UID=$(stat -c %u $SHARED_DIR)
HOST_GID=$(stat -c %g $SHARED_DIR)
DK_GROUP=$(stat -c %G $SHARED_DIR)
if [ $DK_GROUP = UNKNOWN ]; then
DK_GROUP=dk_group
addgroup -g $HOST_GID $DK_GROUP
fi
DK_USER=$(stat -c %U $SHARED_DIR)
if [ $DK_USER = UNKNOWN ]; then
DK_USER=dk_user
adduser -h /home/$DK_USER -G $DK_GROUP \
-u $HOST_UID -s /bin/sh -D $DK_USER
fi
su $DK_USER
Dockerfile
シェルスクリプトは、PATH上の適当な場所へコピっておく。
COPY dk_user.sh /usr/local/bin/
コンテナ
コンテナを開始するときに、シェルスクリプトを実行する。
デフォルトでは、コンテナの(初期の)作業ディレクトリがSHARED_DIR
として使われる。作業ディレクトリが共有フォルダじゃない場合は、明示的にSHARED_DIR
を教えてやる必要がある。
$ docker run --rm -it \
--env "SHARED_DIR=/tmp/share" \
--volume $(shell pwd):/tmp/share \
--name my_container \
my_image dk_user.sh