Dockerのパーミッションエラーを調べる

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

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

## 検証環境 (Docker)
下記の構成でテストしてみる。

```ファイル構成
├ docker-compose.yml
├ httpd
│  └ Dockerfile 
└ www                 ← DocumentRootフォルダ
```

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

```yaml: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が異なるよう下記３ユーザーで試す。

* vagrant (デフォルトユーザー)
* hoge (uid:1000)
* fuga (uid:1001)

## ユーザー作成とUIDの確認

**vagrantユーザー(UID:500)**

```bash:ターミナル
$ id
uid=500(vagrant) gid=500(vagrant) groups=500(vagrant),497(docker) ...
```

**hogeユーザー(UID:1000)**

```bash:ターミナル
$ 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)**

```bash:ターミナル
$ 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を確認してみる
上記３つのユーザーでコンテナを起動し、wwwフォルダのUIDとGIDを確認してみる。

結果は、<font color=red>実行ユーザーのUIDとGIDに依存する</font>。
( まあ、それぞれのユーザーが作成したフォルダだから普通に考えればそうだよね )

**vagrantユーザー**
wwwフォルダのUID:GIDは、「<font color=red>500:500</font>」のようです。

```bash:ターミナル
$ 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は、「<font color=red>1000:1000</font>」のようです。

```bash:ターミナル
$ 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は、「<font color=red>1001:1001</font>」のようです。

```bash:ターミナル
$ 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コンテナのソースを確認する限りでは、<font color=red>www-data</font>というユーザーで実行されているっぽい
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は「<font color=red>33:33</font>」のようです。

```
$ 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をまとめて定義できるようなので試してみる。

```yaml: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フォルダは、強制的に「<font color=red>root:root</font>」になるようです。

```bash:ターミナル
$ 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を動的に変更する）

下記記事を参考に

* [docker composeでMySQLのデータ領域をローカルにマウントする](https://blog.leko.jp/post/how-to-mount-data-volume-to-local-with-docker-compose/)

起動時に、UID:GIDを実行ユーザーのものをセットするしかないかなと試してみる。

```yaml: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コマンド実行前に、定義が必要なため、下記のように実行する。

```bash:ターミナル
$ export UID && \
  export COMPOSE_PROJECT_NAME=web && \
  docker-compose up -d
```

コンテナの起動状況を確認する

```bash:ターミナル
$ 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ユーザー)

```bash:ターミナル
$ 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: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が異なっても動くことが理解できていなくて気持ち悪い感じがします。
（まだ、完全には理解できていない感じがする・・・）

## 参考サイト
* [Dockerで実行ユーザーとグループを指定する](https://qiita.com/acro5piano/items/8cd987253cb205cefbb5)
* [docker composeでMySQLのデータ領域をローカルにマウントする](https://blog.leko.jp/post/how-to-mount-data-volume-to-local-with-docker-compose/)