Dockerを使って一つのホストでPHP7.0(RC)とPHP5.6を動かす

一つのホストで複数のPHPのバージョンを利用してみたいという場合にDockerを使うと簡単にできそうだったのでメモ。

# 環境

+ AmazonLinux(2015.09)
+ nginx1.9.5(DockerHub公式リポジトリ)
+ php-fpm7.0.0RC-fpm(DockerHub公式リポジトリ)
+ php-fpm5.6.14(DockerHub公式リポジトリ)

# 参考

+ [周回遅れのDocker 2: Docker による PHP サーバ](http://qiita.com/kumatch/items/7ea3bd4f87237980a399)
+ [DockerHub PHP official repository](https://hub.docker.com/_/php/)
+ [DockerHub nginx official repository](https://hub.docker.com/_/nginx/)

# 準備

以降、EC2にsshして実行

```bash
# dcokerをインストール
$sudo yum install docker -y

#dockerグループに追加
$sudo usermod -g docker ec2-user

# 一度ログアウト
$exit

# 再度sshログイン後、サービス起動
$sudo service docker start
```

# nginxでの静的なファイルのホスティング

まずは、nginxのコンテナを使ってホストのhtmlファイルをマウントし、表示してみます。


```bash
# hello worldコンテンツ作成
$mkdir -p ~/www/html/
$echo "hello in nginx container" >> ~/www/html/index.html

# nginx1.9.5のコンテナの取得
$docker pull nginx:1.9.5

# 確認
$docker images

# hello worldコンテンツをマウントして起動する
$docker run --name hello-nginx -v /home/ec2-user/www/html/:/usr/share/nginx/html:ro -d -p 80:80 nginx:1.9.5

$ 確認
$curl http://localhost
hello in nginx container
```

# PHP-fpmとの連携

php-fpm7.0.0のコンテナを同一ホストに起動し、nginxのコンテナとlinkして接続を行うようにします。

nginxの設定ファイルを変えてFastCGIを利用するようにする必要がありますが、デフォルトでどのような設定になっているか確認します。

```bash
$docker cp hello-nginx:/etc/nginx/nginx.conf docker-nginx.conf
```

```/etc/nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
```

/etc/nginx/conf.d配下の設定ファイルを読み込んでいるのでこちらを確認します。


```bash
$docker cp hello-nginx:/etc/nginx/conf.d conf.d
```


```lang:/etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}
    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
```

なるほど。このdefault.confを変えれば良さそうですね。

コピーした内容を参考に変更します。

```default.conf
server {
    listen       80;
    server_name  localhost;

    location ~ \.php$ {
        root           /var/www/html/;
        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}
```

`fastcgi_pass`ディレクティブは{host}:{port}という形で指定する必要があり、hostをphpとしています。

docker runコマンドのlinkオプションを指定した場合、起動したコンテナの`/etc/hosts`の内容にlinkしたコンテナのホストのaliasを追加するため、上記のようにコンテナ名を記載したのみでも名前解決することができ、nginxからphp-fpmのコンテナに接続することができるようになります。

```lang:/etc/hosts
172.17.0.33     1d663b45e414
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.30     php 2791c3c259d7 hello-phpfpm
```

envディレクティブとperl_setディレクティブを使って環境変数をnginx.confで読み込むことができますが、DockerHubのnginxのイメージはperl_setディレクティブの解決ができなかったので、利用する場合、独自でビルドしてそのイメージを使う必要がありそうです。（異なるホストの場合、この対応をしないとダメかと思います）

現在はphpの挙動を確認するphpスクリプトがないので作成しておきます。

```www/html/index.php
<?php
phpinfo();
```

準備ができたので、php-fpmコンテナを起動し、その後nginxのコンテナを起動します。nginxコンテナ起動時には先ほど作成したdefault.confをマウントし、php-fpmコンテナとリンクすることでコンテナ間の通信をできるようにします。


```bash
#php-fpmのイメージを取得
$docker pull php:7.0.0RC5-fpm

# php-fpmコンテナの起動。AmazonLinuxのファイルをマウント
$docker run --name hello-phpfpm -v /home/ec2-user/www/html/:/var/www/html:ro -d php:7.0.0RC5-fpm

# 先ほどのコンテナは停止、削除
$docker stop hello-nginx
$docker rm hello-nginx

# nginxコンテナの起動
$docker run --name hello-nginx -v /home/ec2-user/default.conf:/etc/nginx/conf.d/default.conf:ro -d -p 80:80 --link hello-phpfpm:php nginx:1.9.5

# 確認
$curl -s http://localhost/index.php |grep PHP_VERSION
<tr><td class="e">PHP_VERSION </td><td class="v">7.0.0RC5 </td></tr>
<tr><td class="e">$_SERVER['PHP_VERSION']</td><td class="v">7.0.0RC5</td></tr>
<tr><td class="e">$_ENV['PHP_VERSION']</td><td class="v">7.0.0RC5</td></tr>

```

無事、PHP7が利用できているようです。

# 一つのサーバーで複数のPHPバージョンを見れるようにする

現在、php7のfpm用コンテナを起動していますが、別途php5.6系のfpmコンテナを起動し、リクエストURLのパスによって同じPHPスクリプトをPHP7.0で表示するかPHP5.6で表示するかという事を実施できそうです。

nginxの設定ファイルを以下のように変更し、パスの先頭で7となっていればPHP 7.0系を56となっていればPHP 5.6系を見るようにします。

```default.conf
server {
    listen       80;
    server_name  localhost;
    root         /var/www/html/;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;

    location ~ ^/7/(.*\.php)$ {
        fastcgi_pass   php7:9000;
    }

    location ~ ^/56/(.*\.php)$ {
        fastcgi_pass   php56:9000;
    }
}
```

準備ができたので、下記を順番に実施し、同じホストから2つのPHPバージョンのコンテナを使ってみます。

```bash
# 先ほどの利用していたdockerを停止、破棄
$docker stop hello-nginx hello-phpfpm
$docker rm hello-nginx hello-phpfpm

# php5.6用のイメージ取得
$docker pull php:5.6.14-fpm

# php-fpm7を起動./var/www/html/7としてマウント
$docker run --name php-fpm7 -v /home/ec2-user/www/html/:/var/www/html/7/:ro -d php:7.0.0RC5-fpm

# php-fpm56を起動./var/www/html/6としてマウント
$docker run --name php-fpm56 -v /home/ec2-user/www/html/:/var/www/html/56/:ro -d php:5.6.14-fpm

# nginxを起動。php-fpm7とphp-fpm56をリンク
$docker run --name hello-nginx -v /home/ec2-user/default.conf:/etc/nginx/conf.d/default.conf:ro -d -p 80:80 --link php-fpm7:php7 --link php-fpm56:php56 nginx:1.9.5

# 確認
$ curl -s http://localhost/7/index.php |grep PHP_VERSION
<tr><td class="e">PHP_VERSION </td><td class="v">7.0.0RC5 </td></tr>
<tr><td class="e">$_SERVER['PHP_VERSION']</td><td class="v">7.0.0RC5</td></tr>
<tr><td class="e">$_ENV['PHP_VERSION']</td><td class="v">7.0.0RC5</td></tr>

$curl -s http://localhost/56/index.php |grep PHP_VERSION
<tr><td class="e">PHP_VERSION </td><td class="v">5.6.14 </td></tr>
<tr><td class="e">_SERVER["PHP_VERSION"]</td><td class="v">5.6.14</td></tr>
<tr><td class="e">_ENV["PHP_VERSION"]</td><td class="v">5.6.14</td></tr>
```

できました！

現在はホストのドキュメントをマウントしてますが、このデータ自体をコンテナ化してdocker runの際に`--volumes-from`を利用すればコンテナだけで環境が作成できます。