ELM 2007/11/29 17:27
Nginx, Fastcgi, PHP, rewrite config for Drupal
Afternoon.
So I've been getting stuck into making Drupal 4.7 (and 5.0) work with Nginx, which is a bit like Lighttpd except without the firehose-esque memory leaks you get with Lighty and actual web traffic busier than a trickle.
This has worked for me for the last several days on (! NSFW !) Cliterati.co.uk, which roars through about 30 HTTP requests per second.
Credit: This page was completely invaluable, and everything Drupal-ish here is merely minor edits to that earlier work.
Why would you want to do this?
Because if you're running Apache/Apache2 with mod_php on a dedicated server with 1Gb of memory, and you have a lot of traffic, and more than about 50 of your visitors are logged in and posting to forum.module most of the time, then your dedicated server can't run Drupal. This is nuts. While people are raging against the non-existent caching for uid>0 in Drupal, you may want to cut your static memory requirement by about 85% at a stroke by showing Apache the door.
Nginx (and lighttpd) do more than just this, in performance terms, but even if they didn't I'd still need to run one of them to keep such a server afloat.
Nginx
Firstly you have to install nginx, which is not going to be covered here. I went for compile-from-source because the versions in Debian repositories are ancient. Next, here's the configuration details I've found to work with Drupal and URL aliasing. I'm posting an example of an entire http{} section from the config file. It passes everything PHP-related to one or more PHP fastcgi processes listening on port 8888, which you'll be setting up after this.
In nginx.conf:
http {
include conf/mime.types;
default_type application/octet-stream;
server_names_hash_bucket_size 128;
#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 logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 20;
tcp_nodelay on;
#gzip on;
server {
listen 192.168.0.1:80; # Replace this IP and port with the right ones for your requirements
server_name example.com [url]www.example.com[/url]; # Multiple hostnames seperated by spaces. Replace these as well.
#charset koi8-r;
#access_log logs/host.access.log main;
location = / {
root /path/to/drupal; # Again, replace this.
index index.php;
}
location / {
root /path/to/drupal;
index index.php index.html;
if (!-f $request_filename) {
rewrite ^(.*)$ /index.php?q=$1 last;
break;
}
if (!-d $request_filename) {
rewrite ^(.*)$ /index.php?q=$1 last;
break;
}
}
error_page 404 /index.php;
# serve static files directly
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
access_log off;
expires 30d;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location ~ .php$ {
fastcgi_pass 127.0.0.1:8888; # By all means use a different server for the fcgi processes if you need to
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /path/to/drupal$fastcgi_script_name; # !! <--- Another path reference for you.
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
}
PHP and Fastcgi
I'm not mentioning PHP 4 or 5 because this part is exactly the same for each. I'm referring to 'php5-cgi' because that's the name of the right binary for the right PHP version on my Debian-based server. Your mileage may vary.
This shell script will launch a few fastcgi PHP processes bound to port 8888 for Nginx to talk to. I launch it as root - it starts the processes as the Debian apache user and exits.
#!/bin/bash
## ABSOLUTE path to the PHP binary
PHPFCGI="/usr/bin/php5-cgi"
## tcp-port to bind on
FCGIPORT="8888"
## IP to bind on
FCGIADDR="127.0.0.1"
## number of PHP children to spawn
PHP_FCGI_CHILDREN=5
## number of request before php-process will be restarted
PHP_FCGI_MAX_REQUESTS=1000
# allowed environment variables sperated by spaces
ALLOWED_ENV="ORACLE_HOME PATH USER"
## if this script is run as root switch to the following user
USERID=www-data
################## no config below this line
if test x$PHP_FCGI_CHILDREN = x; then
PHP_FCGI_CHILDREN=5
fi
ALLOWED_ENV="$ALLOWED_ENV PHP_FCGI_CHILDREN"
ALLOWED_ENV="$ALLOWED_ENV PHP_FCGI_MAX_REQUESTS"
ALLOWED_ENV="$ALLOWED_ENV FCGI_WEB_SERVER_ADDRS"
if test x$UID = x0; then
EX="/bin/su -m -c \"$PHPFCGI -q -b $FCGIADDR:$FCGIPORT\" $USERID"
else
EX="$PHPFCGI -b $FCGIADDR:$FCGIPORT"
fi
echo $EX
# copy the allowed environment variables
E=
for i in $ALLOWED_ENV; do
E="$E $i=${!i}"
done
# clean environment and set up a new one
nohup env - $E sh -c "$EX" &> /dev/null &
Initial results
I've moved two phpAdsNew ad servers, a fairly-busy Wordpress blog, the above-mentioned Drupal site and my own Drupal site from Apache2 into Nginx in the last week. Processor use has increased on the server, but critically the ongoing web+database memory use has come down by over 300MB. This means that the box hasn't gone into swap for a week (normally it was paging out every day), server response times are up and (because I'm also using APC to store Drupal's source files in memory) page download speeds are also up considerably.
Basically, it's saved a server.
I've tested it with Drupal 4.7 and Drupal 5.0, and with PHP4.3.3 and PHP5.2.0. All those configurations work.
Caveats
Drupal can serve static files either directly or via Drupal itself - it's a configuration option (in Drupal 4.7 it's in admin/settings - I don't yet have 5.0 seared into memory). This configuration requires that you serve those files directly. If Drupal's configured the other way your site will look incredibly odd, and image.module in particular breaks horribly. Me, I don't care. If this is an issue for you, it might be time to get into that in the comments.
Enjoy... and if you've got suggestions to improve this, I'd love to hear them.
From: [url]http://drupal.org/node/110224[/url]