Installing the FPM
You’ll need to be running PHP 5.3 or above. The IUS repository offers these packages for RHEL and CentOS.
PHP 5.3 and 5.4 have reached end of life status upstream. The IUS packages for those versions will not receive any further security updates. Please consider using a non-EOL version. Currently those are: 5.5, 5,6, and 7.0. Be aware that 5.5 will also reach EOL status on 2016-07-10.
yum –enablerepo=ius-archive install php53u-fpm
yum –enablerepo=ius-archive install php54-fpm
yum install php55u-fpm
yum install php56u-fpm
yum install php70u-fpm
- By default, you’ll get an FPM pool running as ‘apache’ or ‘php-fpm’ user configured in /etc/php-fpm.d/www.conf . That might be all you need.
- More likely though, you’ll want to run this as a particular user to avoid permissions issues with FTP.
- Multiple websites? Configure a pool for each! User separation, and allows config per website. For each pool you want to create, add a “.conf” file under /etc/php-fpm.d/. Call it username.conf for easy reference. You can name the pool at the top of the file – use the vhost domain name. Usefully, the pool name will show up in ps and ‘top -c’ reports, making it easy to see where the load is coming from.
- We need to configure the FPM pool(s) to listen on a socket instead of the default TCP port 9000. Shared memory is as good a place as any, but it doesn’t really matter.
- Also set the user and socket ownership/permissions if changing from apache. ‘someuser’ = your application username. Probably the same as your FTP login.
cd /etc/php-fpm.d
cp www.conf someuser.conf
mv www.conf www.conf.disabled
vim someuser.conf
Leave defaults, but make the following changes:
[somewebsite.com]
;listen = 127.0.0.1:9000
listen = /dev/shm/someuser-php.sock
listen.owner = someuser
listen.group = apache
listen.mode = 0660
user = someuser
group = apache
pm.max_children = 100
pm.start_servers = 35
pm.min_spare_servers = 35
The pm.max_children default of 50 is quite low, unless this is only one of many PHP pools. See note later on, about real memory usage! Absolutely something to keep an eye on. Apache returns a 500 if you hit pm.max_children. It will show in the Apache error logs, so shouldn’t be too hard to troubleshoot those 500 errors.
Start the service:
service php-fpm start
chkconfig php-fpm on
php_admin_flags and values
/etc/php.ini remains the place for global config.
Apache directives php_admin_flag and php_admin_value will no longer work in Apache config or .htaccess as they’re only for mod_php. Instead you can add lines to each /etc/php-fpm.d/someuser.conf like this:
php_admin_value[error_log] = /var/log/php/somewebsite.com-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 512M
Hint: logs will be written by the FPM pool user, not root. I.e. “someuser” in these examples (default apache). Make sure you choose a user writable log path and set up some rotation.
Install mod_fastcgi
We need talk fastcgi to the FPM backend. Apache < 2.4 cannot natively talk fastcgi, so we need to install mod_fastcgi.
Not packaged by Red Hat, epel, Rackspace IUS, so we have to install it from source. There are two options; for Magento I would the Nexcess patched SRPM.
mod_fastcgi does not seem to build on CentOS 7 at this time. More testing required, but providing this as a warning in the meantime if you find yourself in a RHEL flavor 7 environment.
Nexcess SRPM
This patched mod_fastcgi source fixes a known bug with Magento and CGI (see below). Nexcess have been kind enough to release it publicly: http://p ubfiles.nexcess.net/misc/
Attached to this article, January 2014, for reference, but do check for any updates from the link above. mod_fastcgi-2.4.6-3.el6.src.rpm
yum -y install httpd-devel rpm-build gcc
mkdir /home/rack/fastcgi
cd /home/rack/fastcgi
wget http://pubfiles.nexcess.net/misc/mod_fastcgi-2.4.6-3.el6.src.rpm
rpmbuild –rebuild mod_fastcgi-2.4.6-3.el6.src.rpm
rpm -ivh /root/rpmbuild/RPMS/x86_64/mod_fastcgi-2.4.6-3.el6.x86_64.rpm
rm -f /etc/httpd/conf.d/mod_fastcgi.conf
It will set up the config a little differently, so get rid of the packaged conf.d/mod_fastcgi.conf and create as below.
If you installed the Nexcess SRPM, ignore the next heading and skip to “Configure mod_fastcgi” now.
Or, compile from source directly
yum -y install httpd-devel gcc
mkdir /home/rack/fastcgi
cd /home/rack/fastcgi
wget https://github.com/whyneus/magneto-ponies/raw/master/mod_fastcgi-SNAP-0910052141.tar.gz
tar -zxf mod_fastcgi*
cd mod_fastcgi-*
make -f Makefile.AP2 top_dir=/usr/lib64/httpd
cp .libs/mod_fastcgi.so /usr/lib64/httpd/modules/
Building on Debian and probably Ubuntu, when non-free repo is not available
Check the worker type:
# apache2ctl -M 2>&1 | grep -i ‘mpm’
mpm_prefork_module (static)
In the example above, we have prefork:
# apt-get install apache2-prefork-dev gcc build-essential
If it’s threaded or worker:
# apt-get install apache2-threaded-dev gcc build-essential
# cd ~rack
# wget http://www.fastcgi.com/dist/mod_fastcgi-SNAP-0910052141.tar.gz
# tar -zxf mod_fastcgi*
# cd mod_fastcgi-*
# make -f Makefile.AP2 top_dir=/usr/share/apache2
# cp .libs/mod_fastcgi.so /usr/lib/apache2/modules/
Configure mod_fastcgi (RHEL Based Servers)
Create /etc/httpd/conf.d/fastcgi.conf with the following contents. Change “someuser” to the user you want to run your code. For multiple pools, just add more FastCGIExternalServer directives.
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
AddType application/x-httpd-php .php
Action application/x-httpd-php /php.fcgi
# Add index.php to the list of files that will be served as directory indexes.
DirectoryIndex index.php
# One per user pool – set up in /etc/php-fpm.d/someuser.conf
FastCGIExternalServer /dev/shm/someuser-php.fcgi -socket /dev/shm/someuser-php.sock -flush -idle-timeout
1800
FastCGIExternalServers can only be defined once, so we can put it in this global config file and then refer to it in as many VirtualHosts as you like. If you are only going to use it in one VirtualHost, you could define it there. Most applications won’t need the timeout (default 30) to be changed, but you might need that for heavy backend processing (like Magento Admin).
Configure mod_fastcgi (Debian Based Servers)
Create /etc/apache2/conf.d/mod_fastcgi.conf with the following contents. Change “someuser” to the user you want to run your code. For multiple pools, just add more FastCGIExternalServer directives.
LoadModule fastcgi_module /usr/lib/apache2/modules/mod_fastcgi.so
AddType application/x-httpd-php .php
Action application/x-httpd-php /php.fcgi
# Add index.php to the list of files that will be served as directory indexes.
DirectoryIndex index.php
# One per user pool – set up in /etc/php-fpm.d/someuser.conf
FastCGIExternalServer /dev/shm/someuser-php.fcgi -socket /dev/shm/someuser-php.sock -flush -idle-timeout
1800
FastCGIExternalServers can only be defined once, so we can put it in this global config file and then refer to it in as many VirtualHosts as you like. If you are only going to use it in one VirtualHost, you could define it there. Most applications won’t need the timeout (default 30) to be changed, but you might need that for heavy backend processing (like Magento Admin).
Action requires the “actions” module, which usually isn’t enabled by default on Debian/Ubuntu. So use to see apache2ctl -M 2>&1 | grep -i actions whether it is enabled, and followed by if not. a2enmod actions service apache2 restart if not.
Per VirtualHost config
<VirtualHost *:80>
Servername somewebsite.com
ServerAlias www.somewebsite.com
# FastCGI handler for PHP-FPM. See conf.d/fastcgi.conf
Alias /php.fcgi /dev/shm/someuser-php.fcgi
…
Disable mod_php
Move the config away, but importantly leave a file named php.conf, otherwise updates to PHP will put in a default config.
mv /etc/httpd/conf.d/php.conf /etc/httpd/conf.d/php.disabled
echo “# mod_php disabled, using mod_fastcgi with PHP-FPM instead” >> /etc/httpd/conf.d/php.conf
At this point, you might want to switch the Apache MPM to ‘worker’, in /etc/sysconfig/httpd
Side by Side config
Useful if you want to leave mod_php for legacy sites, or perhaps you’re switching one VirtualHost at a time…
- DO NOT use the application/x-httpd-php type, I couldn’t override that from mod_php
- DO NOT set the actions/handlers globally, we don’t want to break mod_php sites.
Your conf.d/fastcgi.conf should contain ONLY this:
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
# One per user pool – set up in /etc/php-fpm.d/username.conf
FastCGIExternalServer /dev/shm/apache-php.fcgi -socket /dev/shm/apache-php.sock -flush
DO put the following in your VirualHost to set the handler to something unique that isn’t x-application-php.
# Override default mod_php handler
AddHandler php-fpm .php
Action php-fpm /php.fcgi
# Alias for the handler pass to the FastCGIExternalServer in /etc/httpd/conf.d/fastcgi.conf
Alias /php.fcgi /dev/shm/apache-php.fcgi
# Ensure we still have index.php as an index
DirectoryIndex index.php
Memory Usage
PHP-FPM is quite good with memory usage, so what you see in ‘ps’ might be way over the mark. When your site is in production, at a busy time, or better, while Customer is doing load testing, try this:
for pid in $(ps aux | grep fpm | grep “pool www” | awk ‘{print $2}’); do pmap -d $pid | tail -1 ; done | sed
‘s/K//’ | awk ‘{sum+=$4} END {print sum/NR/1024}’
It will show you the average overhead, in MB, of each PHP FPM process, for FPM pool “www”. Be sure to look for the right pool name, if you changed it as above. You should be able to use this to accurately predict the total memory usage of your PHP, or work out a sensible max_children for the amount of RAM you have. To be super-conservative, just replace the last ‘awk’ with a “sort -n”, and go with the biggest one.
If you have bad code that leaks memory, maybe lower pm.max_requests (just like MaxRequestsPerChild for Apache/mod_php)
Monitoring
You might want to set up the /status handler in the PHP-FPM pool config. If you have, you could use the following to monitor it:
NB: the Alias needs to refer back to the “FastCGIExternalServer” set in /etc/httpd/conf. If you deviated from the normal “/status” path, then update the LocationMatch to reflect that.
# cat /etc/httpd/vhost.d/zzz-server-status.conf
## This dummy vhost is here to avoid RewriteRules on the primary website breaking /server-status handler.
<VirtualHost 127.0.0.1:80>
ServerName apache.status
<IfModule mod_fastcgi.c>
AddHandler php5-fcgi .php
Action php5-fcgi /php5-fcgi
Alias /php5-fcgi /dev/shm/someuser-php.fcgi
<LocationMatch “/(ping|status)”>
SetHandler php5-fcgi-virt
Action php5-fcgi-virt /php5-fcgi virtual
</LocationMatch>
</IfModule>
</VirtualHost>
Known issues
500 errors – Magento Bug
Magento has a bug where duplicate headers cause a 500 error.
Log will show:
… aborted: error parsing headers: duplicate header ‘Content-Type’
Solution A: Customer should modify the code as per http://www.magentocommerce.com/boards/viewthread/229253/
Solution B:Or, use the patched mod_fastcgi from Nexcess (above). The patch is specifically to fix this problem