周回遅れのDocker 2: Docker による PHP サーバ

https://registry.hub.docker.com/_/php/

Docker 公式の PHP イメージを使って Web サーバに相当するコンテナを作ってみる。今回のコンテナ例では立ち上げると phpinfo() を出力する PHP Web サーバを提供することとする。

また -v (--volume) オプションを使って、ローカルディスクの領域を Web サーバのドキュメントルートとしてマウントした際の挙動もあわせて確認する。

なお以下の例で実行している docker コマンドは、docker-machine で作られたホストに対して行っており、事前に `docker-machine env` による環境変数が設定済みの状態。




## Apache + PHP

### コンテナイメージの準備

まず PHP の phpinfo() を出力するファイルを用意。

```php:public/info.php
<?php
phpinfo();
```

続けて image ビルドのための Dockerfile を用意。ここでは、 Apache モジュールが同梱された PHP コンテナイメージをベースとして使う。
あわせて、先ほどの PHP ファイルをコンテナイメージに同梱するよう `COPY` を使ってローカルファイルをコンテナに含めるようにする。コピー先は、ベースとなるコンテナが提供している仕様に基づくディレクトリとする。すなわち今回利用しているベースコンテナの php:apache の Apache がドキュメントルートとして利用しているディレクトリで、ここでは `/var/www/html` となっている。

```bash:Dockerfile
FROM php:apache
COPY ./public/info.php /var/www/html/
```


`docker build` コマンドを使って、新たな自前 Docker イメージを作成する。
Dockerfile で定義した利用コンテナイメージのダウンロードが行われ、それを基にして新規イメージが作られる。

```bash
$ docker build -t sandbox-phpinfo-apache .
```

`docker images` コマンドを実行すると、先ほど作った新規イメージが利用可能な状態であることがわかる。

```bash
$ docker images

REPOSITORY               TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
sandbox-phpinfo-apache   latest              5cfc1b74c92a        42 seconds ago      481 MB
php                      5.6-apache          385019d6807e        12 days ago         481 MB
```

`docker run` コマンドでこの自前 Docker イメージからコンテナを実行する。
-p オプションはポートフォワード設定で、Docker ホストへのアクセスに対してコンテナへ流す設定を行っている。

```bash
$ docker run --name phpinfo -p 80:80 -d sandbox-phpinfo-apache
fe055eccf5d24cc1fa5cec7940a6e619480dc6b091e0b83a8f0217007a795de6
```


### 動作確認

起動した Apache + PHP コンテナイメージに対してアクセスしてみる。
まずは Docker ホストの IP アドレスを `docker-machine ip` コマンドでチェック。

```bash
$ docker-machine ip dev

192.168.99.100
```

得られたアドレスを使って Docker ホストへブラウザでルートアクセスすると、phpinfo() が表示される。

![スクリーンショット 2015-06-29 23.52.58.png](https://qiita-image-store.s3.amazonaws.com/0/18511/d90c6cf5-3b6f-6bdc-1168-75a9910dbcdb.png "Apache + PHP の phpinfo()")


### ローカルディレクトリをマウントして Web サーバを動かす

続けて、ローカルの public ディレクトリを先ほどのコンテナの Apache ドキュメントルートとしてマウントして動かしてみる。

ローカルには先ほどの phpinfo() 出力プログラムとは違う内容のものを用意。

```php:public/index.php
<?php
echo "Hello, world: ", time();
```


先ほどのコンテナを一旦止めて、-v オプションで public ディレクトリをドキュメントルートへマウントする。

```bash
$ docker ps
CONTAINER ID        IMAGE                    COMMAND                CREATED             STATUS              PORTS                NAMES
fe055eccf5d2        sandbox-phpinfo-apache   "apache2-foreground"   7 minutes ago       Up 7 minutes        0.0.0.0:80->80/tcp   phpinfo

$ docker stop phpinfo
phpinfo

$ docker run --name phpinfo2 -v /path/to/sandbox-docker-phpinfo-apache/public:/var/www/html -p 80:80 -d sandbox-phpinfo-apache
40b99a767cbc2fbddb90d22fdc9e70296401cbce625c6133c355b455bbb2f0c7
```

再びブラウザでルートアクセスすると、今度は phpinfo() ではなく、`public/index.php` に書かれた Hello, world プログラムの結果が表示される。また、/info.php にアクセスすると phpinfo() が表示される。以上より、マウントした public ディレクトリがコンテナ内 Apache のドキュメントルートになって稼働していることがわかる。



## Nginx + PHP-FPM

続いてもう１つ PHP サーバを動かすスタイルとしてよく見られる Nginx と PHP-FPM を組み合わせたサーバを Docker で作ってみる。ここでは、Nginx コンテナと PHP-FPM コンテナの２つを用意して、Nginx コンテナからの Proxy アクセスで PHP アプリケーションが利用できるようにする。


### PHP-FPM コンテナ

php-fpm の Docker イメージをベースにしてコンテナを作成。あわせて phpinfo() を表示する PHP ファイルを先ほどの Apache のときと同じようにコピーする。

```bash:phpfpm/Dockerfile
FROM php:fpm
COPY index.php /var/www/html/
```

```php:phpfpm/index.php
<?php
phpinfo();
```

イメージの作成とコンテナ起動。ここでは phpfpm という名称で起動した。
php:fpm の Docker コンテナな PHP-FPM を tcp:9000 で待ち受けするとのことだが、Nginx からの Proxy アクセスのみを考慮しているので、Docker 起動時のポートマッピングオプションは必要なしになる。

```bash
$ docker build -t sandbox-phpinfo-phpfpm ./phpfpm
$ docker run --name phpfpm -d sandbox-phpinfo-phpfpm
```


### Nginx コンテナ

[Nginx イメージ](https://registry.hub.docker.com/_/nginx/) を使う。
PHP-FPM への Proxy アクセスのためのサーバ設定ファイルを用意して、コンテナをビルドする際に設定ファイルディレクトリへファイルを埋め込む。

```nginx/Dockerfile
FROM nginx
ADD server.conf /etc/nginx/conf.d/server.conf
```

Proxy 設定ファイルは以下のとおり。
ここでポイントなのが、Proxy 先となる PHP-FPM のホストを 127.0.0.1 などの IP, hostname ではなく、Docker のコンテナ間接続のための機構である **link** オプションによるエイリアス名を指定することになる。ここでは `php` という名称にした。 ("fastcgi_pass php:9000;" の箇所)

```nginx/server.conf
server {
    listen 80 default;
    server_name _;
    root /var/www/html;
    index index.php index.html index.htm;
    charset utf-8;

    access_log off;
    error_log off;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

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


イメージの作成とコンテナ起動。
前述したコンテナ間接続のため、link オプションで <コンテナ名>:<エイリアス名> を指定する。phpfpm コンテナに対して Nginx 設定ファイル内で php というエイリアス名を使っているので、 `--link phpfpm:php` となる。

```bash
$ docker build -t sandbox-phpinfo-nginx ./nginx
$ docker run --name nginx -p 80:80 --link phpfpm:php -d sandbox-phpinfo-nginx
```

### 動作確認

Apache + PHP の時と同様、Docker ホストへブラウザでルートアクセスすると、PHP-FPM による phpinfo() が表示される。

![スクリーンショット 2015-06-30 22.50.40.png](https://qiita-image-store.s3.amazonaws.com/0/18511/7deb0a0e-368e-2d51-4b23-65b5b6ee6e59.png "スクリーンショット 2015-06-30 22.50.40.png")


## サンプルソース

今回利用したサンプルソース。

* [Apache + PHP](https://github.com/kumatch/sandbox-docker-phpinfo-apache/tree/1.0.0)
* [Nginx + PHP-FPM](https://github.com/kumatch/sandbox-docker-phpinfo-nginx-phpfpm/tree/1.0.0)