php-fpm 與 nginx

最後更新: 2018-01-22

目錄

 


介紹

 

php5-fpm - PHP FastCGI Process Manager

 * process spawning

systemd-+...
        |-php-fpm-+-php-fpm
        |         |-php-fpm
        |         |-php-fpm
        |         |-php-fpm
        |         |-php-fpm
        |         `-php-fpm

Pool

不同的 Pool 是不同設定的 php process, 比如 php version, upload_max_filesize

Pool 是用不同的 port 去區分 (Default first pool: 9000/TCP)

Daemon

runs as a daemon and receives Fast/CGI requests.

Browser --> Nginx --> php-fpm --> php

* 當 Nginx 連不到 php-fpm proccess 時, 那 client 會收到 "502 Bad Gateway"

 

 


php5-fpm

 

Opts:

  • -t            Test FPM configuration file
  • -i             PHP information and configuration
  • -m           Show compiled in modules

 


php*-fpm 設定

 

php-fpm 主設定檔

/etc/php5/fpm/php-fpm.conf

; The maximum number of processes FPM will fork.
; The global number of processes when using dynamic PM within a lot of pools.
process.max = 64

; child processes to wait for a reaction on signals from master
process_control_timeout = 0

; Set open file descriptor rlimit for the master process
rlimit_files = 10240
; -19 (highest priority) to 20 (lower priority)
process.priority = 1

# 每個 Pool 的獨立 Setting
include=/etc/php5/fpm/pool.d/*.conf

Pool 的設定

/etc/php5/fpm/pool.d/www.conf

# 這是 pool 的名, variable $pool 相當於這字
[www]

# 這 pool 的 proccess 的執行身分
user = www-data
group = www-data

# TCP
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
# backlog: maximum length to which the queue of pending connections
# man 2 listen 有解釋. Default: -1 => unlimited. 另見
listen.backlog = 128

# Socket
listen = /var/run/php5-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660


# Proccess 數量設定
pm = dynamic

# alive at the same time (ApacheMaxClients)
# simultaneous requests that will be served.
pm.max_children = 32
# children created on startu
pm.start_servers = 4
# children in 'idle'
pm.min_spare_servers = 2
pm.max_spare_servers = 6
# idle process will be killed
pm.process_idle_timeout = 10s
# each child process should execute before respawning
pm.max_requests = 1024

# real-time FPM status monitoring
# Default Value: not set
# http://127.0.0.1/status Or http://www.foo.bar/status?full
pm.status_path = /status

# The access log file
; Default: not set
; Default Format: "%R - %u %t \"%m %r\" %s"
;  %R: remote IP address
;  %u: remote user
;  %t: server time
;  %m: request method
;  %r: the request URI
;  %s: status
access.log = /var/log/fpm-php/pool/$pool.access.log

; The log file for slow requests
slowlog = /var/log/fpm-php/pool/$pool.log.slow
request_slowlog_timeout = 10
request_terminate_timeout = 10m

# Limits the extensions of the main script FPM will allow to parse.
security.limit_extensions = .php .php3 .php4 .php5


# Additional php.ini defines, specific to this pool of workers.
php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f [email protected]
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php/pool/$pool.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M
php_admin_value[max_execution_time] = 180
php_admin_value[date.timezone] = Asia/Hong_Kong
php_admin_value[upload_max_filesize] = 15M
php_admin_value[post_max_size] = 20M
php_admin_value[short_open_tag] = on

# Restart Service

service php-fpm restart

 


nginx 與 php 的溝通時間

 

nginx

fastcgi_connect_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;

php-fpm

php_admin_value[max_execution_time] = 300
php_admin_value[max_input_time] = 300
php_admin_value[default_socket_timeout] = 300

Data size

client_max_body_size 50M;

 


FPM Log

 

Default: /var/log/php-fpm.log

Setting: /etc/php-fpm.conf

; If it's set to "syslog", log is sent to syslogd instead of file
error_log = /var/log/php-fpm/error.log
log_level = notice

Or

;syslog.facility = daemon
;syslog.ident = php-fpm

 


Nginx FPM 設定

 

snippets/php56-fpm.conf

location ~* \.php$ {
  # The "if" lets NGINX check whether the *.php does indeed exist
  # to prevent NGINX to feeding PHP FPM non php script file 
  # (like uploaded image)
  # 不能用 "try_files $uri =404;", 因為底有另一個 "if"
  if (!-f $document_root$fastcgi_script_name) { return 404; }

  #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
  include fastcgi_params;

  fastcgi_index index.php;

  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_param SCRIPT_NAME     $fastcgi_script_name;

  # disallows the FPM to process files in the /uploads/ directory
  if ($uri !~ "^/uploads/") {
    # fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_pass php56-fpm;
  }
}

snippets/common.conf

location ~ /\. { deny all; }

location = /favicon.ico { log_not_found off; access_log off; }

location = /robots.txt { log_not_found off; access_log off; }

# disallows access php in ? folder
location ~* /(?:uploads|files)/.*\.php$ { deny all; }

# no php is touched for static content, 因為中了 "$uri" 或 "$uri/"
location / { try_files $uri $uri/ /index.php?$args; }

sites-enabled/datahunter.org.conf

# vhost 設定
upstream php56-fpm {
    server 127.0.0.1:9056;
}
server {
  server_name datahunter.org www.datahunter.org;
  listen 80;
  root /home/vhosts/datahunter.org/public_html;
  index index.php index.html index.htm;
  ...
  # Protect admin panel
  location ~ ^/(wp-admin|wp-login\.php) {
    auth_basic             "Restricted";
    auth_basic_user_file    htpasswd;
  }
  ...
  include snippets/common.conf;
  include snippets/php56-fpm.conf;
}

 

 


Status page config in Nginx

 

Setting:

location ~ ^/(status|ping)$ {
     access_log off;
     allow 127.0.0.1;
     deny all;
     include fastcgi_params;
     fastcgi_pass 127.0.0.1:9000;
}

查看 status

curl -k  https://owncloud.datahunter.org:8443/status

pool:                 www
process manager:      dynamic
start time:           03/May/2014:09:29:47 +0000   # started or reloaded
start since:          110
accepted conn:        12
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       3
active processes:     1
total processes:      4
max active processes: 1
max children reached: 0                            # 多少次到達了 children limit 

listen queue

The number of request in the queue of pending connections.
If this number is non-zero, then you better increase number of process FPM can spawn.

listen queue < listen queue len

listen queue len (listen.backlog)

the size of the socket queue of pending connections

total processes (idle + active)

The current total number of processes.

Since FPM has started

max listen queue

The maximum number of requests in the queue of pending connections

max active processes

The maximum number of active processes since FPM has started

max children reached

The number of times that pm.max_children has been reached

slow requests

The total number of requests that have hit the configured request_slowlog_timeout

Output Format (short/full)

# short status

  • /status
  • status?json
  • /status?html
  • /status?xml

# full status

  • /status?full
  • /status?json&full
  • /status?html&full
  • /status?xml&full

 


Status page on Apache

 

# RHEL

conf.d/php-fpm-status.conf

<LocationMatch "/status">
    Require ip 127.0.0.1
    # Require ip 1.2.3.4    # Your IP here

    # Adjust the path to the socket if needed
    #ProxyPass "unix:/run/php-fpm/www.sock|fcgi://localhost/status"
    ProxyPass "fcgi://localhost:9080/php-fpm-status"
</LocationMatch>

<LocationMatch "/ping">
    Require ip 127.0.0.1
    # Require ip 1.2.3.4    # Your IP here

    # Adjust the path to the socket if needed
    #ProxyPass "unix:/run/php-fpm/www.sock|fcgi://localhost/ping"
    ProxyPass "fcgi://localhost:9080/php-fpm-ping"
</LocationMatch>

php-fpm.conf

ping.path = /php-fpm-ping
pm.status_path = /php-fpm-status

Test

  1. curl localhost/php-fpm-ping
  2. curl localhost//php-fpm-status

 


fastcgi setting

 

fastcgi_intercept_errors

fastcgi_intercept_errors on; # Default: off

Determines whether FastCGI server responses with codes

greater than or equal to 300 should be passed to a client or

be intercepted and redirected to nginx for processing with the error_page directive.

fastcgi_split_path_info

Syntax: fastcgi_split_path_info regex;

The fastcgi_split_path_info regex capable to correctly handle request like

  • /test.php/foo/blah.php
  • /test.php/  

first regex capture: becomes a value of the $fastcgi_script_name

second regex capture: becomes a value of the $fastcgi_path_info

i.e.

Setting

fastcgi_split_path_info ^(.+?\.php)(/.*)$;

include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO       $fastcgi_path_info;

request "/show.php/article/0001"

$fastcgi_script_name = first = /show.php

$fastcgi_path_info = second = /article/0001

 


php.ini - cgi.fix_pathinfo

 

cgi.fix_pathinfo (Default: 1)

# 修改

sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php.ini

# Checking

test.php

<?php var_export($_SERVER)?>

* Pay attention to the value of

  • REQUEST_URI,
  • SCRIPT_FILENAME,
  • SCRIPT_NAME,
  • PATH_INFO, PHP_SELF

Request: /path/to/script.php/THIS/IS/PATH/INFO

[1]

PATH_INFO = /THIS/IS/PATH/INFO

SCRIPT_FILENAME = /path/to/script.php

[0]

make PHP_SELF variable broken (not equal to DOCUMENT_URI).

Remark

現在已很小要修改 cgi.fix_pathinfo 了, 因為 php 有 security.limit_extensions

security.limit_extensions 會限制了 php(process) 執行那些檔案

/etc/php-fpm.d/www.conf

# Default Value: .php
security.limit_extensions = .php

 


.user.ini

 

要放在 public_html 才有效 !! (public_html/.user.ini)

由於它是放在 public_html 內, 所以要用 .htaccess 保護它 (public_html/.htaccess)

<Files ".user.ini">
    Require all denied
</Files>

.user.ini

short_open_tag=On
memory_limit=256M
upload_max_filesize=50M
post_max_size=60M
date.timezone=Asia/Hong_Kong
session.gc_maxlifetime=3600
max_execution_time=60
error_reporting=E_ALL & ~E_WARNING & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE

Notes: 不支援的 Settings

  • expose_php=Off

有關 .user.ini 的 php.ini Settings

user_ini.cache_ttl    300
user_ini.filename    .user.ini

 


 

 

Creative Commons license icon Creative Commons license icon