Recently I did a server migration from an older server to a newer server and in an attempt to help stability I wanted to see if there was a better way to do PHP FastCGI. In my research I came across running PHP using the fastCGI server that spins up a PHP on a TCP/IP port and allows the web server to connect to it. However this doesn't help with spawning or keeping track of instances or error recovery.
This is where PHP FPM comes in handy. It does all of the hard work for us, it spawns the processes and has a bunch of really awesome features that help run PHP as various different users as required with different PHP ini files and memory limits. After setting up PHP FPM I had to set up Apache.
mod_fcgid doesn't allow for remote connections, and as such I was unable to use it for what I needed it for. mod_fastcgi provides the FastCGIExternalServer configuration key, which is exactly what I needed.
"The FastCgiExternalServer directive defines filename as an external FastCGI application. If filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot. The filename does not have to exist in the local filesystem. URIs that Apache resolves to this filename will be handled by this external FastCGI application."
What this documentation does not state is that the path up to the last part of it has to exist in the local file system. So in my first couple of attempts I pointed it at /usr/local/www/fastcgi/php5.fcgi without having an actual fastcgi directory located in /usr/local/www/. There are a lot of examples that require creating a new FastCGIExternalServer for each and every VirtualHost this is unacceptable to me, the reason they require it is because they set the FastCGIExternalServer path to the folder where they are going to be serving files from.
In the end I found that after creating the /usr/local/www/fastcgi directory (and reading the mod_fastcgi source code) that all it does is make Apache believe a file exists in a certain directory, much like Alias, except Alias allows full paths to be aliased, not so here.
The AddType and Action add custom types and what the action should be when such a type is encountered. In this case the action is to redirect the request to /php5.fcgi which will handle the rest of the request. This does not require another FastCGI section per VirtualHost as each PHP request will just get shuttled to the php handler.
Do take note that I have specifically disallowed Apache to serve anything from the /usr/local/www/fastcgi/ folder, except for a single file php5.fcgi which is our FastCGIExternalServer file.
<IfModule mod_fastcgi.c>
Alias /php5.fcgi /usr/local/www/fastcgi/php5.fcgi
FastCGIExternalServer /usr/local/www/fastcgi/php5.fcgi -flush -host 127.0.0.1:9000
AddType application/x-httpd-fastphp5 .php
Action application/x-httpd-fastphp5 /php5.fcgi
<Directory "/usr/local/www/fastcgi/">
Order deny,allow
Deny from all
<Files "php5.fcgi">
Order allow,deny
Allow from all
</Files>
</Directory>
</IfModule>
Note that even-though in my last post concerning mod_fastcgi I as moving away from it, I am now doing the opposite, instead of moving from mod_fastcgi to mod_fcgid I'm back to mod_fastcgi, only because mod_fcgid doesn't offer the same functionality.
So far this has provided far more stability, along with PHP-FPM doing all of the process management I can now use a single PHP instance that is running on a single port for the various web servers I am testing. At the moment I have both Lighttpd and Apache using the same PHP-FPM running instance. It is faster, less memory is wasted and PHP-FPM is much better at process management than mod_fastcgi or mod_fcgid.
Update: I've since moved to a PHP-FPM with mod_fastcgi based setup which works much better than mod_fcgid could ever dream of, taking process management away from Apache and putting it where it actually makes sense has improved server load and improved website speed. See my new PHP-FPM with mod_fastcgi post for more information.
--
Errors, all they cause is trouble. The dreaded 500 error showed up when visiting my favourite tech website, the one I am the administrator for. This time I had enough, there was going to be no more playing around with PHP settings, attempting to figure out why PHP was suddenly dying and or why mod_fastcgi refused to retry a select() when it failed due to a system signal.
The server in question runs Apache in MPM worker mode since a threaded Apache is going to be faster than a pre-fork, besides this server does not have as much memory so it seemed to be better to have multiple threads rather than multiple processes, each of which would have their own memory segment. There however is the issue that this server also needs to run PHP, the accepted method to do so is to use mod_php along with Apache; however PHP is not thread safe.
The alternative is FastCGI, basically it spawns PHP processes as a separate stand-alone process, with their own memory space, much like Apache pre-fork, however now when one process grows to big, or when a process is no longer needed it can be cleaned up, keeping memory to a minimum. Also, FastCGI is perfectly thread safe, this means that with Apache running in MPM worker mode we could now still run our PHP scripts even when they were not thread safe.
Setting up mod_fastcgi is not that hard, it takes some httpd.conf configuration values, and off course the loadmodule is assumed here:
# Set up mod_fastcgi
<IfModule mod_fastcgi.c>
FastCgiIpcDir /var/tmp/fcgi-ipc/
FastCgiConfig -autoUpdate -singleThreshold 100 -killInterval 300 -idle-timeout 240 -pass-header HTTP_AUTHORIZATION
AddHandler fastcgi-script .fcgi .fcg .fpl
Action application/x-httpd-php5 /fastcgi-bin/php5.fcgi
AddType application/x-httpd-php5 .php .php5
</IfModule>
# Set up the script alias, basically anything in this directory gets executed as a fastcgi script
ScriptAlias /fastcgi-bin/ "/usr/local/www/fastcgi-bin/"
<Location /fastcgi-bin/>
Options ExecCGI
SetHandler fastcgi-script
Order allow,deny
Allow from all
</Location>
Because of errors that mod_fastcgi was throwing out at me, I figured mod_fcgid is worth a try. However all of the configurations I found required me to add an FCGIWrapper line into each of my VirtualHost blocks, which was an immediate no-no since there is well over 100 of those on the server, and I'd rather spend my time doing other things.
With some trial and error, and some Googling I put the following together for mod_fcgid, tested it out, and once it worked perfectly set Apache off running with the following configuration:
# Set up mod_fcgid
<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi .fcg .fpl
IPCCommTimeout 60
SocketPath /var/tmp/fcgi-ipc/
Action application/x-httpd-php5 /fastcgi-bin/php5.fcgi
AddType application/x-httpd-php5 .php .php5
</IfModule>
# Set up the script alias, basically anything in this directory gets executed as a fastcgi script
ScriptAlias /fastcgi-bin/ "/usr/local/www/fastcgi-bin/"
<Location /fastcgi-bin/>
Options ExecCGI
SetHandler fcgid-script
Order allow,deny
Allow from all
</Location>
Notice how similar they both are, that was the whole goal. mod_fcgid was supposed to be a drop in replacement, and thankfully it was. PHP was served as it once was, with a twist.
The php5.fcgi script is as follows for those of you trying to set this up as well:
#!/bin/sh
# To use your own php.ini, comment the next line and uncomment the following one
#PHPRC="/usr/local/etc"
#export PHPRC
PHP_FCGI_CHILDREN=4
export PHP_FCGI_CHILDREN
exec /usr/local/bin/php-cgi
Some things that that intrigued me is that my php processes seemed to be capped now. Where mod_fastcgi would spawn processes but never remove them or kill them when they were no longer needed, mod_fcgid keeps the running php processes to a sane limit instantly starting new processes when required to handle the requests coming in. This means that the server now has more free memory for MySQL, or file caches which has helped speed up other types of transfers as well. Even with the relatively short time that mod_fcgid has been in place it has been faster, more reliable and more sane than mod_fastcgi. Whichever one you pick, make sure to benchmark your server and check your error logs to solve common issues, the issue may not be what FastCGI module you picked but rather PHP itself that is causing the errors!