一つのホストで複数のPHPのバージョンを利用してみたいという場合にDockerを使うと簡単にできそうだったのでメモ。
環境
- AmazonLinux(2015.09)
- nginx1.9.5(DockerHub公式リポジトリ)
- php-fpm7.0.0RC-fpm(DockerHub公式リポジトリ)
- php-fpm5.6.14(DockerHub公式リポジトリ)
参考
- 周回遅れのDocker 2: Docker による PHP サーバ
- DockerHub PHP official repository
- DockerHub nginx official repository
準備
以降、EC2にsshして実行
# dcokerをインストール
$sudo yum install docker -y
#dockerグループに追加
$sudo usermod -g docker ec2-user
# 一度ログアウト
$exit
# 再度sshログイン後、サービス起動
$sudo service docker start
nginxでの静的なファイルのホスティング
まずは、nginxのコンテナを使ってホストのhtmlファイルをマウントし、表示してみます。
# 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を利用するようにする必要がありますが、デフォルトでどのような設定になっているか確認します。
$docker cp hello-nginx:/etc/nginx/nginx.conf docker-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配下の設定ファイルを読み込んでいるのでこちらを確認します。
$docker cp hello-nginx:/etc/nginx/conf.d conf.d
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を変えれば良さそうですね。
コピーした内容を参考に変更します。
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のコンテナに接続することができるようになります。
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スクリプトがないので作成しておきます。
<?php
phpinfo();
準備ができたので、php-fpmコンテナを起動し、その後nginxのコンテナを起動します。nginxコンテナ起動時には先ほど作成したdefault.confをマウントし、php-fpmコンテナとリンクすることでコンテナ間の通信をできるようにします。
#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系を見るようにします。
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バージョンのコンテナを使ってみます。
# 先ほどの利用していた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
を利用すればコンテナだけで環境が作成できます。