単一のコンテナで PHP-FPM を起動した後、Nginx を起動 (または http ポートをリッスン) するにはどうすればよいですか?

単一のコンテナで PHP-FPM を起動した後、Nginx を起動 (または http ポートをリッスン) するにはどうすればよいですか?

Cloud Run 上の単一コンテナで Nginx+PHP-FPM を実行すると問題が発生します。私のコンテナは Alpine ベースで、Supervisor によって Nginx と PHP-FPM の起動を管理します。全体的には問題なく動作しますが、Nginx が HTTP ポートのリッスンを開始してから PHP-FPM が起動するまでの時間が短くなります。これにより、次のログ メッセージとともに 502 HTTP エラーが表示されます。

6#6: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 169.254.8.129, server: _, request: "POST / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000"

ここでの問題は、Cloud Run が 8080 ポートを開いたときにコンテナがリクエストを処理する準備ができていると判断することです。ポートが開いた直後に、Cloud Run はリクエストを送信しますが、FPM がまだ準備ができていないため、最初の試行では常に失敗します。ログ メッセージは、NOTICE: fpm is running, pid 4最初のリクエストが到着して失敗した後に表示されます。

PHP-FPM の準備ができたときにのみ Nginx のポートを開くように管理するにはどうすればよいですか?

スーパーバイザー構成:

[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid

[program:php-fpm]
command=php-fpm -F
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

[program:nginx]
command=nginx -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

Nginx 設定:

# Write temporary files to /tmp so they can be created as a non-privileged user
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp_path;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;

access_log /dev/stdout;
error_log /dev/stderr notice;

server {
        listen 8080 default_server;

        index index.php;

        keepalive_requests    10;
        keepalive_timeout     60 60;

        root /var/www/html/app/public;

        charset utf-8;

        server_name _;

        # Deny hidden files (.htaccess, .htpasswd, .DS_Store).
        location ~ /\. {
            deny            all;
            access_log      off;
            log_not_found   off;
        }
        ########################
        # mappings             #
        ########################

        location ~ \.(js|css|png|jpg|ico) {
            expires 5d;
            try_files $uri $uri/ =404;
            return 404;
        }

        # Allow fpm ping and status from localhost
        location ~ ^/(fpm-status|fpm-ping)$ {
            access_log off;
            allow 127.0.0.1;
            deny all;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
            fastcgi_pass 127.0.0.1:9000;
        }

        location / {
            client_max_body_size 100m;
            try_files $uri @fpm;
        }

        location @fpm {
            # worker may take long time to finish (max 1 hour)
            fastcgi_read_timeout 3600s;
            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;
            fastcgi_param HTTP_PROXY "";
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /var/www/html/app/public/index.php;
            fastcgi_param SCRIPT_NAME index.php;
        }
}

FPM ping/ステータス ページを有効にしました。これを使用して Nginx ポートの開放をトリガーできますか?

アップデート1:

スーパーバイザーの優先順位と開始秒数を調整してみました:

...
[program:php-fpm]
...
priority=100
startsecs=3

[program:nginx]
...
priority=200

しかし、成功しませんでした。

[18-Dec-2020 00:31:04] NOTICE: ready to handle connections
[18-Dec-2020 00:31:04] NOTICE: fpm is running, pid 3
2020-12-18 00:30:30,689 INFO success: php-fpm entered RUNNING state, process has stayed up for > than 3 seconds (startsecs)
2020-12-18 00:30:28,388 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Error POST 502 549 B 3.286 s Google-Cloud-Scheduler https://***.run.app/
169.254.8.129 - - [18/Dec/2020:00:30:27 +0000] "POST / HTTP/1.1" 502 150 "-" "Google-Cloud-Scheduler"
169.254.8.129 - - [18/Dec/2020:00:30:27 +0000] "POST / HTTP/1.1" 502 150 "-" "Google-Cloud-Scheduler" "35.187.131.214"
2020/12/18 00:30:27 [error] 6#6: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 169.254.8.129, server: _, request: "POST / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "***.run.app"
2020-12-18 00:30:26,937 INFO spawned: 'nginx' with pid 4
2020-12-18 00:30:26,829 INFO spawned: 'php-fpm' with pid 3
2020-12-18 00:30:25,730 INFO supervisord started with pid 1
2020-12-18 00:30:25,704 CRIT Supervisor is running as root. Privileges were not dropped because no user

両方のアプリは引き続き Supervisord によって同時に起動され、nginx が最初に初期化されます。SupervisordRUNNINGがアプリに適用する状態は、Cloud Run にとっては何の意味もありません。

答え1

今のところ、nginx を起動する前に PHP-FPM が実行されていることを確認する次のエントリポイント スクリプトを作成しました。

#!/usr/bin/env sh
set -e

# Start PHP-FPM as a daemon in the background
php-fpm -D

# Wait until PHP-FPM is up and accepts connections. Fail if not started in 10 secs.
for run in $(seq 20)
do
  if [ "$run" -gt "1" ]; then
    echo "Retrying..."
  fi
  RESPONSE=$(
    SCRIPT_NAME=/fpm-ping \
    SCRIPT_FILENAME=/fpm-ping \
    REQUEST_METHOD=GET \
    cgi-fcgi -bind -connect 127.0.0.1:9000 || true)
  case $RESPONSE in
    *"pong"*)
      echo "FPM is running and ready. Starting nginx."
      # Run nginx without exiting to keep the container running
      nginx -g 'daemon off;'
      exit 0
      ;;
  esac
  sleep .5
done
echo "FPM has failed to start on-time, exiting"
exit 1

コマンドapk add fcgiは必須です (Alpine Linux の場合)。

php-fpm -Dまた、コマンドは FPM の準備ができたら常に終了するので、ループは必要なく、コマンドを次々に実行するだけであると想定しています。ただし、テストは行っていません。

答え2

Supervisorにはオプションが必要なのですstartdelayが、現在はオプションがありません。nginxの起動を遅らせるには、代わりにSupervisorに指定するコマンドを変更します(関連スレッド):

  1. シェルを実行してスリープ状態にしてから、nginx を直接起動します。

    [program:nginx]
    command=bash -c "sleep 5 && exec nginx -g 'daemon off;'"
    ...
    
  2. nginx を起動する前にスリープするスクリプトを実行します。

    [program:nginx]
    command=delayed-nginx.sh
    ...
    

    delayed-nginx.sh:

    #!/bin/bash
    sleep 5
    exec nginx -g 'daemon off;'
    

元の回答

私はSupervisorを特に使用したことはありませんが、この答えphp-fpmうまくいくかもしれません。これにより、Supervisor は実行中にnginx を一度だけ起動するようになります。

[program:php-fpm]
command=php-fpm -F
...
priority=100

[program:nginx]
command=nginx -g 'daemon off;'
...
priority=200

また、 Supervisor が、たとえば 5 秒など、より長い時間実行された後にstartsecsのみ起動されたと見なすように、より高い値 (デフォルトの 1 秒よりも高い値) を設定する必要がある場合もあります。php-fpm

[program:php-fpm]
command=php-fpm -F
...
priority=100
startsecs=5

詳細については、Supervisor のドキュメントを参照してください。

関連情報