NGINX + PHP-FPM + APC = Awesome

The following guide will walk you through setting up possibly the fastest way to serve PHP known to man. If there is a faster way, I’ve not yet found it climbing through zillions of blog posts out there on the subject. In this article, we’ll be installing nginx http server, PHP with the PHP-FPM patches, as well as APC. The end result? Pure awesome.
Some Background

For the last 2+ years, we’ve been running Apache with mod_php at Massify. And, for the most part, it’s been painless and hassle free. But as traffic increases, we’ve noticed Apache struggling to keep up. It chews up memory like crazy and don’t even get me started about CPU. Add in the fact that we are currently moving the site from co-located metal to hosting on the cloud, we’ve needed to find a faster way to serve up these pages on machines that are a quarter of the spec we had with the metal.

It’s a pretty well know fact by now that Nginx (pronounced engine-x, though I call it n-jinx) in typical scenarios outpaces Apache on all kinds of fronts: i/o, cpu, memory, reqs/sec. Feel free to google around for more comparitive information. I can tell you from my own informal load testing on both setups of Massify that I’ve seen a pronounced difference between the two, specifically in the configuration I’ll be writing about in this post. I won’t be posting numbers, because my testing wasn’t scientific and it’s not really the focus of this article, but I am more than confident that we’ll be getting a level of performance a few steps above Apache. (Please don’t write me telling me I can recompile and reconfigure Apache to approach Nginx’s performance. I’m certain you are correct, but I don’t really care at this point.)
PHP-FPM?

The typical nginx configuration involves using spawn-fcgi from the LightTPD project to get nginx serving up PHP. There are a few issues with spawn-fcgi that are covered on more in-depth posts across the interwebs, so I won’t rehash them here. Suffice to say, it can lead to major headaches, so we want to do what we can to avoid those headaches.

Enter PHP-FPM, which stands for PHP FastCGI Process Manager. It is actually a set of patches for the PHP source that bake in FastCGI process management into your PHP binaries.

Note: Even if you are sticking with Apache, there are a variety of reasons to skip mod_php and go straight for PHP via FastCGI. For starters, with mod_php, each request that Apache handles loads PHP – and all of it’s libraries. That’s an epic shit ton of overhead. With FastCGI, PHP acts more akin to an application server. PHP-FPM, as well as spawn-fcgi, manage this; loading and killing PHP instances as needed. This has a lot of benefits, not the least of which is reduced memory overhead.
Let’s Get Rocking

We’re using a fresh install of Ubuntu 8.10 Intrepid. With a little tooling, this can work equally well on other linux distros. I’ve deployed this on CentOS, with minor changes, just fine.

First things first, we’re going to have to install all the dependencies for building everything:

sudo apt-get install make bison flex gcc patch autoconf subversion locate
sudo apt-get install libxml2-dev libbz2-dev libpcre3-dev libssl-dev zlib1g-dev libmcrypt-dev libmhash-dev libmhash2 libcurl4-openssl-dev libpq-dev libpq5 libsyck0-dev

Now that we have everything we need, now it’s time to …
Compile PHP

We’re going to download the source for PHP 5.2.8, as well as the matching patches for PHP-FPM. We’ll then apply the patches and get compling.

cd /usr/local/src/
sudo wget http://us.php.net/get/php-5.2.8.tar.gz/from/this/mirror
sudo tar zvxf php-5.2.8.tar.gz
sudo wget http://php-fpm.anight.org/downloads/head/php-5.2.8-fpm-0.5.10.diff.gz
sudo gzip -cd php-5.2.8-fpm-0.5.10.diff.gz | sudo patch -d php-5.2.8 -p1
cd php-5.2.8
sudo ./configure –enable-fastcgi –enable-fpm –with-mcrypt –with-zlib –enable-mbstring –disable-pdo –with-pgsql –with-curl –disable-debug –enable-pic –disable-rpath –enable-inline-optimization –with-bz2 –with-xml –with-zlib –enable-sockets –enable-sysvsem –enable-sysvshm –enable-pcntl –enable-mbregex –with-mhash –enable-xslt –enable-memcache –enable-zip –with-pcre-regex
sudo make all install
sudo strip /usr/local/bin/php-cgi

The above should have been completely auto-pilot. If you run into any errors, more than likely you don’t have some of the dependencies installed. Note: Make sure you enable and disable whatever PHP configuration options you need for your particular setup. The above are what we use at Massify, more or less. (We actually apply a couple of other custom patches, but that’s another post for another day).

While we are at it, let’s install some of the modules we’re going to need via PECL:

sudo pecl install memcache
sudo pecl install apc
sudo pecl install syck-beta

Now, when you build the APC extension, make sure you turn off the option to compile for apache. It will prompt you to make this change.

Let’s copy the stock php.ini:

sudo cp /usr/local/src/php-5.2.8/php.ini-recommended /usr/local/lib/php.ini

Finally, let’s setup symbolic links to make things easier to find:

sudo mkdir /etc/php/
sudo ln -s /usr/local/lib/php.ini /etc/php/php.ini
sudo ln -s /usr/local/etc/php-fpm.conf /etc/php/php-fpm.conf

That’s it for compiling PHP. The only thing we have left to do is change some setting in the php-fpm conf. Open up /etc/php/php-fpm.conf in your editor of choice. Do some searching and change the users who own the processes to www-data. The file is too big to post in here, but the values we want to change are on lines 51, 52, 63 and 66. Should look something like:

www-data
www-data
www-data
www-data

Done!
NGINX

This is just as easy as compiling PHP was:

cd ..
sudo wget http://sysoev.ru/nginx/nginx-0.6.35.tar.gz
sudo tar zxvf nginx-0.6.35.tar.gz
sudo rm -f nginx-0.6.35.tar.gz
cd nginx-0.6.35
sudo ./configure –sbin-path=/usr/local/sbin –with-http_ssl_module –without-mail_pop3_module –without-mail_imap_module –without-mail_smtp_module –with-http_stub_status_module
sudo make && sudo make install

Let’s set up some links:

sudo ln -s /usr/local/nginx/conf /etc/nginx

Now the next step is optional, but this is what I’ve been using so far. Let’s open up /etc/nginx/nginx.conf in our editor and go to town. Salt and pepper to taste:

user www-data;
worker_processes 6;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 10 10;

gzip on;
gzip_comp_level 1;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

log_format main ‘$remote_addr – $remote_user [$time_local] ‘
‘”$request” $status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;

access_log /var/log/nginx_access.log main;

error_log /var/log/nginx_error.log debug;

include /usr/local/nginx/sites-enabled/*;
}

We’re also going to have to set up some FastCGI parameters so that PHP doesn’t choke and you’ll avoid random 503 errors from nginx. Open up /etc/nginx/fastcgi_params and add the following:

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

Finally, let’s create a SystemV style init script and store it in /etc/init.d/nginx. This script was lifted from here.

#! /bin/sh

### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/sbin/nginx
NAME=nginx
DESC=nginx

test -x $DAEMON || exit 0

# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
. /etc/default/nginx
fi

set -e

case “$1” in
start)
echo -n “Starting $DESC: ”
start-stop-daemon –start –quiet –pidfile /usr/local/nginx/logs/$NAME.pid \
–exec $DAEMON — $DAEMON_OPTS
echo “$NAME.”
;;
stop)
echo -n “Stopping $DESC: ”
start-stop-daemon –stop –quiet –pidfile /usr/local/nginx/logs/$NAME.pid \
–exec $DAEMON
echo “$NAME.”
;;
restart|force-reload)
echo -n “Restarting $DESC: ”
start-stop-daemon –stop –quiet –pidfile \
/usr/local/nginx/logs/$NAME.pid –exec $DAEMON
sleep 1
start-stop-daemon –start –quiet –pidfile \
/usr/local/nginx/logs/$NAME.pid –exec $DAEMON — $DAEMON_OPTS
echo “$NAME.”
;;
reload)
echo -n “Reloading $DESC configuration: ”
start-stop-daemon –stop –signal HUP –quiet –pidfile /usr/local/nginx/logs/$NAME.pid \
–exec $DAEMON
echo “$NAME.”
;;
*)
N=/etc/init.d/$NAME
echo “Usage: $N {start|stop|restart|force-reload}” >&2
exit 1
;;
esac

exit 0

Don’t forget to set executable permissions on it!
Set Up Your Site

This part you’ll have to tool yourself, but here’s a rough guide.

First we’ll have to create a directory to store our site(s) conf files:

sudo mkdir /usr/local/nginx/sites-enabled
sudo ln -s /usr/local/nginx/sites-enabled /etc/sites

And now let’s add a conf file for our default site at /etc/sites/default.conf. The contents:

server {
listen *:80;

location / {
root /var/www/default/pub;
index index.php;

# if file exists return it right away
if (-f $request_filename) {
break;
}

# otherwise rewrite the fucker
if (!-e $request_filename) {
rewrite ^(.+)$ /index.php$1 last;
break;
}

}

# if the request starts with our frontcontroller, pass it on to fastcgi
location ~ ^/index.php
{
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME /var/www/default/pub$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
include /usr/local/nginx/conf/fastcgi_params;
}
}

The conf file above is for front controller style sites, which include wordpress, cake, etc. It also takes care of serving up static content without going through FastCGI. Note: You will need to change /var/www/default to wherever the files for your site reside.
Start It Up

Now all we have to do to get it rocking:

sudo php-fm start
sudo /etc/init.d/nginx start

You should be able to test your site out and see it working. Any questions, feel free to ask.
Reference

* Nginx
* Nginx vs Apache Death Match
* 10,000 req/s with Nginx
* Replacing Apache With Nginx For Static File Serving
* PHP-FPM
* How to Install PHP-FPM
* PHP-FPM, A Smoother PHP FastCGI Process Manager


[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 5 out of 5)
Loading … Loading …
Blogs

* Coding Horror
* Doug McCune
* GSkinner
* Mutant PHP
* Ted on Flex

News I Read

* DZone
* Hacker News
* Reddit

Nice People

* Christine Allegra
* Hustler of Culture
* I’m In Like With You Blog

Where I Work

* Empty Pockets
* Massify

Recent Posts

* NGINX + PHP-FPM + APC = Awesome
* Filmmakers Finding New Action Online
* Wordle
* Ciarán Walsh’s Blog » TextMate Tip – Using PHP for Commands
* Feedburner WP Plugin Category Fix

Tags
as3 flash Flex flex php as3 Massify nginx PHP php-fpm Poker ubuntu Webisode

* Home
Frontpage
* Archives
Browse freely
* Massify
Where I Work
* RSS
Syndicate
* Top
Return to top

Based on Grid Focus by Derek Punsalan 5thirtyone.com.

g33kadmin

I am a g33k, Linux blogger, developer, student and Tech Writer for Liquidweb.com/kb. My passion for all things tech drives my hunt for all the coolz. I often need a vacation after I get back from vacation....

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.