Unix Like, 操作系统, 服务器, 未分类, 运营维护

让cPanel/WHM的Apache使用PHP FPM

雅黑PHP探针

因为FasiCGI,CGI模式下均有少许问题,所以一直以来都给Apache使用mod_php方式执行PHP。

但此模式的资源占用可要给差评了,在网站访问量大的时候,特别是被CC的时候,FORK出的大量Apache进程经常会变成“僵尸”进程,无法正常退出,即使访问量恢复正常,攻击已结束,那些“僵尸”进程一样占用着你的资源,有时攻击猛烈些,直接整个系统宕掉,非硬重启无法救活。纵使有些人会使用如CloudLinux,BetterLinux之类的限制单用户的进程,但还是会有以Apache默认的执行身份fork出的进程……

为了给虚拟主机带来更稳定,更安全的使用环境,必须更换PHP执行方式!

经过测试与研究,最后采用了PHP FPM。

WHY PHP FPM?

PHP FPM不仅能满足使用admin value,opcache的需求,还能针对每个不同的pool的最大进程数量进行限制。这也就意味着,可以防止某个站点占用服务器过多的资源。

同时,让Apache使用worker模式运行,仅负责静态资源,仅需数个进程就能处理大量的访问。

可见,PHP FPM性能,安全皆具备!

 

很可惜,一直以“强大”著称的cPanel/WHM的,在PHP设置的选项中并没有表现出任何的强大:

Configure PHP and suEXEC

 

 

很明显,这要自己动手了。

 

动手前,先定一个大概的步骤:

1. 切换Apache为worker模式。

2. 编译安装PHP。

3. 给cPanel/WHM添加HOOK。

4. 给已有的用户创建PHP FPM POOL配置文件。

5. 重建所有站点的Apache配置文件。

 

因为服务器有已有虚拟主机的用户,因此“改造”过程中尽量减少Apache的暂停时间。

 

切换Apache为worker模式

看了一下cPanel/WHM编译Apache的的参数,是使用–with-mpm指定mpm模块的,要更换的话,只能重新编译。EasyApache中并未mod_proxy_fcgi,要让Apache使用FastCGI Server,此模块是必须的。

既然如此,那自行编译是最好不过的选择了。

下载Apache,APR,APR-UTILS:

wget http://mirror.nus.edu.sg/apache//httpd/httpd-2.4.10.tar.bz2 -O- | tar xjvf - -C /usr/src/
cd /usr/src/httpd-2.4.10
wget http://mirror.nus.edu.sg/apache//apr/apr-1.5.1.tar.bz2 -O- | tar xjvf - -C ./srclib/
wget http://mirror.nus.edu.sg/apache//apr/apr-util-1.5.4.tar.bz2 -O- | tar xjvf - -C ./srclib/
mv ./srclib/apr-1.5.1 ./srclib/apr
mv ./srclib/apr-util-1.5.4 ./srclib/apr-util

开始编译:

./configure \
--disable-v4-mapped \
--enable-access-compat=static \
--enable-actions=static \
--enable-alias=static \
--enable-auth_basic=static \
--enable-authn_core=static \
--enable-authn_file=static \
--enable-authz_core=static \
--enable-authz_groupfile=static \
--enable-authz_host=static \
--enable-authz_user=static \
--enable-autoindex=static \
--enable-cache=static \
--enable-cache-disk=static \
--enable-cgi=static \
--enable-deflate=static \
--enable-dir=static \
--enable-echo=static \
--enable-env=static \
--enable-expires=static \
--enable-ext-filter=static \
--enable-file-cache=static \
--enable-filter=static \
--enable-headers=static \
--enable-include=static \
--enable-info=static \
--enable-ldap=static \
--enable-log_config=static \
--enable-logio=static \
--enable-mime=static \
--enable-modules=none \
--enable-negotiation=static \
--enable-proxy=static \
--enable-proxy-connect=static \
--enable-proxy-http=static \
--enable-proxy-fcgi \
--enable-rewrite=static \
--enable-setenvif=static \
--enable-slotmem_shm=static \
--enable-socache_dbm=static \
--enable-socache_shmcb=static \
--enable-ssl=static \
--enable-status=static \
--enable-suexec=static \
--enable-unique-id=static \
--enable-unixd=static \
--enable-userdir=static \
--enable-vhost-alias=static \
--enable-remoteip \
--prefix=/usr/local/apache \
--with-included-apr \
--with-ldap \
--enable-mpms-shared=all \
--with-pcre=/opt/pcre \
--with-ssl=/usr \
--with-suexec-caller=nobody \
--with-suexec-docroot=/ \
--with-suexec-gidmin=100 \
--with-suexec-logfile=/usr/local/apache/logs/suexec_log \
--with-suexec-uidmin=100 \
--with-suexec-userdir=public_html && make && make install

configure的参数,你可以根据自己的需要做出修改。

这里不能重启Apache,否则无法启动了。

编辑/etc/httpd/conf/includes/pre_main_global.conf文件,在里面加入下列代码:

LoadModule proxy_fcgi_module /usr/local/apache/modules/mod_proxy_fcgi.so
LoadModule mpm_worker_module /usr/local/apache/modules/mod_mpm_worker.so

这样,就成功切换Apache为worker模式运行,并启用mod_proxy_fcgi模块。

由于是自行编译的Apache,如果使用/scripts/rebuildhttpdconf,会出现“This tool unavailable until Apache is built by EasyApache 3.x”的提示,编辑/scripts/rebuildhttpdconf文件,把此行:

die 'This tool unavailable until Apache is built by EasyApache 3.x' if !Cpanel::Config::Httpd::is_ea3();    # issafe

注释,或者删掉即可。

 

编译安装PHP

这里使用的是PHP 5.4.31:

wget http://hk1.php.net/get/php-5.4.33.tar.bz2/from/this/mirror -O- | tar xjvf - -C /usr/src/
cd /usr/src/php-5.4.33

编译安装,并配置,把下面的代码保存到一个文件,使用“bash 文件名”方式执行

VERSION="php54"
PREFIX="/usr/local/${VERSION}"
SCANDIR="${PREFIX}/etc/conf.d"
MYSQLSOCK="/var/lib/mysql/mysql.sock"
./configure \
--enable-bcmath \
--enable-calendar \
--enable-exif \
--enable-ftp \
--enable-gd-native-ttf \
--enable-intl \
--enable-libxml \
--enable-mbstring \
--enable-pdo=shared \
--enable-soap \
--enable-sockets \
--enable-wddx \
--enable-zip \
--enable-fpm \
--prefix=${PREFIX} \
--with-config-file-path=${SCANDIR} \
--with-config-file-scan-dir=${SCANDIR} \
--with-bz2 \
--with-curl=/opt/curlssl/ \
--with-freetype-dir=/usr \
--with-gd \
--with-gettext \
--with-icu-dir=/usr \
--with-imap=/opt/php_with_imap_client/ \
--with-imap-ssl=/usr \
--with-jpeg-dir=/usr \
--with-kerberos \
--with-libexpat-dir=/usr \
--with-libxml-dir=/opt/xml2 \
--with-libxml-dir=/opt/xml2/ \
--with-mcrypt=/opt/libmcrypt/ \
--with-mysql=/usr \
--with-mysql-sock=${MYSQLSOCK} \
--with-mysqli=/usr/bin/mysql_config \
--with-openssl=/usr \
--with-openssl-dir=/usr \
--with-pcre-regex=/opt/pcre \
--with-pdo-mysql=shared \
--with-pdo-sqlite=shared \
--with-png-dir=/usr \
--with-snmp \
--with-xmlrpc \
--with-xpm-dir=/usr \
--with-xsl=/opt/xslt/ \
--with-zlib \
--with-zlib-dir=/usr \
--with-ldap \
--with-mhash && make && make install || exit 1
mkdir -p ${PREFIX}/etc/fpm.d/ ${SCANDIR} /usr/local/${VERSION}/sockets/
\cp -ar ./php.ini-production ${SCANDIR}/php.ini && chown -R root:root ${SCANDIR} && chmod -R 755 ${SCANDIR}
cat>${PREFIX}/etc/php-fpm.conf<<EOF
include=etc/fpm.d/*.conf
[global]
EOF
\cp -ar sapi/fpm/init.d.php-fpm /etc/init.d/${VERSION}-fpm && chown root:root /etc/init.d/${VERSION}-fpm && chmod 755 /etc/init.d/${VERSION}-fpm && chkconfig --add ${VERSION}-fpm && chkconfig ${VERSION}-fpm on
sed -r -i "s#php_opts=\"(.*)\"#php_opts=\"\1 -c \${prefix}/etc/conf.d/php.ini\"#g" /etc/init.d/${VERSION}-fpm

同样的,编译参数你可以自行更改。

 

给cPanel/WHM添加HOOK

根据cPanel/WHM的官方文档,使用HOOK Script实现在成功创建用户后创建PHP FPM的POOL配置文件,以及成功删除用户后PHP FPM POOL配置文件。

编辑/scripts/postwwwacct,加入下列代码:

#!/bin/bash
while [ "${1}" != "user" ]
do
        echo "Shift ${1}"
        shift
done
eval ${1}=${2}
echo ${user}
if [ "${user}" = "root" ] || [ "${user}" = "" ];then
        exit;
fi
cat>/usr/local/php54/etc/fpm.d/${user}.conf<<EOF
[${user}]
user = \$pool
group = \$pool
listen = /usr/local/php54/sockets/\$pool.sock
listen.owner = \$pool
listen.group = nobody
listen.mode = 660
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 20
php_admin_value[open_basedir] = /home/${user}/:/tmp/:/var/tmp/:/usr/local/php54/lib/
;php_admin_value[mail.log] = /home/${user}/php-mail.log
security.limit_extensions = .php .php52 .php53 .php54 .php55 .php60 .php54 .php0
EOF
/usr/sbin/usermod -aG ${user} apache
/etc/init.d/httpd restart
/etc/init.d/php54-fpm reload

还有/scripts/postkillacct:

#!/bin/bash
while [ "${1}" != "user" ]
do
        echo "Shift ${1}"
        shift
done
eval ${1}=${2}
echo ${user}
if [ "${user}" = "root" ] || [ "${user}" = "" ];then
        exit;
fi
rm -f /usr/local/php54/etc/fpm.d/${user}.conf
/usr/sbin/groupdel ${user}
/etc/init.d/php54-fpm reload

赋予上述文件的执行权限:

chmod +x /scripts/postwwwacct /scripts/postkillacct

 

给已有的用户创建PHP FPM POOL配置文件

用户多的话,手动创建是不可能的,这里提供一个Shell Script:

#!/bin/bash
cd /home/
killall -9 php-fpm
killall -9 httpd
for username in *
do
        if [ "$(cat /etc/passwd | egrep "^${username}:")" != "" ];then
                echo "Create PHP FPM configration file for user ${username}." && /usr/local/cpanel/scripts/postwwwacct user ${username} && echo "${username}.conf create successfully." || echo "${username}.conf create unsuccessfully."
        fi
done
service php54-fpm start
service httpd start

如果一切无误,启动PHP FPM:

service php54-fpm start

 

重建所有站点的Apache配置文件

先添加一个Apache的用户:

useradd -rmd /var/www -s /sbin/nologin apache

修改cPanel/WHM的Apache配置文件模板,进入/var/cpanel/templates/apache2_4(如果你之前使用的是Apache2.2,应该要进入/var/cpanel/templates/apache2)文件夹,创建自定义配置文件模板:

cp -a main.default main.local
cp -a ssl_vhost.default ssl_vhost.local
cp -a vhost.default vhost.local

这样cPanel/WHM就会使用.local结尾的作为模板创建Apache的配置文件。

修改main.local文件,找到此行:

User [% main.user.item.user %

将其更改为:

User apache

分别编辑ssl_vhost.local,vhost.local,在文件首行,也就是”<VirtualHost[% FOREACH ipblock IN vhost.ips %] [% ipblock.ip %]:[% ipblock.port %][% END %]>”之前,加入这些代码:

<Proxy "unix:/usr/local/php54/sockets/[% vhost.user %].sock|fcgi://php-fpm54.[% vhost.user %]/">
        ProxySet min=0
</Proxy>

接下来,找到“UserDir enabled [% vhost.user %]”,在他后面三行的“[% END -%]”后加入一行:

ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/usr/local/php54/sockets/[% vhost.user %].sock|fcgi://php-fpm54.[% vhost.user %][% vhost.documentroot %]/$1

如图所示:

ProxyPassMatch

 

完成上述修改后,执行:

/scripts/rebuildhttpdconf

重建所有用户的配置文件。

最后,让Apache对配置文件进行检测:

/usr/local/apache/bin/apachectl configtest

如果出现类似的提示:

[Thu Oct 02 17:45:30.490596 2014] [:crit] [pid 752:tid 3067594544] Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe.  You need to recompile PHP.
AH00013: Pre-configuration failed

那你离成功不远了,访问Configure PHP and suEXEC,把PHP 5 Handler改为none即可:

PHP 5 Handler

一般提交后,WHM会自动帮你重启Apache,不过我这里建议最好还是再使用configtest参数检测一下,并重启Apache。

 

到这里,改造就完成了,可以用雅黑PHP探针对PHP环境进行检测:

雅黑PHP探针

能看到“PHP运行方式”一项为“FPM-FCGI”,那就说明大功告成了。

105 Posts

自信、努力、活出精彩;以前未所见的颜色,绘大千世界!
View all posts

3 thoughts on “让cPanel/WHM的Apache使用PHP FPM”

Leave a reply

Your email address will not be published. Required fields are marked *