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 len

the size of the socket queue of pending connections

total processes (idle + active)

The current total number of processes.

max listen queue # since FPM has started

The maximum number of requests in the queue of pending connections

 

max active processes # since FPM has started

the maximum number of active processes since FPM has started

 

slow requests

The total number of requests that have hit the configured request_slowlog_timeout

output

# 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