この記事は個人ブログと同じ内容です
nginx と php-fpm の構成で docker コンテナの PHP アプリケーション実行環境を構築してみます。
(開発環境構築の話ではないので docker-compose は使いません)
nginx と php-fpm の通信(How)が今回の話のメインです。
ディレクトリ構成など
docker イメージとコンテナの作成に入る前に、今回は php-fpm と nginx のコンテナを作成するため、それぞれ php-fpm 用に php というディレクトリを作成し、nginx 用に web というディレクトリを切って進めます。
そして、アプリケーション(の代わり)として index.php を php-fpm 側に配置しておきます。
docker/ ├─ php/ │ └ src/ │ └─ index.php └─ web/
なお、index.php は phpinfo を表示させるだけの簡単なものになっています。
<?php phpinfo(); ?>
php-fpm の Dockerfile を作成
Dockerfile を作成して、まずは最もベースとなる部分の定義を行います。
php/ 配下に Dockerfile を作成し以下を記述します。
php/Dockerfile
FROM php:8.0-fpm-alpine # install configure file RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini # settings COPY settings/php.ini /usr/local/etc/php/conf.d/php.ini # app sources COPY src /usr/share/nginx/html
- ベースイメージには PHP の公式イメージを指定しています。
- PHP の設定ファイルである php.ini を作成します。
- 追加で設定するための PHP 設定ファイルを設置しています。
- アプリケーションをドキュメントルートにコピーしています。
PHP の設定ファイル php.ini について
工程 2 では、PHP に予め用意されているテンプレートからそのまま php.ini を作成し設置しています。
設定値をデフォルトから変更したいものについては、予め用意した php.ini にその設定を記述し、工程 3 で conf.d/ 配下に設置する事で設定値を上書きしています。
[PHP] date.timezone = "Asia/Tokyo"
なので、変更が不要なら工程 3 の記述は不要です。
nginx の Dockerfile を作成
次に、nginx 側の Dockerfile を作成します。
web/ 配下に Dockerfile を作成し以下を記述します。
web/Dockerfile
FROM nginx:1.19-alpine # settings COPY settings/default.conf /etc/nginx/conf.d/default.conf
- ベースイメージには nginx の公式イメージを指定しています。
- nginx の設定ファイルを設置します。
nginx - Docker Official Images
nginx と php-fpm の通信を定義する
nginx と php-fpm の通信方式は 2 通りあって、それぞれで設定ファイルや Dockerfile への記述が異なります。
どちらを採用すべきか?については検証と結論に至れていないので、今回は両方で構築してみます。
TCP で通信を行う
TCP で通信する場合は、nginx の設定ファイルを以下のように定義します。
web/settings/default.conf
server { listen 80; root /usr/share/nginx/html; location / { index index.php index.html index.htm; fastcgi_pass php-sample:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
設定のもろもろは条件によって書き方が異なるためここの記述自体はかなり簡略化してますが、ポイントは fastcgi_pass の値です。
fastcgi_pass php-sample:9000;
php-sample というのは、今回作成する php-fpm のコンテナ名です。この後、php-fpm のコンテナを立ち上げる際にこの名前で起動させるので、それをここに指定します。
:9000 はポート番号です。php-fpm のデフォルトの待受ポートは 9000 なのでこれを指定しています。
前項で php-fpm と nginx の、ベースの定義を行いましたが、TCP で通信する場合は nginx の設定ファイルの定義のみで実現できます。
動作確認
ではイメージの作成とコンテナの起動を行いアプリケーションを動作させてみます。
以下のコマンドを実行して環境を構築します。
# 1. PHP のイメージを作成 docker build --no-cache -t php/sample:20210109 . # 2. nginx のイメージを作成 docker build --no-cache -t nginx/sample:20210109 . # 3. ネットワークを作成 docker network create --driver bridge sample_nw # 4. PHP のコンテナを起動 docker run --net=sample_nw --name php-sample php/sample:20210109 # 5. nginx のコンテナを起動 docker run -p 80:80 --net=sample_nw --name nginx-sample nginx/sample:20210109
ブラウザからアクセスします。
nginx と php-fpm を TCP で通信させてアプリケーションが実行できました。
最終的なファイル構成
docker/ ├── php/ │ ├ Dockerfile │ ├ settings/ │ │ └─ php.ini │ └ src/ │ └─ index.php └── web/ ├ Dockerfile └ settings/ └─ default.conf
Docker コンテナ・ネットワークについて
先程の環境立ち上げの際に、イメージとコンテナ以外に「ネットワーク」を作成しました。
1つのネットワークを作成しそのネットワークに各コンテナを接続する事で、コンテナ間が互いを識別できるようにしています。
作成したネットワークの詳細を確認すると、コンテナが2つ接続されている事がわかります。
# ネットワークを作成 docker network create --driver bridge sample_nw # ネットワーク一覧 % docker network ls NETWORK ID NAME DRIVER SCOPE 9326dc6f3546 sample_nw bridge local # 作成したネットワークを確認 % docker network inspect sample_nw [ { "Name": "sample_nw", "Id": "9326dc6f35463041ec9511fba0426bd81311a704f757da755a81a42fd5da51ef", "Created": "2021-01-09T04:31:03.73817688Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "61d2d36499511f76ea2e64cd3124da3fc0fc05aa5b8af115bd70a3b937258e31": { "Name": "nginx-sample", "EndpointID": "27a848cb6d4e39121a2a8fcd0ec69c0929411e6bf353b868c7dc70354a351ef9", "MacAddress": "08:19:ac:14:00:06", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" }, "acbc9a2bf88caeb1fad3a8d0d0e5a409642ea2651487abede28157ad883b027d": { "Name": "php-sample", "EndpointID": "b1fe75cad1c2e6d9541cfca82577a1e02e93107b6e026ecb476e9309456ae1ce", "MacAddress": "08:19:ac:14:00:05", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ]
Docker コンテナ・ネットワークの理解
ちなみに、ネットワークを作成せずに、 nginx 側のコンテナ起動時に --link オプションを使って php-fpm 側のコンテナを識別させる方法もありますが、現在では非推奨となっています。
コンテナ・リンク機能(古い機能)
UNIX ドメインソケット で通信を行う
UNIX ドメインソケットで通信する場合に TCP の時と違うのは、通信の為にソケットを指定する事と、nginx 側からソケットを参照できるようにする事です。
php-fpm
php/Dockerfile
FROM php:8.0-fpm-alpine # add user RUN addgroup -S nginx && adduser -S nginx -G nginx # install setting RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini # settings COPY settings/zzz-docker.conf /usr/local/etc/php-fpm.d/zzz-docker.conf COPY settings/php.ini /usr/local/etc/php/conf.d/app-php.ini # unix socket RUN mkdir /var/run/php-fpm VOLUME ["/var/run/php-fpm"] # app sources COPY src /usr/share/nginx/html
- ベースイメージには PHP の公式イメージを指定しています。
- nginx ユーザを作成します。
- PHP の設定ファイルである php.ini を作成します。
- php-fpm の設定ファイル(更新分)を設置します。
- PHP の設定ファイル(更新分)を設置します。
- ソケットを設置するディレクトリを作成し、ボリュームマウントします。
- アプリケーションをドキュメントルートにコピーします。
php-fpm の設定ファイルはデフォルトで設置されていますが、UNIX ドメインソケットを利用するために値を変更したい(工程 5 のところ)ので、設定値を上書きするための設定ファイルを作成します。
php/settings/zzz-docker.conf
[www] listen = /var/run/php-fpm/php-fpm.sock listen.owner = nginx listen.group = nginx listen.mode = 0660
unix ドメインソケットを使うように指定したのと、ソケットを使用するユーザ(パーミッション)を設定しています。
owner/group は、nginx 側からソケットを参照するユーザを指定しています。
nginx
web/Dockerfile
FROM nginx:1.19-alpine # settings COPY settings/default.conf /etc/nginx/conf.d/default.conf
- ベースイメージには nginx の公式イメージを指定しています。
- nginx の設定ファイルを設置します。
Dockerfile は TCP の時と変わりません。
nginx の設定ファイルを以下に定義します。
web/settings/default.conf
server { listen 80; server_name localhost; root /usr/share/nginx/html; location / { index index.php index.html index.htm; fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
TCP の時との違いは、fastcgi_pass に unix ドメインソケットを指定している点です。
動作確認
ではイメージの作成とコンテナの起動を行いアプリケーションを動作させてみます。
以下のコマンドを実行して環境を構築します。
# 1. PHP のイメージを作成 docker build --no-cache -t php/sample:20210109 . # 2. nginx のイメージを作成 docker build --no-cache -t nginx/sample:20210109 . # 3. PHP のコンテナを起動 docker run --name php-sample php/sample:20210109 # 4. nginx のコンテナを起動 docker run -p 80:80 --volumes-from php-sample --name nginx-sample nginx/sample:20210109
ブラウザからアクセスします。
nginx と php-fpm を UNIX ドメインソケット で通信させてアプリケーションが実行できました。
最終的なファイル構成
docker/ ├ php/ │ ├ Dockerfile │ ├ settings/ │ │ ├─ php.ini │ │ └─ zzz-docker.conf │ └ src/ │ └─ index.php └ web/ ├ Dockerfile └ settings/ └── default.conf
まとめ
Laravel や CakePHP 等の PHP フレームワークを動かす際も基本的には同じ要領で、nginx の設定ファイル側を調整したり、必要なパッケージを PHP 側に入れたりなどすれば動作させる事ができます。
実行環境なのでアプリケーションごとイメージに固めていますが、これらの dockerfile と docker-compose を併せて役割を分ければ開発環境の構築も可能です。