先日、作成した nginx + php-fpm 環境は TCP で連携させていた。この実行環境で nginx と php-fpm 間を UNIX Socket で連携できるようにする。
手順の流れ
- php-fpm を UNIX ソケットで listen させるには
- php-fpm を UNIX ソケットで連携させる dokcer を用意する
- コンテナを稼働させて、設定の反映を確認する
php-fpm を UNIX ソケットで listen させるには
大まかには次の設定が必要になる。
- php-fpm イメージに登録されているユーザーを nginx のイメージに追加する
- php-fpm への接続を TCP から UNIX Socket に変更する設定ファイルを追加する
- nginx が php-fpm に接続する際の設定ファイルを追加する
php-fpm イメージに登録されているユーザーを nginx のイメージに追加する
php-fpm イメージに登録されているユーザーを nginx イメージに追加する理由は、php-fpm の UNIX ソケットを操作する権限を追加する必要があるためだ。この権限を追加しないと、nginx からのリクエストを php-fpm に渡すことができない。
php-fpm コンテナを起動し、php-fpm イメージに登録されているユーザーを確認する
$ docker run --detach --net=sample_nw --name php-sample php/sample:20220327
8ffaac4a194dbfee7cd214f4a84fb613b3dbc929dca0f46305d4550f9129acda
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ffaac4a194d php/sample:20220327 "docker-php-entrypoi…" 31 seconds ago Up 29 seconds 9000/tcp php-sample
$ docker exec -i -t 8ffaac4a194d /bin/sh
# cat /etc/passwd | grep nginx
# cat /etc/passwd | grep www-data
www-data:x:82:82:Linux User,,,:/home/www-data:/sbin/nologin
/var/www/html # id www-data
uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)
WEB サーバー関連のユーザーとして、uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)
が登録されているので、このユーザーを nginx のイメージに登録する。
nginx イメージに登録されているユーザーを確認する
nginx のコンテを起動して、登録ユーザーを確認する。
www_data グループは登録されているが、www-data ユーザーは登録されていない。php イメージには、www-data のグループとユーザー登録されているので、nginx のイメージには www-data ユーザを登録する。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
95a89d5cac6d php-socket_nginx-sample "/docker-entrypoint.…" 18 seconds ago Up 17 seconds 0.0.0.0:80->80/tcp php-socket_nginx-sample_1
d82e7e343c79 php-socket_php-sample "docker-php-entrypoi…" 19 seconds ago Up 17 seconds 9000/tcp php-socket_php-sample_1
$ docker exec -i -t bde24e5cbe3d /bin/sh
# cat www-data /etc/group
www-data:x:82:www-data
# cat nginx /etc/group
nginx:x:101:nginx
# grep www-data /etc/passwd
# grep nginx /etc/passwd
nginx:x:101:101:nginx:/var/cache/nginx:/sbin/nologin
# id nginx
uid=101(nginx) gid=101(nginx) groups=101(nginx),101(nginx)
# head /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
# exit
www-data と nginx のグループは登録されているが、ユーザーが登録されているのは、nginx ユーザーだけなのが上記で確認できた。
また、/etc/nginx/nginx.conf
に登録されている nginx プロセスのユーザーは nginx で登録されているので、php-fpm の UNIX ソケットを操作する権限を付与するために、/etc/nginx/nginx.conf
の設定を変更する。
php-fpm への接続を TCP から UNIX Socket に変更する設定ファイルを追加する
php-fpm がリクエストを待ち受ける設定ファイルは、下記のディレクトリ下にある。
$ docker run --detach --net=sample_nw --name php-sample php/sample:20220327
8ffaac4a194dbfee7cd214f4a84fb613b3dbc929dca0f46305d4550f9129acda
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ffaac4a194d php/sample:20220327 "docker-php-entrypoi…" 31 seconds ago Up 29 seconds 9000/tcp php-sample
$ docker exec -i -t 8ffaac4a194d /bin/sh
# ls -l /usr/local/etc/php-fpm.d/
total 56
-rw-r--r-- 1 root root 357 Mar 23 19:57 docker.conf
-rw-r--r-- 1 root root 20876 Mar 23 19:57 www.conf
-rw-r--r-- 1 root root 20876 Mar 23 19:56 www.conf.default
-rw-r--r-- 1 root root 45 Mar 23 19:57 zz-docker.conf
# cat /usr/local/etc/php-fpm.d/zz-docker.conf
[global]
daemonize = no
[www]
listen = 9000
上記の設定ファイル /usr/local/etc/php-fpm.d/zz-docker.conf
はなかなかの曲者で、Dockerfile の COPY や docker-compose.yaml で、ファイルを利用して UNIX ソケットを listen する設定に置き換えても、変更点が反映されない
。
調べてみると、公式の php-fpm イメージの Dokcerfile の記述で、ユーザーの変更を公式の Dockerfile が上書きしていたことが原因だった。
} | tee php-fpm.d/docker.conf; \
{ \
echo '[global]'; \
echo 'daemonize = no'; \
echo; \
echo '[www]'; \
echo 'listen = 9000'; \
} | tee php-fpm.d/zz-docker.conf
参考
- Documentation regarding configuration. #241
- PHPの公式DockerイメージでUNIXソケット通信しようとして罠にハマる
- docker-composeでunixソケットを使った、Nginx、php-fpmコンテナを作る
では、php-fpm を UNIX ソケットで listen させるにはどうしたら良いのかというと、
- php-fpm の設定は、後から読み込まれる設定で上書きされる
- 設定ファイルは、ファイル名の昇順で読み込まれる
- この仕様を利用して、
/usr/local/etc/php-fpm.d/zz-docker.conf
よりも後に設定ファイルを読み込ませて、TCP で listen する設定を、UNIX ソケットで listen する設定で上書きする
ことが必要になる。
この回避策を記載している WEB ページはいくつかあったが、上記の理由は記載されていなかった。そのため、ファイル名を socket.conf
にして設定変更を試みても反映されない理由がわからず、原因の確定に時間がかなりかかった。
php-fpm を UNIX ソケットで連携させる dokcer を用意する
docker は先日、作成した設定を流用する。
前回作成した docker の設定を複製する
作成した docker の設定が下記ディレクトリ $HOME/works/docker-sample/php-tcp
にあるとする。それを複製する。
$ pwd
$HOME/works/docker-sample
$ ls -l
total 0
drwxr-xr-x 5 centipede centipede 160 3 27 13:36 php-tcp
$ cp -r php-tcp
$ ls -l
total 0
drwxr-xr-x 7 centipede centipede 224 3 28 15:19 php-socket
drwxr-xr-x 5 centipede centipede 160 3 27 13:36 php-tcp
docker-compose.yaml を編集する
php-fpm が生成する UNIX ソケットを、nginx のコンテナから参照できるようにする。
$ cp -p docker-compose.yaml docker-compose.yaml.orig
$ vim docker-compose.yaml
$ cat docker-compose.yaml
version: '3'
# nginx と php-fpm のコンテナ間で共有されるボリューム名を設定
volumes:
php-fpm-sock:
services:
php-sample:
restart: always
build:
context: .
dockerfile: ./php/Dockerfile
volumes:
# 共有されるボリュームを次のディレクトリにマウントさせる
- php-fpm-sock:/var/run/php-fpm
nginx-sample:
restart: always
ports:
- 80:80
build:
context: .
dockerfile: ./nginx/Dockerfile
volumes:
# 共有されるボリュームを次のディレクトリにマウントさせる
- php-fpm-sock:/var/run/php-fpm
# ngixn のプロセスを実行するユーザーを変更するために、設定ファイルを置き換える
- ./nginx/settings/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- php-sample
変更点は次の通り。
$ diff -uw docker-compose.yaml.orig docker-compose.yaml
--- docker-compose.yaml.orig 2022-03-27 13:51:28.000000000 +0900
+++ docker-compose.yaml 2022-03-28 15:20:51.000000000 +0900
@@ -1,11 +1,16 @@
version: '3'
+volumes:
+ php-fpm-sock:
+
services:
php-sample:
restart: always
build:
context: .
dockerfile: ./php/Dockerfile
+ volumes:
+ - php-fpm-sock:/var/run/php-fpm
nginx-sample:
restart: always
@@ -14,3 +19,8 @@
build:
context: .
dockerfile: ./nginx/Dockerfile
+ volumes:
+ - php-fpm-sock:/var/run/php-fpm
+ - ./nginx/settings/nginx.conf:/etc/nginx/nginx.conf
+ depends_on:
+ - php-sample
nginx の Dockerfile を変更する
php-fpm の UNIX ソケットを操作するために、nginx のイメージにユーザーを追加する。
この時に、php のイメージに登録されている www-data ユーザーのユーザー ID と一致させること。今回は ID が 82 だったので、ユーザー ID が 82 をもつ www-data ユーザーを nginx のイメージに作成する。
$ cp nginx/Dockerfile nginx/Dockerfile.tcp
$ vim nginx/Dockerfile
$ cat nginx/Dockerfile
FROM nginx:1.21.6-alpine
# add nginx group and nginx user
RUN adduser -S www-data -G www-data -u 82 \
&& echo "www-data ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
&& echo 'www-data:www-data' | chpasswd
COPY nginx/settings/default.conf /etc/nginx/conf.d/default.conf
変更点は次の通り。
$ diff -uw nginx/Dockerfile.bak nginx/Dockerfile
--- nginx/Dockerfile.tcp 2022-03-27 17:50:49.000000000 +0900
+++ nginx/Dockerfile 2022-03-28 15:22:08.000000000 +0900
@@ -1,3 +1,8 @@
FROM nginx:1.21.6-alpine
-COPY settings/default.conf /etc/nginx/conf.d/default.conf
+# add nginx group and nginx user
+RUN adduser -S www-data -G www-data -u 82 \
+ && echo "www-data ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
+ && echo 'www-data:www-data' | chpasswd
+
+COPY nginx/settings/default.conf /etc/nginx/conf.d/default.conf
\ No newline at end of file
UNIX ソケットを介して nginx と php-fpm の連携するように変更をする
nginx を実行するユーザーを nginx から www-data に変更するために、変更を加えた nginc.conf
を用意する。
$ touch nginx/settings/nginx.conf
$ vim nginx/settings/nginx.conf
$ cat nginx/settings/nginx.conf
# change nginx user to www-data
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
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;
}
php-fpm の UNIX ソケットとやりとりするように、default.conf
の設定を変更する。php-fpm の UNIX ソケットは、/var/run/php-fpm/php-fpm.sock
に生成する。
$ cp nginx/settings/default.conf nginx/settings/default.conf.orig
$ vim nginx/settings/default.conf
$ cat nginx/settings/default.conf
server {
listen 80;
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;
}
}
変更点は次の通り。
$ diff -uw nginx/settings/default.conf.orig nginx/settings/default.conf
--- nginx/settings/default.conf.orig 2022-03-27 18:00:38.000000000 +0900
+++ nginx/settings/default.conf 2022-03-27 18:31:28.000000000 +0900
@@ -4,7 +4,7 @@
location / {
index index.php index.html index.htm;
- fastcgi_pass php-sample:9000;
+ 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;
php-fpm の Dockerfile を変更する
php-fpm が TCP 9000 ポートを listen させる設定 /usr/local/etc/php-fpm.d/zz-dcoker.conf
を読み込んだ後に、その設定を UNIX ソケットを listen する設定に上書きするために、ファイル名 /usr/local/etc/php-fpm.d/zz-www.conf
を php-fpm のイメージに取り込ませる。
$ cp php/Dockerfile php/Dockerfile.tcp
$ vim php/Dockerfile
$ cat php/Dockerfile
FROM php:8.1.4-fpm-alpine
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
COPY php/settings/php.ini /usr/local/etc/php/conf.d/php.ini
# import the conf file ordred by file name to overwrite listening TCP port setting to unix socket
COPY php/settings/php-fpm.d/zz-www.conf /usr/local/etc/php-fpm.d/zz-www.conf
COPY php/src /usr/share/nginx/html
変更点は次の通り。
$ diff -uw php/Dockerfile.tcp php/Dockerfile
--- php/Dockerfile.tcp 2022-03-27 17:51:59.000000000 +0900
+++ php/Dockerfile 2022-03-28 15:36:56.000000000 +0900
@@ -1,5 +1,9 @@
FROM php:8.1.4-fpm-alpine
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
+
COPY php/settings/php.ini /usr/local/etc/php/conf.d/php.ini
+# import the conf file to overwrite listening TCP port setting to unix socket
+COPY php/settings/php-fpm.d/zz-www.conf /usr/local/etc/php-fpm.d/zz-www.conf
+
COPY php/src /usr/share/nginx/html
コンテナを稼働させて、設定の反映を確認する
念のため、現在のディレクトリを確認する。
$ pwd
$HOME/works/docker-sample/php-socket
$ ls -l
$ ls -l
total 16
-rw-r--r-- 1 centipede centipede 471 3 28 15:20 docker-compose.yaml
-rw-r--r-- 1 centipede centipede 248 3 27 13:51 docker-compose.yaml.orig
drwxr-xr-x 6 centipede centipede 192 4 3 18:50 nginx
drwxr-xr-x 7 centipede centipede 224 3 28 15:20 php
drwxr-xr-x 2 centipede centipede 64 3 28 13:29 src
$ tree
$ tree
.
├── docker-compose.yaml
├── docker-compose.yaml.orig
├── nginx
│ ├── Dockerfile
│ ├── Dockerfile.tcp
│ └── settings
│ ├── default.conf
│ ├── default.conf.orig
│ └── nginx.conf
└── php
├── Dockerfile
├── Dockerfile.tcp
├── settings
│ ├── php-fpm.d
│ │ └── zz-www.conf
│ └── php.ini
└── src
└── index.php
6 directories, 12 files
コンテナを稼働させる。
$ docker-compose up -d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
95a89d5cac6d php-socket_nginx-sample "/docker-entrypoint.…" 6 days ago Up 6 days 0.0.0.0:80->80/tcp php-socket_nginx-sample_1
d82e7e343c79 php-socket_php-sample "docker-php-entrypoi…" 6 days ago Up 6 days 9000/tcp php-socket_php-sample_1
php のコンテナにログインして、状態を確認する。
$ docker exec -i -t d82e7e343c79 /bin/sh
/var/www/html # ls -l /usr/local/etc/php-fpm.d/
docker.conf www.conf www.conf.default zz-docker.conf zz-www.conf
/var/www/html # ls -l /usr/local/etc/php-fpm.d/zz-www.conf
-rw-r--r-- 1 root root 173 Mar 28 06:20 /usr/local/etc/php-fpm.d/zz-www.conf
/var/www/html # cat /usr/local/etc/php-fpm.d/zz-www.conf
[global]
daemonize = no
[www]
user = www-data
group = www-data
listen = /var/run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
/var/www/html # ls -l /var/run/php-fpm/
total 0
srw-rw---- 1 www-data www-data 0 Mar 28 06:23 php-fpm.sock
/var/www/html # netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.11:44299 0.0.0.0:* LISTEN -
/var/www/html # exit
php-fpm の設定ファイルが取り込まれ、UNIX ソケットが想定通りに生成されている。netstat -natp
を実行した結果、php-fpm が ポートを listen していないことが確認できた。
次に、nginx の状態を確認する。
$ docker exec -i -t 95a89d5cac6d /bin/sh
/ # ls -l /var/run/php-fpm/
total 0
srw-rw---- 1 www-data www-data 0 Mar 28 06:23 php-fpm.sock
/ # netstat -natp | grep nginx
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/nginx: master pro
/ # exit
nginx のコンテナから php-fpm の UNIX ソケットが見えていることが確認できた。
ホストマシンから curl で接続をしてみる。
$ curl -I http://localhost/
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sun, 03 Apr 2022 09:58:42 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/8.1.4
http://localhost/ にも接続をして、phpinfo() の実行結果が表示されることを確認した。