起因

阿里云轻量应用服务器的 WordPress 镜像是“开箱即用”的代价:环境固定,PHP 版本停在镜像制作时的老版本,而新的 WordPress 和插件不断要求更新的 PHP。想升级,只能自己动手在镜像环境里再编译一个新 PHP。

需求

  • 把镜像默认的旧 PHP 升级到 PHP 7.3,站点继续由 Apache 提供
  • 旧版本保留可回退,升级失败不至于整站瘫掉

技术实现原理

升级 = “再装一个”,不是“覆盖旧的”

新 PHP 编译安装到独立的 /usr/local/php73 目录,旧版本原地不动;要回退,把 Apache 指回旧 fpm 即可。这是生产环境换运行时的通用安全姿势:先并存、验证、再切换。

切换的枢纽:Apache 与 php-fpm 之间的 socket

这套镜像里 Apache 不自己解释 PHP,而是把请求交给 php-fpm(FastCGI)。两边靠一个 unix socket 文件接头:fpm 在 www.conf 里 listen 一个 sock 路径,Apache 的 httpd.conf 里把 SetHandler 指到同一个路径。所以“升级 PHP”的完整动作其实只有两步——起一个新版 fpm 监听新 socket,再把 Apache 的指向改成新 socket。两个 fpm 各听各的 socket、可以并存,这也是后文“不停止旧版也不影响”的原因。

小内存机器为什么要先配 SWAP

编译是把几十 MB 源码翻译成机器码,gcc 需要大量内存;1G 内存的轻量服务器上,内核会因内存耗尽直接杀掉编译进程(表现为 cc1 被 killed)。SWAP 拿磁盘块冒充内存垫底,让编译撑过去——代价是慢,但对一次性的编译任务完全可以接受。

libzip 与 zipconf.h

PHP 7.3 要求 libzip >= 0.11,yum 源里的版本达不到,只能源码装新版;新版把头文件装到 /usr/local/lib/libzip/include/,而编译器默认只找 /usr/local/include,所以要补一个软链接。两步都是在填“源码安装的库与系统默认搜索路径不一致”这个坑。

fpm pool 配置(www.conf)各项在管什么

pm = dynamic 表示进程数按负载伸缩:min/max_spare_servers 圈定空闲进程的波动区间,max_children 是上限(内存小的机器要调小,每个子进程都是一份完整 PHP 运行时);max_requests 让子进程处理若干请求后重生,防内存泄漏累积;slowlog 记录执行慢的请求,是排查卡顿的第一入口。

注意事项

  • 建议使用默认的环境操作
  • WordPress镜像用的是4.8.1这个版本
  • wget缓慢请下载到本地后上传root目录
  • 务必使用root账户执行

安装依赖

1
2
3
yum install -y libxml2-devel openssl-devel curl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel xslt libxslt-devel
yum update -y curl curl-devel
yum remove -y libzip

配置SWAP

如果内存是低于1GB的,则需要配置一下swap,编译过程需要依赖更多内存,内存太低会报错

1
2
3
dd if=/dev/zero of=/swap bs=1M count=2048
mkswap -f /swap
swapon /swap

安装libzip

PHP7.3要求libzip版本大于0.11,但yum安装版本达不到要求,所以这里手动编译一下,该项不满足报错对应如下:

1
2
3
4
5
6
7
    checking for libzip... configure: error: system libzip must be upgraded to version >= 0.11
    
    wget https://nih.at/libzip/libzip-1.2.0.tar.gz
    tar -zxvf libzip-1.2.0.tar.gz
    cd libzip-1.2.0
    ./configure
    make && make install

编译好后还需要做一下软连接,否则会提示报错找不到zipconf.h文件

1
ln -s /usr/local/lib/libzip/include/zipconf.h /usr/local/include

编译安装PHP

1
2
3
4
5
    wget https://www.php.net/distributions/php-7.3.9.tar.gz
    tar -xvf php-7.3.9.tar.gz
    cd php-7.3.9
    ./configure --prefix=/usr/local/php73 --enable-soap --enable-cgi --with-mysql=/usr/local/mysql --with-mysqli=mysqlnd --with-gd --with-pdo-mysql=mysqlnd --with-zlib --enable-zip --enable-fpm --without-pear --disable-phar --with-openssl --enable-mbstring=all --with-jpeg-dir=/usr --with-png-dir=/usr --with-curl --with-freetype-dir=/usr --enable-gd-native-ttf --with-xsl=/usr --enable-calendar --enable-exif --enable-ftp --with-iconv --enable-bcmath --with-mcrypt=/usr/local/libmcrypt --enable-opcache
    make && make install

这个编译过程会比较长,编译完成后修改一下配置文件

将启动脚本复制到init.d中。

1
cp /root/php-7.3.9/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm73

给启动脚本加上执行权限

1
chmod +x /etc/init.d/php-fpm73

将默认配置文件复制为.conf文件

1
cp /usr/local/php73/etc/php-fpm.conf.default ${PHP73_DIR}/etc/php-fpm.conf

添加pool的配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
cat << EOF > /usr/local/php73/etc/php-fpm.d/www.conf
[www]
listen = /home/www/logs/php73-fpm.sock
listen.mode = 0666
user = www
group = www
pm = dynamic
pm.max_children = 128
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 300
rlimit_files = 1024
slowlog = /home/www/logs/php73-fpm-slow.log
EOF

至此PHP已经编译配置完成,接下来需要修改apache使用新版的php

修改apache

镜像中默认是用的php-fpm,使用的是socket方式的监听,Apache对应配置文件

1
    /usr/local/apache/conf/httpd.conf

可以看到其中配置如下

我们只需要修改其中socket的文件路径指向到新版本的PHP就可以了,在/usr/local/php73/etc/php-fpm.d/www.conf有指定,新的配置到/home/www/logs/php73-fpm.sock即可,如图

重启服务

停止旧版本的PHP(实际不停止也不影响,停止可以减少一些系统资源占用)

1
/etc/init.d/php-fpm stop

启动新版PHP-FPM

1
/etc/init.d/php-fpm73 start

启动报错请修改文件名

1
修改/usr/local/php73/etc/php-fpm.conf.default为php-fpm.conf

重启apache

1
/etc/init.d/apachectl restart

版本验证

站点根目录是/home/www/htdocs/,放入phpinfo探针即可检测PHP版本

phpinfo 版本验证截图

至此轻量应用服务器wordpress镜像更新PHP版本操作已完成,可以愉快的更新wordpress到新版本了。

踩坑记录与注意事项

  • socket 路径两处必须一字不差:www.conf 的 listen 与 httpd.conf 指向的路径对不上,Apache 直接 502/503,这是切换失败的头号原因;
  • PHP 7 移除了老 mysql 扩展:代码里还有 mysql_xxx 函数(PHP 5 写法)会报函数不存在——升级 PHP 最大的隐性成本是代码改造,mysqli/PDO 才是活路;configure 里的 –with-mysql 在 PHP 7 已不被识别(只给警告),真正生效的是 –with-mysqli=mysqlnd;
  • 扩展要按新版本重编:yaf、redis 这类 .so 是按 PHP 内部 ABI 编译的,换了 7.3 就要用 7.3 的 phpize 重编一遍,直接复制旧 .so 无效;
  • socket 权限:listen.mode = 0666 保证 Apache 的运行用户能读写 socket,权限太紧会连接被拒;
  • SWAP 用完可以拆:dd 出来的 /swap 占 2G 磁盘,常驻会影响性能(数据库机器不建议长期开),升级验证完成后 swapoff 再删文件;
  • 升级前备份旧配置:思路虽是并存安装,但 init.d 服务脚本名是共享的,留好旧 php.ini 与 httpd.conf 备份才能快速指回去;
  • 验证顺序:先命令行 php -v 确认二进制,再放 phpinfo 探针确认 Apache 真切到了新 fpm,最后进 WordPress 站点健康检查。