跳至内容

第三部分. 应用服务器

PHP 和 PHP-FPM

在本章中,您将了解 PHP 和 PHP-FPM。

PHP (PHP Hypertext Preprocessor) 是一种专门为 Web 应用开发设计的源代码脚本语言。2024 年,PHP 在全球生成的网页中占比接近 80%。PHP 是开源的,也是最著名的 CMS(WordPress、Drupal、Joomla!、Magento 等)的核心。

PHP-FPM (FastCGI Process Manager) 自 PHP 5.3.3 版本起集成。PHP 的 FastCGI 版本带来了额外的功能。


目标:您将学会如何

✔ 安装 PHP 应用服务器
✔ 配置 PHP-FPM 池
✔ 优化 PHP-FPM 应用服务器

🏁 PHPPHP-FPM应用服务器

知识: ⭐ ⭐ ⭐
复杂性: ⭐ ⭐ ⭐

阅读时间: 30 分钟


概述

CGI (Common Gateway Interface) 和 FastCGI 允许 Web 服务器(Apache 或 Nginx)与开发语言(PHP、Python、Java)之间的通信。

  • CGI 的情况下,每次请求都会创建一个 新进程,这在性能方面效率较低。
  • FastCGI 依赖于 一定数量的进程 来处理其客户端请求。

PHP-FPM 在提供更好性能的同时,还带来了

  • 更好的 应用分区:使用不同的 uid/gid 启动进程,使用自定义的 php.ini 文件,
  • 统计信息管理,
  • 日志管理,
  • 进程的动态管理和无服务中断的重启(“平滑”)。

注意

由于 Apache 具有 PHP 模块,php-fpm 更常用于 Nginx 服务器。

选择 PHP 版本

Rocky Linux 与其上游一样,提供了多种语言版本。其中一些版本已达到生命周期终点,但仍被保留用于托管尚未兼容新 PHP 版本的旧应用程序。请参阅 php.net 网站上的 支持版本 页面来选择一个受支持的版本。

要获取可用版本的列表,请运行以下命令

$ sudo dnf module list php

Rocky Linux 9 - AppStream
Name                                                 Stream                                                  Profiles                                                                   Summary
php                                                  8.1 [d]                                                 common [d], devel, minimal

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Remi 仓库提供了比 Appstream 仓库更新的 PHP 版本,包括 8.2 和 8.3 版本。

要安装 Remi 仓库,请运行以下命令

sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm

运行以下命令启用 Remi 仓库

sudo dnf config-manager --set-enabled remi

现在,您可以通过运行以下命令来激活一个较新的模块(PHP 8.3)

sudo dnf module enable php:remi-8.3
$ sudo dnf module list php

Rocky Linux 8 - AppStream
Name                                                 Stream                                                  Profiles                                                                   Summary
php                                                  7.2 [d]                                                 common [d], devel, minimal                                                 PHP scripting language
php                                                  7.3                                                     common [d], devel, minimal                                                 PHP scripting language
php                                                  7.4                                                     common [d], devel, minimal                                                 PHP scripting language
php                                                  8.0                                                     common [d], devel, minimal                                                 PHP scripting language

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Rocky 从其 AppStream 仓库提供不同的 PHP 模块。

您会注意到 Rocky 8.9 的默认版本是 7.2,而在撰写本文时,它已经达到了生命周期终点。

您可以运行以下命令来激活一个较新的模块

sudo dnf module enable php:8.0
==============================================================================================
Package               Architecture         Version               Repository             Size
==============================================================================================
Enabling module streams:
httpd                                      2.4
nginx                                      1.14
php                                        8.0

Transaction Summary
==============================================================================================

Is this ok [y/N]:

Transaction Summary
==============================================================================================

Is this ok [y/N]: y
Complete!

现在您可以继续安装 PHP 引擎了。

安装 PHP CGI 模式

首先,安装并使用 CGI 模式的 PHP。它只能与 Apache Web 服务器及其 mod_php 模块配合使用。本文档的 FastCGI 部分(php-fpm)解释了如何将 PHP 集成到 Nginx(以及 Apache)中。

PHP 的安装相对简单。它包括安装主包和您需要的几个模块。

以下示例安装了通常随 PHP 一起安装的模块。

sudo dnf install php php-cli php-gd php-curl php-zip php-mbstring

安装过程中,系统会提示您导入 epel9(Extra Packages for Enterprise Linux 9)和 Remi 仓库的 GPG 密钥。输入 y 导入密钥

Extra Packages for Enterprise Linux 9 - x86_64
Importing GPG key 0x3228467C:
Userid     : "Fedora (epel9) <epel@fedoraproject.org>"
Fingerprint: FF8A D134 4597 106E CE81 3B91 8A38 72BF 3228 467C
From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-9
Is this ok [y/N]: y
Key imported successfully
Remi's RPM repository for Enterprise Linux 9 - x86_64
Importing GPG key 0x478F8947:
Userid     : "Remi's RPM repository (https://rpms.remirepo.net/) <remi@remirepo.net>"
Fingerprint: B1AB F71E 14C9 D748 97E1 98A8 B195 27F1 478F 8947
From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el9
Is this ok [y/N]: y
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.

Complete!
sudo dnf install php php-cli php-gd php-curl php-zip php-mbstring

您可以检查已安装的版本是否与预期版本一致

$ php -v
PHP 8.3.2 (cli) (built: Jan 16 2024 13:46:41) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
with Zend OPcache v8.3.2, Copyright (c), by Zend Technologies
$ php -v
PHP 7.4.19 (cli) (built: May  4 2021 11:06:37) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.19, Copyright (c), by Zend Technologies

Apache 集成

要以 CGI 模式提供 PHP 页面,您必须安装 Apache 服务器,配置它,激活它并启动它。

  • 安装
sudo dnf install httpd
activation:
sudo systemctl enable --now httpd
sudo systemctl status httpd
  • 不要忘记配置防火墙
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --reload

默认的 vhost 应该可以直接工作。PHP 提供了一个 phpinfo() 函数,可以生成其配置的摘要表。这有助于测试 PHP 是否正常工作。但是,请注意不要在服务器上留下此类测试文件。它们对您的基础设施构成巨大的安全风险。

创建文件 /var/www/html/info.php/var/www/html 是默认 Apache 配置的默认 vhost 目录)

<?php
phpinfo();
?>

使用 Web 浏览器访问页面 http://your-server-ip/info.php 来检查服务器是否正常工作。

警告

不要在服务器上保留 info.php 文件!

安装 PHP CGI 模式 (PHP-FPM)

如前所述,将 Web 托管切换到 PHP-FPM 模式有很多好处。

安装仅包含 php-fpm 包

sudo dnf install php-fpm

由于 php-fpm 是一个系统服务,您必须激活并启动它

sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm

配置 PHP CGI 模式

主配置文件是 /etc/php-fpm.conf

include=/etc/php-fpm.d/*.conf
[global]
pid = /run/php-fpm/php-fpm.pid
error_log = /var/log/php-fpm/error.log
daemonize = yes

注意

php-fpm 的配置文件有大量的注释。去看看吧!

如您所见,/etc/php-fpm.d/ 目录中以 .conf 扩展名结尾的文件总是被包含在内。

默认情况下,一个名为 www 的 PHP 进程池声明位于 /etc/php-fpm.d/www.conf

[www]
user = apache
group = apache

listen = /run/php-fpm/www.sock
listen.acl_users = apache,nginx
listen.allowed_clients = 127.0.0.1

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

slowlog = /var/log/php-fpm/www-slow.log

php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache
说明 描述
[pool] 进程池名称。配置文件可以包含多个进程池(方括号中的池名开始一个新的部分)。
listen 定义监听接口或使用的 Unix 套接字。

配置 PHP-FPM 进程的访问方式

有两种连接方式。

使用 inet 接口,例如

listen = 127.0.0.1:9000.

或使用 UNIX 套接字

listen = /run/php-fpm/www.sock.

注意

当 Web 服务器和 PHP 服务器在同一台机器上时使用套接字可以移除 TCP/IP 层并优化性能。

使用接口时,您必须配置 listen.ownerlisten.grouplisten.mode 来指定 Unix 套接字的属主、属组和权限。警告: Web 服务器和 PHP 服务器都必须对套接字具有访问权限。

使用套接字时,您必须配置 listen.allowed_clients 来限制对 PHP 服务器的访问,仅允许某些 IP 地址。

示例:listen.allowed_clients = 127.0.0.1

静态或动态配置

您可以静态或动态地管理 PHP-FPM 进程。

在静态模式下,pm.max_children 设置子进程的数量限制。

pm = static
pm.max_children = 10

此配置启动 10 个进程。

在动态模式下,PHP-FPM 最多启动由 pm.max_children 值指定的进程数。它首先启动一些对应于 pm.start_servers 的进程,保持至少 pm.min_spare_servers 个不活动的进程,并且最多有 pm.max_spare_servers 个不活动的进程。

示例

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

PHP-FPM 将创建一个新进程来替换一个已经处理了相当于 pm.max_requests 的请求的进程。

默认情况下,pm.max_requests 的值为 0,表示进程永不回收。对于存在内存泄漏的应用程序,pm.max_requests 选项可能很有吸引力。

第三种操作模式是 ondemand 模式。此模式仅在收到请求时启动一个进程。对于流量较大的网站,这不是最优模式,仅适用于特定需求(请求量小的网站、管理后端等)。

注意

PHP-FPM 操作模式的配置对于确保 Web 服务器的正常运行至关重要。

进程状态

就像 Apache 及其 mod_status 模块一样,PHP-FPM 提供了一个显示进程状态的页面。

要激活该页面,请使用 pm.status_path 指令设置其访问路径。

pm.status_path = /status
$ curl https:///status_php
pool:                 www
process manager:      dynamic
start time:           03/Dec/2021:14:00:00 +0100
start since:          600
accepted conn:        548
listen queue:         0
max listen queue:     15
listen queue len:     128
idle processes:       3
active processes:     3
total processes:      5
max active processes: 5
max children reached: 0
slow requests:        0

记录长请求

slowlog 指令指定了接收过于缓慢的请求日志的文件(例如,其执行时间超过 request_slowlog_timeout 指令的值)。

生成文件的默认位置是 /var/log/php-fpm/www-slow.log

request_slowlog_timeout = 5
slowlog = /var/log/php-fpm/www-slow.log

request_slowlog_timeout 的值为 0 会禁用日志记录。

Nginx 集成

nginx 的默认设置已经包含了使 PHP 与 PHP-FPM 正常工作的必要配置。

配置文件 fastcgi.conf(或 fastcgi_params)位于 /etc/nginx/ 目录下。

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

为了让 nginx 处理 .php 文件,请将以下指令添加到站点配置文件中。

如果 PHP-FPM 监听端口 9000

location ~ \.php$ {
  include /etc/nginx/fastcgi_params;
  fastcgi_pass 127.0.0.1:9000;
}

如果 php-fpm 监听 Unix 套接字

location ~ \.php$ {
  include /etc/nginx/fastcgi_params;
  fastcgi_pass unix:/run/php-fpm/www.sock;
}

Apache 集成

配置 Apache 使用 PHP 池非常简单。您必须使用代理模块和 ProxyPassMatch 指令,例如

<VirtualHost *:80>
  ServerName web.rockylinux.org
  DocumentRoot "/var/www/html/current/public"

  <Directory "/var/www/html/current/public">
    AllowOverride All
    Options -Indexes +FollowSymLinks
    Require all granted
  </Directory>
  ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/html/current/public"

</VirtualHost>

PHP 池的稳健配置

优化处理的请求数量并分析 PHP 脚本使用的内存对于最大化启动线程数至关重要。

首先,您需要使用以下命令了解 PHP 进程的平均内存使用量:

while true; do ps --no-headers -o "rss,cmd" -C php-fpm | grep "pool www" | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"Mb") }' >> avg_php_proc; sleep 60; done

这将让您对该服务器上 PHP 进程的平均内存占用有一个相当准确的了解。

本文档的其余部分假定在满载情况下,内存占用为每个进程 120MB。

在一台 8GB RAM 的服务器上,保留 1GB 给系统,1GB 给 OPCache(参见本文档其余部分),还剩下 6GB 来处理客户端的 PHP 请求。

您可以得出结论,该服务器最多可以接受 50 个线程 ((6*1024) / 120)

针对此用例的 php-fpm 的典型配置是:

pm = dynamic
pm.max_children = 50
pm.start_servers = 12
pm.min_spare_servers = 12
pm.max_spare_servers = 36
pm.max_requests = 500

其中

  • pm.start_servers = max_children 的 25%
  • pm.min_spare_servers = max_children 的 25%
  • pm.max_spare_servers = max_children 的 75%

Opcache 配置

opcache (Optimizer Plus Cache) 是您可以影响的第一级缓存。

它将编译后的 PHP 脚本保存在内存中,这对网页的执行有很大影响(消除了从磁盘读取脚本和编译时间)。

要配置它,您必须处理

  • 根据命中率分配给 opcache 的内存大小,正确配置它。
  • 要缓存的 PHP 脚本数量(键的数量 + 最大脚本数量)
  • 要缓存的字符串数量

要安装它

sudo dnf install php-opcache

要配置它,请编辑 /etc/php.d/10-opcache.ini 配置文件。

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000

其中

  • opcache.memory_consumption 对应于 opcache 所需的内存量(增加此值直到获得正确的命中率)。
  • opcache.interned_strings_buffer 是要缓存的字符串量。
  • opcache.max_accelerated_files 大致等于 find ./ -iname "*.php"|wc -l 命令的结果。

要配置 opcache,请参考 info.php 页面(包含 phpinfo();)(例如,查看 Cached scriptsCached strings 的值)。

注意

每次新代码部署时,都需要清空 opcache(例如,通过重启 php-fpm 进程)。

注意

不要低估正确设置和配置 opcache 所能带来的速度提升。

作者:Antoine Le Morvan

贡献者:Steven Spencer, Ganna Zhyrnova