Docker で nginx + PHP-FPM コンテナを作る方法
近年 Web サーバーは、Apache の代わりに nginx もよく使用されています。 お仕事で使用している方も多いのではないでしょうか。 nginx から PHP を使用したい場合、FPM(FastCGI Process Manager)版の PHP が必要になります。 Docker で nginx + PHP-FPM コンテナを作成してみましょう。
Docker で nginx コンテナを作る
コンテナのイメージは、下記の公式イメージを使用します。
イメージのバージョン(タグ)はいろいろありますが、特にこだわりが無いなら最新版(latest)で良いと思います。
nginx は Web サーバーですので、コンテナ側のポート番号は 80 番です。 ホスト側から Web ブラウザ(Chrome など)で接続できるよう、コンテナ側のポート 80 番へのマッピングを設定しましょう。 ホスト側のポート番号は、他で使用されていないものなら何番でも良いと思います。
ドキュメントルートはデフォルトだと "/usr/share/nginx/html" になります。 ホストのディレクトリとバインドマウントして、そこに HTML ファイルを置いて動作確認してみましょう。
version: "3.8"
services:
# nginx コンテナ
nginx:
image: nginx:latest
ports:
- 8080:80
volumes:
- ./html:/usr/share/nginx/html
nginx の設定ファイルを使用する
nginx の設定を変更したい場合は、nginx の設定ファイルを使用すると良いでしょう。
nginx の設定ファイルの詳しい書き方はここでは記載しませんが、
基本的な書き方としては {} で囲ってブロックになること、
終端には ; が必要になること、
# で1行コメントになることを覚えておくと良いでしょう。
nginx の設定ファイルの場所は "/etc/nginx" ディレクトリの配下です。 nginx の設定ファイルは複数のファイルで構成されています。 全体の設定は "/etc/nginx/nginx.conf" ファイルで、 そこからサーバーの設定として "/etc/nginx/conf.d" ディレクトリ配下の ".conf" ファイルが読み込まれるようになっています。
nginx の全体の設定である "/etc/nginx/nginx.conf" の方には大事な設定が書かれているので、変更する際は注意が必要です。 基本的にはサーバーの設定の方を変更する機会が多いと思いますので、そちらのファイルを変更しましょう。 デフォルトのサーバーの設定のファイルは "/etc/nginx/conf.d/default.conf" です。
(省略)
events {
(省略)
}
http {
(省略)
include /etc/nginx/conf.d/*.conf;
}
server {
listen 80;
(省略)
}
ドキュメントルートを変更する
デフォルトのサーバーの設定のファイルを変更してドキュメントルートを変更してみましょう。
ドキュメントルートを変更するには root を使用します。
server {
# ドキュメントルート
root /path/to/new/root;
}
バインドマウントを使用する
nginx の設定ファイルを頻繁に変更するなら、バインドマウントを使用すると良いでしょう。 なお、nginx の設定ファイルを変更した場合は、反映させるためにコンテナの再起動(正確には nginx の再起動)が必要になるので注意してください。
version: "3.8"
services:
# nginx コンテナ
nginx:
image: nginx:latest
ports:
- 8080:80
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf
- ./html:/path/to/new/root
Dockerfile を使用する
nginx の設定ファイルをあまり変更しないのなら、バインドマウントではなく Dockerfile を使用すると良いでしょう。
Dockerfile の中で、nginx の設定ファイルをコンテナにコピーします。
ついでに chmod コマンドでパーミッションを設定しておくと良いでしょう。
なお、nginx の設定ファイルを変更した場合は、反映させるためにコンテナをビルドし直す必要があります。
FROM nginx:latest # nginx のデフォルトのサーバーの設定ファイルを配置 COPY ./default.conf /etc/nginx/conf.d/default.conf RUN chmod 644 /etc/nginx/conf.d/default.conf
version: "3.8"
services:
# nginx コンテナ
nginx:
build: ./nginx-custom
ports:
- 8080:80
volumes:
- ./html:/path/to/new/root
Docker で PHP-FPM コンテナを作る
HTML、CSS、JavaScript、画像ファイルなどの静的なファイルは nginx だけで処理を行うことができますが、PHP ファイルは実行することができません。 nginx から PHP を使用するためには、FPM(FastCGI Process Manager)版の PHP が必要になります。 PHP が使用できるように PHP-FPM のコンテナを作成してみましょう。
コンテナのイメージは、下記の PHP の公式イメージを使用します。
イメージのバージョン(タグ)はいろいろありますが、今回は PHP 8.2 の FPM 版である 8.2-fpm を使用します。
PHP のバージョンを変更したい場合は、別のタグに変更してください(例:PHP 8.1 の場合は 8.1-fpm)。
なお、PHP-FPM コンテナに PHP のエクステンションをインストールする方法や、PHP の設定ファイルを使用する方法は、 「Docker で Apache + PHP コンテナを作る方法」に記載している方法と同じですので、 そちらを参考にしてください。
PHP-FPM コンテナには、nginx コンテナから PHP ファイルのリクエストが来ますので、実行したい PHP ファイルを置いておく必要があります。 バインドマウントなどを使用して、PHP ファイルを配置しておきましょう。 配置場所は nginx のドキュメントルートと同じにしておくとわかりやすくて良いと思います。
nginx コンテナからのリクエストを待ち受けるポート番号は 9000 番です。
version: "3.8"
services:
# nginx コンテナ
nginx:
(省略)
# PHP-FPM コンテナ
php-fpm:
image: php:8.2-fpm
volumes:
- ./html:/usr/share/nginx/html
nginx コンテナと PHP-FPM コンテナをつなぐ
nginx コンテナと PHP-FPM コンテナを作成しましたが、このままだと2つのコンテナはバラバラの状態です。 この2つのコンテナをつないであげる必要があります。 具体的には、nginx コンテナで受け取ったリクエストを、FastCGI を使用して PHP-FPM コンテナに転送します。
PHP-FPM コンテナにリクエストを転送する
nginx のサーバーの設定ファイルを下記のように書き換えます。
location で、どの URL に対する設定なのかを指定できます。
指定する URL は、デフォルトでは前方一致での指定になるですが、~ を使用すると正規表現で指定することができます。
$document_root や $fastcgi_script_name など、$ から始まっているものは変数です。
例えば $document_root には、root で指定したドキュメントルートの値が入っています。
FastCGI を使用して接続する接続先の情報は fastcgi_pass で指定します。
TCP ポートを使用する方法と、Unix ソケットを使用する方法があるのですが、ここでは TCP ポートを使用する方法を説明します。
ホスト名として docker-compose.yml で定義しているサービス名(つまり、services 項目で指定しているキー)を使用できます。
上記の例では、ホスト名として "php-fpm" が使用できます。
PHP-FPM コンテナがリクエストを待ち受けているポート番号は 9000 番ですので、ポート番号には 9000 番を指定します。
fastcgi_param で PHP-FPM コンテナに渡すパラメーターを指定します。
デフォルトのパラメーターは "/etc/nginx/fastcgi_params" ファイルに用意されていますので、include で読み込んで使用します。
PHP-FPM コンテナで実行する PHP ファイルの名前は、SCRIPT_FILENAME パラメーターで指定します。
なお、実行する PHP ファイルは、PHP-FPM コンテナに置いておく必要があるので注意してください。
server {
# ドキュメントルート
root /usr/share/nginx/html;
# 全ての PHP ファイル
location ~ \.php$ {
# PHP ファイルが nginx コンテナに存在しない場合は、
# PHP-FPM コンテナに転送せずに 404 Not Found にする
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# FastCGI で PHP-FPM コンテナへ転送
fastcgi_pass php-fpm:9000;
# デフォルトの FastCGI パラメーター
include fastcgi_params;
# PHP-FPM コンテナで実行する PHP ファイルの名前
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
フロントコントローラーパターンを使用する
PHP のフレームワークでは、全てのリクエストを "/index.php" で処理するというフロントコントローラーパターンを使用しているものが多いです。
Apache を使用している場合は、mod_rewrite モジュールの RewriteRule を使用して URL を "/index.php" に書き換えていたと思います。
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
これと同じことを nginx でもできるように設定ファイルを変更してみましょう。
なお、nginx の設定ファイルはかなり自由度が高く、いろいろな書き方ができます。 下記の記述はほんの一例にすぎませんので、ご自身の環境に合わせて設定してください。
try_files を使用すると、ファイルが存在しているかどうかを確認することができます。
server {
# ドキュメントルート
root /usr/share/nginx/html;
# 全てのリクエスト
location / {
# リクエストされたファイルが存在しなければ、
# フロントコントローラーに内部リダイレクト
try_files $uri /index.php?$query_string;
}
# 全ての PHP ファイル
location ~ \.php$ {
# nginx コンテナで PHP ファイルの存在確認はしない
# 全てフロントコントローラーにまかせる
# FastCGI で PHP-FPM コンテナへ転送
fastcgi_pass php-fpm:9000;
# デフォルトの FastCGI パラメーター
include fastcgi_params;
# PHP-FPM コンテナで実行する PHP ファイルの名前
# - 全てのリクエストをフロントコントローラーで実行
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}
}
おまけ:バーチャルホストを使用する
複数のサイトを Docker で動作させたい場合、 1つのサイトごとに1つずつコンテナを作っても良いのですが、 それだと CPU やメモリなどの消費リソースが増えてしまい、 スペックの低い PC だと動作させるのが大変かもしれません。 そういった場合は、nginx のバーチャルホストを使用すると良いでしょう。 バーチャルホストを使用すると複数のサイトを1つのコンテナで動作させることができます。
例として、2つのサイトを使えるようにしてみます。
デフォルトのサイト設定を無効化する
まずはデフォルトのサイト設定が邪魔にならないよう無効化しておきましょう。 無効化しなくても良いと言えば良いのですが、バーチャルホストを使って複数サイトを管理するならおそらく使用しないと思います。
デフォルトのサーバーの設定のファイルは "/etc/nginx/conf.d/default.conf" ですので、そのファイルを読み込まないようにします。 削除するのもあれなので、念のためにファイル名を変更して置いておくことにします。 設定ファイルとして読み込まれるのは拡張子が ".conf" のファイルだけなので、拡張子 ".bak" を付けて無効化します。
FROM nginx:latest # デフォルトのサイト設定を無効化 RUN mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
各サイトの設定を追加する
次にバーチャルホストを使用したい各サイトの設定ファイルを追加します。
nginx の設定ファイルは最初からバーチャルホストに対応しています。
各サイトの設定ファイルを "/etc/nginx/conf.d" ディレクトリの配下に置くだけです。
設定ファイルの server ブロックが1つのサーバー(つまりホスト)の扱いなので、
1つのファイルに複数の設定を記述することもできるのですが、ファイルは分けておいた方が良いでしょう。
ホスト名は server_name で指定できます。
サイトの設定ファイルはアルファベット順に読み込まれます。 優先順位を付けたい場合は、ファイルの名前の先頭に番号を付けておくと良いでしょう。 どのサイトのホスト名にも一致しない URL が指定された場合、最初に読み込まれた設定ファイルのサイトが表示されます。
server {
# ホスト名
server_name site1.localhost;
# ドキュメントルート
root /path/to/site1/root;
}
server {
# ホスト名
server_name site2.localhost;
# ドキュメントルート
root /path/to/site2/root;
}
FROM nginx:latest # デフォルトのサイト設定を無効化 RUN mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak # サイト1の設定を追加 COPY ./001-site1.conf /etc/nginx/conf.d/001-site1.conf RUN chmod 644 /etc/nginx/conf.d/001-site1.conf # サイト2の設定を追加 COPY ./002-site2.conf /etc/nginx/conf.d/002-site2.conf RUN chmod 644 /etc/nginx/conf.d/002-site2.conf
各サイトのドキュメントルートにファイルを配置する
最後に各サイトのドキュメントルートにバインドマウントして、表示したいファイルを配置します。
version: "3.8"
services:
# nginx コンテナ
nginx:
build: ./nginx-custom
ports:
- 8080:80
volumes:
- ./site1_html:/path/to/site1/root
- ./site2_html:/path/to/site2/root
なお、バーチャルホストで使用する URL のホスト名は hosts ファイルなどで、ローカルホスト(例:"127.0.0.1")に名前解決する必要があるので注意してください。 また、ホスト側のポート番号を 80 番以外にしている場合は、URL にポート番号を付けるのを忘れないようにしましょう。 例えば、上記の例のような設定だと、サイト1にアクセスするURLは "http://site1.localhost:8080/"、サイト2にアクセスするURLは "http://site2.localhost:8080/" のようになります。