最近我的 Server 升級到 CentOS 7 還有 PHP7,其實有以前的舊專案要測試會出點相容性問題,因此要讓新的 Server 能夠跑多個版本的 PHP,目前比較好的做法是用 SCL 的方式跑多個版本比較好管理,CentOS 官方本身有收納,可以看 http://mirror.centos.org/centos/7/sclo/x86_64/rh/ ,但官方僅收錄 php54 和 php55,其實是很少的。
我一直都是使用 Remi's RPM repository 提供的 PHP 套件來部屬,所以接下來介紹的也是使用 Remi 提供的 SCL 方式來部屬多版本 PHP。
部屬目標
- Nginx + php-fpm (Fast CGI 模式)
- php 5.6
- php 7.0
- PHP 主要版本是 7.0
安裝 Remi Repo
可以參見 http://blog.remirepo.net/pages/Config-en 有所有版本的 CentOS 安裝方式,CentOS 7 的方式如下 :
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
安裝 PHP 7.0 成為主要版本
yum --enablerepo=remi,remi-php70 install php php-fpm
這段下了之後會有 php cli 和 php-fpm 能用,我們未來跑的主要版本就是這個版本了。
安裝 PHP 5.6
yum --enablerepo=remi install php56 php56-php-fpm
這段下了之後,Server 上會有 PHP 5.6 的套件,但是套件內所有檔案會放在 /opt/remi/php56/root,看下圖列出的目錄結構,應該不難想像,就是模擬整個環境,因此所有設定都可以往這去修改,這不會影響到我們的主要版本。
CLI 模式下測試多版本運作
主要版本 PHP 7.0
[root@localhost root]# php -v PHP 7.0.0 (cli) (built: Dec 1 2015 17:53:27) ( NTS ) Copyright (c) 1997-2015 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies
這就是一般的方式執行了,接下來是 PHP 5.6
[root@localhost root]# scl enable php56 "php -v" PHP 5.6.16 (cli) (built: Nov 26 2015 07:03:47) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
或者也可以用絕對路徑
[root@localhost root]# /opt/remi/php56/root/bin/php -v PHP 5.6.16 (cli) (built: Nov 26 2015 07:03:47) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
更簡單點
[root@localhost root]# php56 -v PHP 5.6.16 (cli) (built: Nov 26 2015 07:03:47) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
到目前為止,看來 CLI 的部分確實能確認有多個版本的 PHP,如果也想要安裝 php 5.5 ,Remi Repo 也是有包的,目前是知道 php54 , php55 , php56 , php70 都有包成 SCL。
PHP-FPM 多版本運作
我們現在有了主要版本(7.0) 和 5.6 了,但是網站的部分必須要讓 php-fpm 兩種版本同時跑起來。
現在有個問題,預設情況下,php-fpm 的設定檔會以 tcpip port 9000 當作 fastcgi 監聽 port,那麼兩個版本並存怎麼辦 ?
所以一定要分別設定,主要版本我們可以不用動,但是 php56 的就要動了,也許可以將 php56 的監聽 port 改成 9001 也行,不過我比較喜歡 Unix Socket ,效率比較好,而且 Unix Socket 設定法在 CentOS 7 可能會遇到一些些權限問題,所以拿來介紹比較好
我以環境是 nginx 為例子,要修改的比較多,如果是跑 Apache ,其實不需要修改很多,可以斟酌
修改預設的 php-fpm.d/www.conf 設定檔,底下先以主要版本 PHP70 為例子,PHP56 也一樣可以如法炮製
- 用編輯器打開 /etc/php-fpm.d/www.conf
- 將 listen = 127.0.0.1:9000 這一行改成 listen = /var/run/php-fpm/php70-fpm.sock
- 將 user = nobody 及 group = nobody 改成 user = nginx 及 group = nginx
- 將 listen.owner 及 listen.group 改成 nginx
- 將 php_admin_value[error_log] 設定為 /var/log/nginx/php-fpm-error.log
- 將 php_value[session.save_path] 設定為 /var/lib/php/session-nginx
- 將 php_value[soap.wsdl_cache_dir] 設定為 /var/lib/php/wsdlcache-nginx
- 存檔
接下來建立一些目錄及檔案
touch /var/log/nginx/php-fpm-error.log chown nginx:adm /var/log/nginx/php-fpm-error.log mkdir /var/lib/php/session-nginx chown root:nginx /var/lib/php/session-nginx mkdir /var/lib/php/wsdlcache-nginx chown root:nginx /var/lib/php/wsdlcache-nginx
建立這些目錄及檔案其實是不想去修改 php-fpm 預設搭配 Apache 的目錄結構而已,所以另外設定給 nginx 用。
上述的過程中 listen = /var/run/php-fpm/php70-fpm.sock 這個要特別說明,因為 CentOS 7.1(或 RHEL 7.1) 的 systemd 導入了 PrivateTmp 的方式,如果我們將 Unix Socket 建立在 /tmp 或 /var/tmp ,其實會有權限問題,將來可能 Nginx 無法連上 php-fpm,解決的方法就是避開 /tmp 或 /var/tmp,又或者是修改 systemd 的 php-fpm 設定檔將 PrivateTmp 設為 false 也行。
主要版本都設定好了,PHP56 這個版本設定的方式一樣,但是請注意 PHP56 的路徑要由 /opt/remi/php56/root ,不要和主要版本一樣免得出問題。
PHP56 的 php-fpm/www.conf 是在 /opt/remi/php56/root/etc/php-fpm.d 底下,直接修改即可
都修改好之後,啟動兩個版本的 php-fpm 試試看
執行以下命令
systemctl enable php-fpm systemctl start php-fpm systemctl enable php56-php-fpm systemctl start php56-php-fpm
上述分別設定 php 兩個版本於開機時啟動,而且也一併當下啟動起來,如果沒意外,我們看 netstat 狀況會如下
[root@localhost /]# netstat -nap | grep php-fpm unix 2 [ ACC ] STREAM LISTENING 355842 17207/php-fpm: mast /var/run/php-fpm/php70-fpm-nginx.sock unix 2 [ ACC ] STREAM LISTENING 425377 25419/php-fpm: mast /opt/remi/php56/root/var/run/php-fpm/php56-fpm-nginx.sock unix 3 [ ] STREAM CONNECTED 355840 17207/php-fpm: mast unix 3 [ ] STREAM CONNECTED 355841 17207/php-fpm: mast unix 3 [ ] STREAM CONNECTED 425375 25419/php-fpm: mast unix 3 [ ] STREAM CONNECTED 425376 25419/php-fpm: mast
我們可以看見有兩個 Unix Sokcet 被建立了,這樣接下來輕鬆了。
Nginx 的設定
建立 php-fpm.conf 在 /etc/nginx/conf.d 之下,內容如下
upstream php-fpm-sock { server unix:/var/run/php-fpm/php70-fpm-nginx.sock; } upstream php56-fpm-sock { server unix:/opt/remi/php56/root/var/run/php-fpm/php56-fpm-nginx.sock; }
現在我們定義了兩個 upstream ,因此在 Virtual Host 中使用就簡單了
如果有多個 VirtualHost 就可以分別使用上述的 upstream 來運行不同的版本,例如
# server 1 server { # .... 略 .... location ~ \.php$ { root /srv/www/wordpress; fastcgi_pass php-fpm-sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } # server 2 server { # .... 略 .... location ~ \.php$ { root /srv/www/wordpress; fastcgi_pass php56-fpm-sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
上述 VirtualHost 設定檔定義 server 1 及 server 2 ,分別跑兩個不同版本 php,其實差別就在於 fastcgi_pass 這個參數,分別指向不同的 upstream,如此就可以讓 nginx 支援不同版本 PHP 了。
後記
有朋友問,docker 好像也可以做到,沒錯,但是 docker 比較肥,而且效能差了一些,管理上也不見得好管,SCL 的方式就是把套件另外安裝到某個路徑而已,不算虛擬化所以不會影響效能,要升級或移除也可以用 yum or rpm 方式移除。