美文网首页
记一次生产升级事件(php)

记一次生产升级事件(php)

作者: 码哥说 | 来源:发表于2020-09-04 13:34 被阅读0次

    背景

    咸鱼君所在公司的安全部门, 扫描出项目的生产服务安全漏洞,并且很负责任的提供了两份扫描文档,

    一份针对nginx(1.1.6),
    一份针对php(7.1.3),

    并限期一周内改善完!

    咸鱼君粗略一扫,全都是服务软件的漏洞,

    于是乎, 两个选择摆在眼前:

    1.根据文档,按图索骥, 一个补丁一个补丁的“劳模打补丁”;
    2.直接升级软件到最新版, 当然, 得确保服务的代码必须要适配!

    于是咸鱼君“认真”考虑了一下(实在是懒,补丁太多,当然,也是觉得升级了对应用代码影响不大), 决定升级服务软件来解决漏洞!

    下面,详细介绍咸鱼君的升级过程~

    评估升级风险!

    有相似需求的朋友一定要认真评估“直接升级服务软件版本”方案是否适合自己的业务, 因为服务软件的版本每次升级都会有些变化, 比如php就会舍弃一些函数, 万一项目代码用到了, 结果你直接升级了php
    , 最后导致生产的服务代码跑不起来,那就悲剧了!!

    评估升级风险的方式,咸鱼君采用的是本地升级服务软件,测试项目代码是否正常~

    有条件的也可以搞台克隆生产环境的服务器, 而后测试升级操作~

    备份备份备份!

    先找到php 的安装目录
    whereis php
    
    查看当前版本(7.1.3)
    /usr/local/php/bin/php -v 
    
    查看当前configure编译参数.这个要保存下,编译新php时要用!
    php -i | grep configure 
    

    这里列出来的数据格式不对,需要处理一下,建议小伙伴保存在txt中,然后将单引号“ ’ ”替换为空值即可

     ./configure  
    --prefix=/usr/local/php
     --with-config-file-
    path=/usr/local/php/etc 
    --enable-fpm 
    --with-mcrypt 
    --enable-mbstring 
    --enable-pdo 
    --with-curl 
    --disable-debug 
    --disable-rpath 
    --enable-inline-optimization 
    --with-bz2 
    --with-zlib 
    --enable-sockets 
    --enable-sysvsem 
    --enable-sysvshm 
    --enable-pcntl 
    --enable-mbregex 
    --with-mhash 
    --enable-zip 
    --with-pcre-regex 
    --with-mysqli 
    --with-gd 
    --with-jpeg-dir 
    --with-freetype-dir 
    --enable-calendar 
    --with-openssl 
    --with-png-dir 
    --with-zlib-dir 
    --enable-gd-native-ttf 
    --with-xsl 
    --with-gettext 
    --with-pdo-mysql
    
    备份旧版的php文件夹
    sudo cp -r /usr/local/php  /usr/local/php_7.1_bac 
    

    php升级

    接下来我们去 https://www.php.net/downloads.php找到合适的稳定的php版本来下载, 记得一定要详细看change log,来决定要不要升级! 否则生产的服务代码因为版本问题挂了,那就悲剧了!! 咸鱼君考察后,选择了 php7.4.4稳定版,粗略一看,应该没问题(事实上后面发现了个小问题!险些悲剧!)
    下载
    //切换会用户目录(下载文件时先切换自己的目录,这是个好习惯!)
    cd ~ 
    wget https://www.php.net/distributions/php-7.4.4.tar.gz 
    
    解压
    tar -zxvf php-7.4.4.tar.gz
    
    配置编译参数 (注意, 这里有很多问题, 为了流程的连贯性, 建议先往下看,读完全文, 最后再执行, 咸鱼君实力踩坑!)
    cd php-7.4.4
    //这里的***代表之前保存的编译参数
    //但是php7.4.4和之前的有些许不同,所以需要大家先转换!!!!
    //务必先不要执行,看完全文再执行,免得踩坑~
    ./configure  --prefix=*** 
    
    编译
    sudo make
    
    安装
    sudo make install
    
    恢复配置
    cd /usr/local/php_7.1_bac/
    sudo cp etc/php.ini  /usr/local/php/etc/php.ini
    sudo cp etc/php-fpm.conf /usr/local/php/etc/php-fpm.conf
    sudo cp etc/php-fpm.d/www.conf /usr/local/php/etc/php-fpm.d/
    
    切换新版php
    //平滑重启
    kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
    
    //当然,有时候急于等待结果的话,可以杀死再重启(不建议)
    //咸鱼君为了快速验证,所以采用杀死再重启的方式,小伙伴们视实际情况而定
    cd /usr/local/php/sbin/
    sudo killall php-fpm
    sudo ./php-fpm
    
    最后,确认服务正常,去访问下生产的服务,是不是正常,如果发现不对,立刻执行以下命令恢复旧php
    cd /usr/local
    sudo killall php-fpm
    sudo rm -r php
    sudo cp -r php_7.1_bac/  php
    sudo /php/sbin/php-fpm
    

    如果真像上面罗列的升级过程一样简单就好了,事实上,咸鱼君升级php7.4.4的过程中碰到了很多的,并且有些问题是网上找不到明确解答的!, 接下来听咸鱼君细细道来~

    php编译期间缺包问题

    No package '*******' found

    碰到这问题很好解决,缺啥装啥即可!
    这里咸鱼君碰到了如下的包缺失问题, 要是你碰到的和我不一样,没关系, 网上这类的问题解决贴一大把,主要因系统环境而异!

    No package 'libjpeg' found
    sudo yum install libjpeg libjpeg-devel
    
    No package 'libxml-2.0' found
    sudo yum -y install libxml2
    sudo yum -y install libxml2-devel
    
    No package 'sqlite3' found
    sudo yum install sqlite-devel
    
    No package 'oniguruma' found
    sudo yum install oniguruma oniguruma-devel
    
    No package 'libpng' found
    sudo yum install libpng-devel
    

    以上包都很好解决,下面这个包有点“意思”,

    No package 'libzip' found

    Please reinstall the libzip distributio 或 configure: error: system libzip must be upgraded to version >= 0.11
    这个包比较特别, 不知道是不是php7.4.4的适配问题, 咸鱼君发现 yum install 安装的版本比较低,并且下载的1.2.1这个包安装上还是识别不了,只有1.2.0可以用!!!
    yum remove libzip libzip-devel     //卸载yum安装的低版本
    wget https://nih.at/libzip/libzip-1.2.0.tar.gz
    tar -xvf libzip-1.2.0.tar.gz
    cd libzip-1.2.0
    ./configure
    make
    ////这句一定要执行,不然编译安装php还是报错找不到!!
    export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"   
    sudo ldconfig
    sudo make install
    

    php执行时加载“.so”扩展失败问题

    咸鱼君安装完各种包,兴冲冲的执行

    cd /usr/local/php/sbin/
    sudo killall php-fpm
    sudo ./php-fpm
    

    时,发现,报错

    PHP Warning: PHP Startup: Unable to load dynamic library “******.so”

    好吧, 折腾呗!
    根本原因就是, 原来编译安装的扩展, 比如“redis”这类的,只适用于当时的php, 所以我们要想继续使用, 则必须得用新的php重新编译这些扩展,重新安装!

    这里注意的时,有些扩展在php的安装包里就有,所以不用额外去下载~

    Unable to load dynamic library “ bcmath.so”
    cd php-7.4.4/ext/bcmath 
    /usr/local/php/bin/phpize
    ./configure --with-php-config=/usr/local/php/bin/php-config
    sudo make
    sudo make install
    
    Unable to load dynamic library “redis.so”
    wget https://pecl.php.net/get/redis-5.2.1.tgz
    tar -axvf redis-5.2.1.tgz
    cd redis-5.2.1
    /usr/local/php/bin/phpize
    ./configure --with-php-config=/usr/local/php/bin/php-config
    sudo make
    sudo make install
    

    咸鱼君就碰到这两个, 安装完后

    cd /usr/local/php/sbin/
    sudo killall php-fpm
    sudo ./php-fpm
    

    好吧,再次报错, 原来是新的扩展打上后, 扩展路径和以前不一样了,所以需要修改php.ini, 注意, 安装扩展时,有返回一个安装路径, extension_dir这填充这个路径即可

    sudo vim /usr/local/php/etc/php.ini
    

    修改

    extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20190902/"
    

    最后

    cd /usr/local/php/sbin/
    sudo killall php-fpm
    sudo ./php-fpm
    

    好的,好像没问题了!!(剧透, 接下来有两个大坑!!!)

    检查服务正常阶段

    看似nginx和php都升级成功了,也不曾有报错了!
    接下来我们来到最后阶段, 访问下项目, 看下是不是正常运行!!

    大坑一: 验证码不显示!

    咸鱼君项目采用的是Laravel5.6 + captcha做的验证码展示, 结果升级后发现,验证码显示不出来, 图片请求全是500错误, 打开Laravel Debug模式,看到报错

    Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined function Intervention\Image\Gd\imagettfbbox()

    一看, php的gd扩展有问题啊!!!
    赶紧去ssh生产,查看gd扩展是不是正常安装的

    php -m | grep gd
    

    发现, gd扩展是有的!
    这就奇怪了, 随后网上搜索下, 发现了各种“大佬”帖子的“解决方案”
    大致方案如下

    1. php重新编译gd扩展
    2. 独立下载gd扩展编译安装
      ………………
      好吧, 咸鱼君尝试了, 并没什么用!
      这里就不发出这类的“解决方案”了,免得小伙伴们和我一样走“歪路”!

    下面是重点了, 解决方案如下

    问题原因

    其实这个问题的确和 gd 扩展有关系, 主要是和gd扩展的子扩展freetype有关系!
    你需要先判断生产环境是不是安装了freetype

    //查看freetype安装目录
    find / -name freetype  
    

    要是安装了, 则编写一个小php脚本来判断有没有正确加载
    gdtest.php

    <?php
    
    if(extension_loaded(gd)){
    echo "can use gd<br>";
    foreach(gd_info() as $cate=>$value)
    echo "$cate: $value<br>";
    }else
    echo "can not use gd";
    ?>
    

    执行

    php test.php
    

    结果:

    can use gd
    GD Version: bundled (2.0.34 compatible)
    FreeType Support:                            #这里冒号后面没有值,说明gd不支持freetype
    T1Lib Support:
    GIF Read Support: 1
    GIF Create Support: 1
    JPEG Support: 1
    PNG Support: 1
    WBMP Support: 1
    XPM Support:
    XBM Support: 1
    JIS-mapped Japanese Font Support:
    

    若“FreeType Support: ”后为空不为“1”则说明,gd库没有freetype支持!

    问题似乎陷入了循环,明明安装了freetype, 但是gd扩展却显示不支持??!!!! 搜遍互联网,咸鱼君得到的全都是“重装freetype”,“重装gd扩展”,甚至“重装php”的“解决方案”!

    最后,咸鱼君无疑中看到一篇帖子,关于介绍php7.4.4的configure编译配置参数的变化的! 如下

    --with-gd                   改为 --enable-gd
    --with-freetype-dir     改为 --with-freetype
    --with-jpeg-dir           改为 --with-jpeg
    --with-webp-dir         改为 --with-webp
    --with-xpm-dir           改为 --with-xpm
    --with-libxml-dir         改为  --with-libxml 
    --enable-zip              改为 --with-zip
    --enable-zlib-dir        改为 --with-zlib
    

    大家看到

    --with-freetype-dir     改为 --with-freetype
    

    有没有眼前一亮?!! 是的,真相如此简单, 还记得我们之前

    php -i | grep configure
    

    查出的那一堆配置参数么, 我们只需要按图索骥, 把对应的旧版参数替换成新版参数, 然后重新

    ./configure  --prefix=***
    make 
    sudo make install
    

    即可!!!

    真是坑爹!

    另外,咸鱼君罗列的可能不全, 大家可以自行查看新版的参数

     //**代表你需要的扩展参数的关键字,来查看对应的配置方式
    ./configure -h  | grep **   
    

    最后编译配置php7.4.4的命令为

    ./configure  
    --prefix=/usr/local/php 
    --with-config-file-path=/usr/local/php/etc 
    --with-php-config=/usr/local/php/bin/php-configure 
    --enable-fpm 
    --enable-bcmath 
    --with-mcrypt 
    --enable-mbstring 
    --enable-pdo 
    --with-curl 
    --disable-debug 
    --disable-rpath 
    --enable-inline-optimization 
    --with-bz2 
    --with-zlib 
    --enable-sockets 
    --enable-sysvsem 
    --enable-sysvshm 
    --enable-pcntl 
    --enable-mbregex 
    --with-mhash 
    --with-zip 
    --with-pcre-regex 
    --with-mysqli 
    --enable-gd 
    --with-jpeg 
    --with-freetype 
    --enable-calendar 
    --with-openssl 
    --with-png 
    --with-zlib 
    --with-gd-native-ttf 
    --with-xsl 
    --with-gettext 
    --with-pdo-mysql
    

    大坑二: php7.4.4数组严格验证

    满心关系的解决完验证码问题, 登入了项目web后台,却突然发现报错

    Trying to access array offset on value of type null

    当时, 吓得咸鱼君一身冷汗, 迅速ssh上生产, 先恢复了旧php!

    之后,排查得到报错原因

    原来php7.4.4移除了很多notice报错,也就是可能老版的php只是warning,notice级别的报错,在新版里全部以error抛出!
    Trying to access array offset on value of type null
    

    就是其中的一种, php7.4.4不允许对值为null的数组不进行判空操作直接使用!

    举个例子

    $arr = //api返回;
    $name = $arr['name'];
    

    这样的操作就会抛出异常,所以我们需要先判断

    if(isset($arr['name'])){
        $name = $arr['name'];
    }
    

    可以看出,php也在进行一些类型的严格规范了!

    修改了这个问题, php就不抛错了, 咸鱼君再次上线了新版php~~

    至此, 顺利从php7.1.3升级至7.4.4!!

    希望咸鱼君的升级笔记能够帮到需要的小伙伴~

    升级需评估 ,否则要背锅~~

    相关文章

      网友评论

          本文标题:记一次生产升级事件(php)

          本文链接:https://www.haomeiwen.com/subject/azyzsktx.html