概要
dockerを使っていると外部から読み込んだフォルダに書き込みをしようとするとパーミッションエラーになります。
UIDが絡んでいることはわかっていますが、いまいちなぜそうなるのかわからなかったので調べてみた結果をメモしておく。
検証環境 (Docker)
下記の構成でテストしてみる。
├ docker-compose.yml
├ httpd
│ └ Dockerfile
└ www ← DocumentRootフォルダ
ApacheとPHPのコンテナを用意します。
version: '2'
services:
httpd:
build: ./httpd/
container_name: 'httpd'
ports:
- '80:80'
- '443:443'
volumes_from:
- php
php:
image: php:5.6-fpm
container_name: 'php'
working_dir: '/usr/local/apache2/htdocs'
ports:
- '9000:9000'
volumes:
- ./www:/usr/local/apache2/htdocs
FROM httpd:2.4
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i '/LoadModule proxy_module/s/^#//g' /usr/local/apache2/conf/httpd.conf
RUN sed -i '/LoadModule proxy_fcgi_module/s/^#//g' /usr/local/apache2/conf/httpd.conf
RUN { \
echo '<FilesMatch \.php$>'; \
echo ' SetHandler "proxy:fcgi://php:9000"'; \
echo '</FilesMatch>'; \
} >> /usr/local/apache2/conf/httpd.conf
検証環境 (User)
テストする環境は、VagrantでCentOS7.3を起動して行います。
UIDが異なるよう下記3ユーザーで試す。
- vagrant (デフォルトユーザー)
- hoge (uid:1000)
- fuga (uid:1001)
ユーザー作成とUIDの確認
vagrantユーザー(UID:500)
$ id
uid=500(vagrant) gid=500(vagrant) groups=500(vagrant),497(docker) ...
hogeユーザー(UID:1000)
$ sudo useradd hoge && \
sudo gpasswd -a hoge wheel && \
sudo gpasswd -a hoge docker
$ sudo su - hoge
$ id
uid=1000(hoge) gid=1000(hoge) groups=1000(hoge),10(wheel),497(docker) ...
fugaユーザー(UID:1001)
$ sudo useradd fuga && \
sudo gpasswd -a fuga wheel && \
sudo gpasswd -a fuga docker
$ sudo su - fuga
$ id
uid=1001(fuga) gid=1001(fuga) groups=1001(fuga),10(wheel),497(docker) ...
wwwフォルダのUID:GIDを確認してみる
上記3つのユーザーでコンテナを起動し、wwwフォルダのUIDとGIDを確認してみる。
結果は、実行ユーザーのUIDとGIDに依存する。
( まあ、それぞれのユーザーが作成したフォルダだから普通に考えればそうだよね )
vagrantユーザー
wwwフォルダのUID:GIDは、「500:500」のようです。
$ export COMPOSE_PROJECT_NAME=vagrant
$ docker-compose up -d
$ docker exec -it php bash -c 'cd ../ && pwd && ls -la'
/usr/local/apache2
total 0
drwxr-sr-x. 3 root root 20 Jan 7 05:28 .
drwxrwsr-x. 1 root staff 21 Jan 7 05:28 ..
drwxr-xr-x. 1 500 500 68 Jan 7 05:27 htdocs
hogeユーザー
wwwフォルダのUID:GIDは、「1000:1000」のようです。
$ export COMPOSE_PROJECT_NAME=hoge
$ docker-compose up -d
$ docker exec -it php bash -c 'cd ../ && pwd && ls -la'
/usr/local/apache2
total 0
drwxr-sr-x. 3 root root 20 Jan 7 05:33 .
drwxrwsr-x. 1 root staff 21 Jan 7 05:33 ..
drwxrwxr-x. 2 1000 1000 6 Jan 7 05:11 htdocs
fugaユーザー
wwwフォルダのUID:GIDは、「1001:1001」のようです。
$ export COMPOSE_PROJECT_NAME=fuga
$ docker-compose up -d
$ docker exec -it php bash -c 'cd ../ && pwd && ls -la'
/usr/local/apache2
total 0
drwxr-sr-x. 3 root root 20 Jan 7 05:37 .
drwxrwsr-x. 1 root staff 21 Jan 7 05:37 ..
drwxrwxr-x. 2 1001 1001 6 Jan 7 05:36 htdocs
PHPやApacheのコンテナの実行ユーザーのUID・GIDは?
公式Apacheコンテナのソースを確認する限りでは、www-dataというユーザーで実行されているっぽい
https://github.com/docker-library/httpd/blob/6d50d7f89f4d8bc6a6a3f86d892e254f4d6bfd8b/2.4/Dockerfile
FROM debian:jessie-backports
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added |
# RUN groupadd -r www-data && useradd -r --create-home -g www-data www-data |
ENV HTTPD_PREFIX /usr/local/apache2
ENV PATH $HTTPD_PREFIX/bin:$PATH
RUN mkdir -p "$HTTPD_PREFIX" \
&& chown www-data:www-data "$HTTPD_PREFIX"
...
では、確認してみるとUID:GIDは「33:33」のようです。
$ docker exec -it -u www-data httpd id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ docker exec -it -u www-data php id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
確認した結果
外からの書き込みは、500,1000,1001で、実行ユーザーは、33ということでパーミッションエラーとなるようです。
volumesを試してみる
version2では、volumesをまとめて定義できるようなので試してみる。
version: '2'
services:
httpd:
build: ./httpd/
container_name: 'httpd'
ports:
- '80:80'
- '443:443'
volumes_from:
- php
php:
image: php:5.6-fpm
container_name: 'php'
working_dir: '/usr/local/apache2/htdocs'
ports:
- '9000:9000'
volumes:
- www-data:/usr/local/apache2/htdocs
volumes:
www-data:
driver_opts:
type: none
device: ${PWD}/www/
o: bind
こちらで試すとwwwフォルダは、強制的に「root:root」になるようです。
$ docker-compose up -d
$ ls -la
合計 4
drwxrwxr-x. 3 fuga fuga 43 1月 7 14:58 .
drwx------. 3 fuga fuga 110 1月 7 14:58 ..
-rw-rw-r--. 1 fuga fuga 442 1月 7 14:58 docker-compose.yml
drwxr-xr-x. 2 root root 6 1月 7 14:36 www
このやり方は、コンテナ内からの書き出しにはいいですが、
双方向の書き込むことはできないようです。
双方向に書き込めないか検討
dockerコマンドの実行ユーザーのUIDと実行ユーザーのUIDを揃えられないかやってみる。
(実行ユーザーのUIDを動的に変更する)
下記記事を参考に
起動時に、UID:GIDを実行ユーザーのものをセットするしかないかなと試してみる。
version: '2'
services:
httpd:
build: ./httpd/
container_name: 'httpd'
ports:
- '80:80'
- '443:443'
volumes_from:
- php
php:
image: php:5.6-fpm
container_name: 'php'
command: bash -c 'usermod -o -u ${UID} www-data; groupmod -o -g ${UID} www-data; php-fpm'
working_dir: '/usr/local/apache2/htdocs'
ports:
- '9000:9000'
volumes:
- ./www:/usr/local/apache2/htdocs
UIDはシェル変数なので、docker-compose upコマンド実行前に、定義が必要なため、下記のように実行する。
$ export UID && \
export COMPOSE_PROJECT_NAME=web && \
docker-compose up -d
コンテナの起動状況を確認する
$ docker-compose ps
httpd httpd-foreground Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
php docker-php-entrypoint bash ... Up 0.0.0.0:9000->9000/tcp
UIDの状況を確認する (fugaユーザー)
$ docker exec -it -u www-data php id
uid=1001(www-data) gid=1001(www-data) groups=1001(www-data)
$ docker exec -it -u www-data httpd id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
phpコンテナのwww-dataユーザーのUIDが1001:1001になったのを確認できました。
wwwフォルダに「touch.php」ファイルを作成して実行してみる。
<?php
$file = 'file.txt';
if (touch($file)) {
echo __DIR__ . '/' . $file;
}
ブラウザでアクセスしてみる
http://192.168.33.10/touch.php
UIDを調整してない場合
Warning: touch(): Unable to create file file.txt because Permission denied in /usr/local/apache2/htdocs/touch.php on line 3
UIDを調整した場合
/usr/local/apache2/htdocs/file.txt
書き込みできたようです。
まとめ
なんとなく、パーミッションエラーの原因がわかってきた感じはしますが、
解消方法が力技って感じになってしまったので、他にいい方法があればいいのだが・・・
また、Apacheのwww-dataユーザーとPHPのwww-dataユーザーのUIDが異なっても動くことが理解できていなくて気持ち悪い感じがします。
(まだ、完全には理解できていない感じがする・・・)