是实测确认扩展路径那一环,我把它固定成“看 /proc 里的 maps”会更稳,另外回滚那块建议连配置一起备份,不然降级也不干净。
先说你提的模块清单对比。php-fpm8.1 -m 和 php -m 对一遍能抓出“FPM 没升”,但抓不到另一种更隐蔽的情况:两边版本一致、扩展清单也一致,可 FPM 加载的 .so 其实还是旧目录里的。直接读进程映射最实在:cat /proc/$(cat /run/php/php8.1-fpm.pid)/maps | grep '\.so',再跟 php -i | grep extension_dir 的结果对照。Debian 系升级大版本后 /etc/php/8.1/fpm/conf.d 和 /etc/php/7.4/fpm/conf.d 会并存,extension=xxx.so 靠扫目录加载,一旦 extension_dir 写死在 pool 或 ini 里,新二进制配旧 .so 就是启动 warning 甚至段错误,这个只能看 maps 才认得出。
入口巡检那条 grep 命令很好用,再补两个位置:/etc/php/*/fpm/pool.d/*.conf 里的 env[PATH] 和 php_admin_value[extension_dir],以及 systemd unit 的 EnvironmentFile=。还有一点容易误会——update-alternatives --display php 只管 CLI,FPM 的 unit 文件名里版本号是写死的,alternatives 切了新版本,systemctl status php7.4-fpm 照样活着,这条别当成验证手段。
回滚包我建议再往前一步:光拷 /var/cache/apt/archives 不够,apt 定期 clean 或装新版时同名旧包容易被覆盖,确认版本号后 apt-mark hold 或者直接记下包全名(如 php8.1-fpm=8.1.2-1ubuntu2.14),降级时用带版本号的 install。同时把整个 /etc/php tar 一份,新旧版本的 pool、ini 混着留是降级后最常见的 502 来源。灰度我更倾向按 upstream 切机器而不是按域名,这样回滚就是改一行 server 指向。
最后一个坑:opcache.preload 指定的 preload 脚本,里面 require 的类在降级或大版本回退后编译缓存不兼容,回滚时除了清 opcache 还得确认 preload 文件路径在新版本下存在,否则 FPM 直接起不来,报错还只在 error log 里,面板上看是绿的。