
我在 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
第一個請求到達但失敗後會顯示日誌訊息。
如何管理Nginx僅在PHP-FPM準備就緒時才開啟其連接埠?
主管配置:
[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:
我嘗試調整supervisord優先順序並啟動秒:
...
[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 首先初始化。RUNNING
Supervisord 應用於應用程式的狀態對於 Cloud Run 來說毫無意義。
答案1
現在,我最終得到了以下入口點腳本,確保 PHP-FPM 在啟動 nginx 之前運行:
#!/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 (相關主題):
運行一個shell進入睡眠狀態,然後直接啟動nginx:
[program:nginx] command=bash -c "sleep 5 && exec nginx -g 'daemon off;'" ...
執行一個在啟動 nginx 之前休眠的腳本:
[program:nginx] command=delayed-nginx.sh ...
delayed-nginx.sh
:#!/bin/bash sleep 5 exec nginx -g 'daemon off;'
原答案
我沒有專門使用Supervisor,但是這個答案可能適合你。這將導致 Supervisor 僅php-fpm
在運行時啟動 nginx 。
[program:php-fpm]
command=php-fpm -F
...
priority=100
[program:nginx]
command=nginx -g 'daemon off;'
...
priority=200
您可能還需要設定startsecs
為更高的值(比預設的 1 秒),以便 Supervisor 僅php-fpm
在運行了較長時間(例如 5 秒)後才考慮啟動:
[program:php-fpm]
command=php-fpm -F
...
priority=100
startsecs=5