原本是安裝 fastcgi 來跑 php 7.0,後來因為遇到 chunked mode 的問題改裝 mod_fcgid,剛好記錄下來作法。
這篇是以 Ubuntu 16.04 + php 7.0 做範例:
Apache2 用 mod_fcgid 跑 php 7.0
在 Ubuntu 我習慣用 ppa 來裝 package,php 的 ppa 是 ppa:ondrej/php
$ sudo add-apt-repository ppa:ondrej/php -y $ sudo apt-get update
安裝 apache2 和 php 7.0
$ sudo apt-get install -y php7.0 php7.0-cgi libapache2-mod-fcgid
我這邊只安裝基本的 php 7.0 base,有需要其他的再自己安裝,基本上都是叫做 php7.0-xxx。
Apache mpm 原本 event 改用 worker 多線程
$ sudo a2dismod mpm_event $ sudo a2enmod fcgid mpm_worker rewrite
修改 fcgid.conf
$ sudo tee /etc/apache2/mods-available/fcgid.conf <<EOF <IfModule mod_fcgid.c> AddHandler fcgid-script .php FcgidWrapper /usr/local/bin/php-wrapper .php FcgidConnectTimeout 20 <IfModule mod_mime.c> AddHandler fcgid-script .php </IfModule> </IfModule> EOF
比較特別的是 FcgidWrapper,一般這邊指定的是 php-cgi,但官方有特別提到,如果要調整 mod_fcgid 的參數必須要透過 FcgidWrapper 來跑腳本
PHP applications are usually configured using the FcgidWrapper directive and a corresponding wrapper script. The wrapper script can be an appropriate place to define any environment variables required by the application, such as PHP_FCGI_MAX_REQUESTS or anything else. (Environment variables can also be set with FcgidInitialEnv, but they then apply to all applications.)
所以會透過 php-wrapper 這隻 script 處理變數後再執行 php-cgi,就像這樣:
$ tee /usr/local/bin/php-wrapper <<EOF #!/bin/sh # Set desired PHP_FCGI_* environment variables. # Example: # PHP FastCGI processes exit after 500 requests by default. PHP_FCGI_MAX_REQUESTS=0 export PHP_FCGI_MAX_REQUESTS # Replace with the path to your FastCGI-enabled PHP executable exec /usr/local/bin/php-cgi EOF
修改 PHP_FCGI_MAX_REQUESTS 的用意是預設 PHP_FCGI_MAX_REQUESTS 只能併發 500 個 processes,Apache2 建議改成 10000 或更高,如果 Request 超出這個數值則 FasCGI 會退出並且出現 500 Internal Server Error,如果要完全避免可以設為 0 但是會有 PHP application leaks resources 的問題。
By default, PHP FastCGI processes exit after handling 500 requests, and they may exit after this module has already connected to the application and sent the next request. When that occurs, an error will be logged and
500 Internal Server Error
will be returned to the client. This PHP behavior can be disabled by settingPHP_FCGI_MAX_REQUESTS
to 0, but that can be a problem if the PHP application leaks resources. Alternatively,PHP_FCGI_MAX_REQUESTS
can be set to a much higher value than the default to reduce the frequency of this problem.FcgidMaxRequestsPerProcess
can be set to a value less than or equal toPHP_FCGI_MAX_REQUESTS
to resolve the problem.
另外在 VirtualHost 這邊要允許 +ExecCGI 執行,否則你的 mod_fcgid 會跑不起來 …
sudo tee /etc/apache2/sites-available/000-default.conf <<EOF <VirtualHost *:80> DocumentRoot /var/www/htdocs/public <Directory /var/www/htdocs/public> Options -Indexes +ExecCGI AllowOverride All Order allow,deny Allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> EOF
啟動 Apache2 服務
$ sudo systemctl enable apache2 $ sudo systemctl start apache2
另外有一點蠻值得提的,mod_fcgid 預設會停用子線程管理,有關於效能和資源應用的問題:
PHP child process management (PHP_FCGI_CHILDREN) should always be disabled with mod_fcgid, which will only route one request at a time to application processes it has spawned; thus, any child processes created by PHP will not be used effectively. (Additionally, the PHP child processes may not be terminated properly.) By default, and with the environment variable setting PHP_FCGI_CHILDREN=0, PHP child process management is disabled.
預設啟動 Apache2 的時候沒有 connection 時不會有 cgi processes,只有連線進來的時候才會自動生成。
谢谢你! 非常有用的!
:)