概要

dockerを使っていると外部から読み込んだフォルダに書き込みをしようとするとパーミッションエラーになります。

UIDが絡んでいることはわかっていますが、いまいちなぜそうなるのかわからなかったので調べてみた結果をメモしておく。

検証環境 (Docker)

下記の構成でテストしてみる。

├ docker-compose.yml
├ httpd
│  └ Dockerfile 
└ www                 ← DocumentRootフォルダ

ApacheとPHPのコンテナを用意します。

docker-compose.yml
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
httpd/Dockerfile
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をまとめて定義できるようなので試してみる。

docker-compose.yml
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を実行ユーザーのものをセットするしかないかなと試してみる。

docker-compose.yml
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」ファイルを作成して実行してみる。

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が異なっても動くことが理解できていなくて気持ち悪い感じがします。
(まだ、完全には理解できていない感じがする・・・)

参考サイト

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.