在一台物理机上安装两个版本相同的PHP
有时候因为特殊情况(嗯,比如某些公司开发出来的不可理喻的应用程序)需要在一台物理机上安装两个版本相同(或不同)的运行环境,并且因为一些其他限制,还不能在docker里分开跑,于是就有了本文。
通常我们的做法是yum安装php,但为了达成本文的目标,我们需要编译安装。本文范例使用的php版本是5.3.29,我和你一样,非常不理解为什么要选用这个高不成低不就的版本。本范例的单一目标是在一台物理服务器上安装两个版本相同的php,你一定觉得我们脑袋秀逗了,但是这里有一个应用偏偏要这个版本,而且还要同时运行zts和nts的环境。很好,我想你已经明白软件开发商是有多么的愚蠢了。
首先编译一个ZTS版本的php,在编译前请将所需的各种依赖和环境先安装以及编译好,不知道需要哪些的话请在博客内搜索之前关于编译php的文章。
./configure --prefix=/usr/local/php53zts --with-config-file-path=/etc/php53zts --with-config-file-scan-dir=/etc/php.d --enable-mbstring --with-mcrypt --with-curl --with-gettext --with-bz2 --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --enable-shmop --enable-calendar --enable-ftp --with-openssl --with-zlib --enable-exif --with-gmp --enable-sysvmsg --enable-sockets --enable-wddx --libdir=/usr/lib --with-libdir=lib --with-gd --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --with-freetype-dir=/usr/lib --enable-zip --enable-sockets --enable-ftp --without-pear --enable-fpm --enable-maintainer-zts
特别要注意的是--prefix=、--with-config-file-path=这两条定义,这里将要定义该php安装到哪个目录,如果不定义就是默认的路径,然而我们需要安装两个php,所以一定要定义。编译完成并make install后,程序会汇报它的路径,本范例中,ZTS版本的路径为
Installing PHP SAPI module: fpm Installing PHP CLI binary: /usr/local/php53zts/bin/ Installing PHP CLI man page: /usr/local/php53zts/man/man1/ Installing PHP FPM binary: /usr/local/php53zts/sbin/ Installing PHP FPM config: /usr/local/php53zts/etc/ Installing PHP FPM man page: /usr/local/php53zts/man/man8/ Installing PHP FPM status page: /usr/local/php53zts/share/php/fpm/ Installing build environment: /usr/lib/build/ Installing header files: /usr/local/php53zts/include/php/ Installing helper programs: /usr/local/php53zts/bin/ program: phpize program: php-config Installing man pages: /usr/local/php53zts/man/man1/ page: phpize.1 page: php-config.1 /tmp/php-5.3.29/build/shtool install -c ext/phar/phar.phar /usr/local/php53zts/bin ln -s -f /usr/local/php53zts/bin/phar.phar /usr/local/php53zts/bin/phar Installing PDO headers: /usr/local/php53zts/include/php/ext/pdo/
接下来需要编译一个NTS版本的环境,不过我在编译中遇到了一个奇特的错误,提示缺少了某个库,但ZTS编译时并未提示,为防万一,我又重新编译了缺少的库libmcrypt
cd /tmp wget http://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz/download tar zxvf libmcrypt-2.5.8.tar.gz cd libmcrypt-2.5.8/ ./configure make && make install /sbin/ldconfig cd libltdl/ ./configure --enable-ltdl-install make && make install
按序执行上述命令后,再次开始编译NTS的PHP
./configure --prefix=/usr/local/php53 --with-config-file-path=/etc/php53 --with-config-file-scan-dir=/etc/php.d --enable-mbstring --with-mcrypt --with-curl --with-gettext --with-bz2 --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --enable-shmop --enable-calendar --enable-ftp --with-openssl --with-zlib --enable-exif --with-gmp --enable-sysvmsg --enable-sockets --enable-wddx --libdir=/usr/lib --with-libdir=lib --with-gd --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --with-freetype-dir=/usr/lib --enable-zip --enable-sockets --enable-ftp --without-pear --enable-fpm
除了--prefix=、--with-config-file-path=这两条定义外,相信你已经看出来NTS和ZTS版本在编译时的区别,对,就取决于一条编译参数--enable-maintainer-zts。编译后NTS版本的路径为
Installing PHP SAPI module: fpm Installing PHP CLI binary: /usr/local/php53/bin/ Installing PHP CLI man page: /usr/local/php53/man/man1/ Installing PHP FPM binary: /usr/local/php53/sbin/ Installing PHP FPM config: /usr/local/php53/etc/ Installing PHP FPM man page: /usr/local/php53/man/man8/ Installing PHP FPM status page: /usr/local/php53/share/php/fpm/ Installing build environment: /usr/lib/build/ Installing header files: /usr/local/php53/include/php/ Installing helper programs: /usr/local/php53/bin/ program: phpize program: php-config Installing man pages: /usr/local/php53/man/man1/ page: phpize.1 page: php-config.1 /tmp/php-5.3.29/build/shtool install -c ext/phar/phar.phar /usr/local/php53/bin ln -s -f /usr/local/php53/bin/phar.phar /usr/local/php53/bin/phar Installing PDO headers: /usr/local/php53/include/php/ext/pdo/
接下来很重要的一步是为这两个版本相同但是运行模式不同的php环境各自配置相关的服务启动脚本
ZTS版本的服务启动脚本:
cd /etc/init.d vi zts-php-fpm 复制黏贴下面的代码 #! /bin/sh ### BEGIN INIT INFO # Provides: php-fpm # Required-Start: $remote_fs $network # Required-Stop: $remote_fs $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts php-fpm # Description: starts the PHP FastCGI Process Manager daemon ### END INIT INFO prefix=/usr/local/php53zts exec_prefix=${prefix} php_fpm_BIN=/usr/local/php53zts/sbin/php-fpm php_fpm_CONF=/etc/zts-php-fpm.conf php_fpm_PID=/var/run/zts-php-fpm.pid php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID" wait_for_pid () { try=0 while test $try -lt 35 ; do case "$1" in 'created') if [ -f "$2" ] ; then try='' break fi ;; 'removed') if [ ! -f "$2" ] ; then try='' break fi ;; esac echo -n . try=`expr $try + 1` sleep 1 done } case "$1" in start) echo -n "Starting php-fpm " $php_fpm_BIN --daemonize $php_opts if [ "$?" != 0 ] ; then echo " failed" exit 1 fi wait_for_pid created $php_fpm_PID if [ -n "$try" ] ; then echo " failed" exit 1 else echo " done" fi ;; stop) echo -n "Gracefully shutting down php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -QUIT `cat $php_fpm_PID` wait_for_pid removed $php_fpm_PID if [ -n "$try" ] ; then echo " failed. Use force-quit" exit 1 else echo " done" fi ;; force-quit) echo -n "Terminating php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -TERM `cat $php_fpm_PID` wait_for_pid removed $php_fpm_PID if [ -n "$try" ] ; then echo " failed" exit 1 else echo " done" fi ;; restart) $0 stop $0 start ;; reload) echo -n "Reload service php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -USR2 `cat $php_fpm_PID` echo " done" ;; *) echo "Usage: $0 {start|stop|force-quit|restart|reload}" exit 1 ;; esac
NTS版本的服务启动脚本:
#! /bin/sh ### BEGIN INIT INFO # Provides: php-fpm # Required-Start: $remote_fs $network # Required-Stop: $remote_fs $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts php-fpm # Description: starts the PHP FastCGI Process Manager daemon ### END INIT INFO prefix=/usr/local/php53 exec_prefix=${prefix} php_fpm_BIN=/usr/local/php53/sbin/php-fpm php_fpm_CONF=/etc/nts-php-fpm.conf php_fpm_PID=/var/run/nts-php-fpm.pid php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID" wait_for_pid () { try=0 while test $try -lt 35 ; do case "$1" in 'created') if [ -f "$2" ] ; then try='' break fi ;; 'removed') if [ ! -f "$2" ] ; then try='' break fi ;; esac echo -n . try=`expr $try + 1` sleep 1 done } case "$1" in start) echo -n "Starting php-fpm " $php_fpm_BIN --daemonize $php_opts if [ "$?" != 0 ] ; then echo " failed" exit 1 fi wait_for_pid created $php_fpm_PID if [ -n "$try" ] ; then echo " failed" exit 1 else echo " done" fi ;; stop) echo -n "Gracefully shutting down php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -QUIT `cat $php_fpm_PID` wait_for_pid removed $php_fpm_PID if [ -n "$try" ] ; then echo " failed. Use force-quit" exit 1 else echo " done" fi ;; force-quit) echo -n "Terminating php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -TERM `cat $php_fpm_PID` wait_for_pid removed $php_fpm_PID if [ -n "$try" ] ; then echo " failed" exit 1 else echo " done" fi ;; restart) $0 stop $0 start ;; reload) echo -n "Reload service php-fpm " if [ ! -r $php_fpm_PID ] ; then echo "warning, no pid file found - php-fpm is not running ?" exit 1 fi kill -USR2 `cat $php_fpm_PID` echo " done" ;; *) echo "Usage: $0 {start|stop|force-quit|restart|reload}" exit 1 ;; esac
然后给两个服务脚本赋予执行权限,并加入系统服务中
chmod +x /etc/init.d/zts-php-fpm chmod +x /etc/init.d/nts-php-fpm chkconfig --add zts-php-fpm chkconfig --add nts-php-fpm chkconfig zts-php-fpm off chkconfig nts-php-fpm off chkconfig zts-php-fpm --level 3 on chkconfig nts-php-fpm --level 3 on
接下来需要更改两个版本的php-fpm的配置文件,使服务监听不同的端口。在本范例中,我们让ZTS版本的php监听9001端口,而NTS版本的php监听9002端口。这部分比较简单,将php-fpm.conf文件复制两份,一份命名zts-php-fpm.conf一份命名nts-php-fpm.conf,然后修改其中的监听端口即可,默认端口是9000.修改后,使用命令分别运行两个php-fpm
service zts-php-fpm start service nts-php-fpm start
通过查看netstat -lptn可以看到有两个fpm进程监听不同的端口,此时只要上级请求转发服务如nginx,转发php请求到相应的端口就可以区分哪些去ZTS版本解析,哪些去NTS版本解析。
最后,需要特别注意的是,在本例中编译时对mysql库文件的引入已经不再使用
--with-mysql=/usr/local/mysql --with-mysqli=/usr/local/mysql --with-pdo-mysql=/usr/local/mysql
而是使用php官方建议的内置数据驱动
--with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd
建议编译php5.3版本及以上时使用该编译参数替代。