Docker環境でのPHP-FPMとNginxの「Connection reset by peer」エラー解決方法

## はじめに

Dockerコンテナを使って、PHP-FPMとNginxを連携させている際に、NginxからPHP-FPMにリクエストを送った際に「Connection reset by peer」エラーが発生しました。この記事では、この問題の原因と解決方法について、具体的な手順と設定例を交えながら解説します。

## 環境

- **Docker**：20.10.17
- **Compose**：v2.10.2
- **PHP-FPM**：8.3.1
- **Nginx**：1.24
- **OS**：Amazon Linux 2023（Dockerコンテナ内）

## 問題の概要

NginxコンテナからPHP-FPMコンテナにリクエストを送信した際、以下のエラーが発生しました。

```
curl web:9000
curl: (56) Recv failure: Connection reset by peer
```

また、Nginxのエラーログには次のようなエラーメッセージが記録されていました。

```
2024/09/18 13:12:48 [error] 7#7: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 172.30.0.1, server: localhost, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://172.30.0.2:9000", host: "localhost:8080"
```

これは、NginxがPHP-FPMに接続できたものの、接続がリセットされた状態を示しています。

## 原因

`/etc/php-fpm.d/www.conf`ファイルにある`listen.allowed_clients`の設定が原因でした。この設定は、PHP-FPMが受け入れるクライアントのIPアドレスを制限するものです。デフォルトでは`127.0.0.1`のみが許可されているため、`nginx`コンテナ（別のIPアドレス）からの接続が拒否されていました。

```ini
listen.allowed_clients = 127.0.0.1
```

## 解決方法

`listen.allowed_clients`の設定をコメントアウトすることで、すべてのクライアントからの接続を許可するように変更します。

### 1. `www.conf`の設定変更

`/etc/php-fpm.d/www.conf`を開き、以下の行をコメントアウトします。

```ini
; listen.allowed_clients = 127.0.0.1
```

### 2. PHP-FPMの再起動

設定変更後、PHP-FPMを再起動して設定を反映させます。

```bash
# PHP-fpmが動作しているwebコンテナを再ビルドして起動
docker compose up --build -d web
```

### 3. 動作確認

NginxコンテナからPHP-FPMコンテナに再度リクエストを送って、接続が正常に行えるか確認します。

```bash
# Nginxコンテナに入る
docker-compose exec nginx bash

# PHP-FPMにリクエスト
curl web:9000
```

## 設定全体の確認

以下は、設定全体を確認するための手順です。

### Docker Compose 設定 (`docker-compose.yml`)

```yaml
version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: web/Dockerfile
    volumes:
      - ./src:/usr/share/nginx/html
      - ./web/www.conf:/etc/php-fpm.d/www.conf
    expose:
      - "9000"
    networks:
      - app-network

  nginx:
    build:
      context: .
      dockerfile: nginx/Dockerfile
    ports:
      - '8443:443'
      - '8080:80'
    volumes:
      - ./src:/usr/share/nginx/html:ro
    depends_on:
      - web
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
```

### PHP-FPM設定 (`www.conf`)

```ini
[www]
listen = 0.0.0.0:9000

; listen.allowed_clients = 127.0.0.1

pm = dynamic
pm.max_children = 100
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
```

### Nginx設定 (`nginx.conf`)

```nginx
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    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;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        server_name  localhost;
        root         /usr/share/nginx/html/public;
        index index.php index.html index.html;

        # PHP-FPMの設定を追加
        location ~ \.php$ {
            fastcgi_pass   web:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }

    # Settings for a TLS enabled server.
    server {
        listen       443 ssl http2;
        server_name  localhost;
        root         /usr/share/nginx/html/public;
        index index.php index.html index.html;

        ssl_certificate "/etc/pki/nginx/server.crt";
        ssl_certificate_key "/etc/pki/nginx/private/server.key";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;

        # PHP-FPMの設定を追加
        location ~ \.php$ {
            fastcgi_pass   web:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}

```

## まとめ

NginxとPHP-FPMをDockerで連携させる際に発生する「Connection reset by peer」エラーは、PHP-FPMの設定に起因することが多いです。今回のケースでは、`listen.allowed_clients`をコメントアウトすることで問題を解決しました。同様の問題に直面した場合は、PHP-FPMの設定ファイルを確認してみてください。

参考にした記事：[PHP-FPMとNginxの連携で「Connection reset by peer」エラーを解決する](https://zenn.dev/tamakiii/scraps/037c81c0cc52c4)