Apache 2.2 and PHP configuration fun on Slackware 13
by BenV on Dec.28, 2009, under Software
As I love to tinker around with apache/php installations to get them to work as fast as possible while still keeping some security in tact, I found myself messing around with FastCGI today. But why?! Here’s why:
On this machine I previously installed suPhP to get php scripts to run as a normal unix user instead of user apache (so users have the ability to completely lock out their database settings etc for other users on that machine). However, to speed stuff up on a magento webshop I wanted to have a PHP opcode cache running (I tried out APC).
Those opcode caches don’t work very well (read: they only slow things down) if you run php the way it’s supposed to run under suPHP though, because suPHP needs to be run using php’s CGI mode. This means no mod_php, but instead starting a new php instance for every request. Not only is this expensive in terms of startup costs, but it also makes those caches completely useless. (since they can’t share any memory without resorting to state files or some sort of uglyness).
Anyway, suphp is still nice to have for simple sites that don’t get many hits or don’t need to be very fast. Therefore I wanted to have both options available. Here’s how.
I started out with the Slackware packages for php and apache, but soon found both to be inadequate. Php lacked some modules that some site wanted, and apache didn’t even bring suexec along.
However, the slackware layout is good enough so I altered the slackbuild script a bit and built my own apache. You might need to install some packages as dependencies though. For instance I didn’t have ‘expat’ and ‘db44’ installed.
benv@it:~$ mkdir -p /usr/src/sources/ ; cd /usr/src/sources
benv@it:/usr/src/sources$ lftp ftp://ftp.nluug.nl/pub/os/Linux/distr/slackware/slackware-13.0/source/n
cd ok, cwd=/pub/os/Linux/distr/slackware/slackware-13.0/source/n
lftp ftp.nluug.nl:/pub/os/Linux/distr/slackware/slackware-13.0/source/n> mirror httpd
# takes a while...
benv@it:/usr/src/sources$ cd httpd
benv@it:/usr/src/sources/httpd$ wget http://www.apache-mirror.com/httpd/httpd-2.2.14.tar.bz2
benv@it:/usr/src/sources/httpd$ vim httpd.SlackBuild
Now edit the slackbuild file so it compiles with your compile flags. My changes were:
1. Changed VERSION to 2.2.14 (was 2.2.13)
2. Added these to the configure flags (to enable suexec) around line 103:
--enable-suexec \
--with-suexec-bin=/usr/sbin/suexec \
--with-suexec-docroot=/www \
--with-suexec-caller=apache \
--with-suexec-uidmin=1000 \
--with-suexec-gidmin=100 \
--without-suexec-userdir \
Compile and install it. Note that the suexec part requires root permissions, so I did this as root.
root@it:/usr/src/sources/httpd# bash httpd.SlackBuild
# takes a while
root@it:/usr/src/sources/httpd# mv /tmp/httpd-2.2.14-i386-1.txz /usr/src/packages
root@it:/usr/src/sources/httpd# installpkg /usr/src/packages/httpd-2.2.14-i386-1.txz
PHP and suPHP
benv@it:/usr/src$ wget http://nl3.php.net/get/php-5.2.12.tar.bz2/from/nl.php.net/mirror
# Leech leech... and tomorrow there will be a new version since you're compiling it today. That's my luck ;)
benv@it:/usr/src$ tar jxf php-5.2.12.tar.bz2
benv@it:/usr/src$ cd php-5.2.12
# Pick your own configure string, but note that we don't build mod_php, so no --with-apxs!
benv@it:/usr/src/php-5.2.12$ ./configure --prefix=/usr --enable-fastcgi --enable-force-cgi-redirect --with-config-file-path=/etc/php --with-openssl --with-zlib --enable-bcmath --with-bz2 --enable-calendar --with-curl --enable-dba=shared --enable-exif --enable-ftp --with-gd --with-ttf --with-t1lib --enable-gd-native-ttf --enable-gd-jis-conv --with-gettext --with-gmp --with-imap=/usr/local/lib/c-client --with-imap-ssl --enable-mbstring --with-mcrypt --with-mhash --with-mysql --with-mysqli --with-ncurses --with-pspell --with-mm --enable-shmop --enable-soap --enable-sockets --enable-sqlite-utf8 --enable-sysvmsg --enable-sysvsem --enable-sysvshm --with-tidy --with-xsl --enable-zip --with-tsrm-pthreads --enable-shared --enable-static --without-ldap --without-ldap-sasl --with-jpeg-dir=/usr --with-freetype-dir=/usr --with-xpm-dir=/usr --with-pdo-mysql --with-png-dir=/usr
# barf barf
benv@it:/usr/src/php-5.2.12$ make
# see you tomorrow
benv@it:/usr/src/php-5.2.12$ sudo make install
Note that since php is a pleuris product it will ignore the DESTDIR variable that usually works for creating a package. If you want a nice package for php, consider using the slackware build script instead. (I was too lazy for it this time). Next suPHP:
benv@it:/usr/src$ wget http://www.suphp.org/download/suphp-0.7.1.tar.gz
benv@it:/usr/src$ tar zxf suphp-0.7.1.tar.gz
benv@it:/usr/src$ cd suphp-0.7.1
benv@it:/usr/src/suphp-0.7.1$ ./configure --prefix=/usr --enable-checkpath --with-min-uid=1000 --with-apache-user=apache --with-logfile=/www/log/suphp_log --with-apr=/usr/bin/apr-1-config
# lala
benv@it:/usr/src/suphp-0.7.1$ make
benv@it:/usr/src/suphp-0.7.1$ mkdir pkg
benv@it:/usr/src/suphp-0.7.1$ make DESTDIR=`pwd`/pkg install
benv@it:/usr/src/suphp-0.7.1$ cd pkg && makepkg /usr/src/packages/suphp-0.7.1-i386-1.tgz
benv@it:/usr/src/suphp-0.7.1$ sudo installpkg /usr/src/packages/suphp-0.7.1-i386-1.tgz
That went smooth. PHP could learn something from this. Now we still need to configure suphp. To do this we create 2 files and alter httpd.conf a little.
First suphp.conf:
root@it:/usr/src/suphp-0.7.1$ cp /usr/src/suphp-0.7.1/doc/suphp.conf-example /etc/suphp.conf
root@it:/usr/src/suphp-0.7.1$ vim /etc/suphp.conf
Personally I left most to default, but I did change the log path and the docroot.
Next we need to enable it in apache, but to keep module stuff in their own section we put this in mod_suphp.conf which we include in the httpd.conf.
root@it:/etc/httpd$ cat > mod_suphp.conf
LoadModule suphp_module /usr/lib/httpd/modules/mod_suphp.so
# AddModule mod_suphp.c
AddType application/x-httpd-php .php
suPHP_AddHandler application/x-httpd-php
# hit ctrl-d here
root@it:/etc/httpd$ echo 'Include /etc/httpd/mod_suphp.conf' >> httpd.conf
That should be enough. Now for sites that you want to enable suphp, simply add a few lines to their vhost config like this:
suPHP_Engine on
suPHP_ConfigPath /etc/php
suPHP_UserGroup jemoeder users
Now you should have a working suPHP installation (provided that you restarted apache etc).
If something fails, check the logs, especially suphp.log and of course the apache error_log.
FastCGI
To get this running I loosely followed this blog post. It pretty much worked like Brandon explained, after I recompiled my apache for suexec support that is. Thus a summary:
Install FastCGI
benv@it:/usr/src$ wget http://www.fastcgi.com/dist/mod_fastcgi-current.tar.gz
benv@it:/usr/src$ tar xvf mod_fastcgi-current.tar.gz
benv@it:/usr/src$ cd mod_fastcgi-2.4.6
benv@it:/usr/src/mod_fastcgi-2.4.6$ apxs -o mod_fastcgi.so -c *.c
# This as root
root@it:/usr/src/mod_fastcgi-2.4.6$ cp .libs/mod_fastcgi.so /usr/lib/httpd/modules
# Now to enable it in apache
root@it:/$ cat > /etc/httpd/mod_fastcgi.conf
LoadModule fastcgi_module /usr/lib/httpd/modules/mod_fastcgi.so
FastCgiConfig -idle-timeout 20 -maxClassProcesses 1
FastCgiWrapper /usr/sbin/suexec
Order Deny,Allow
Deny from All
Allow from env=REDIRECT_STATUS
Options ExecCGI
SetHandler fastcgi-script
# ctrl-d here
root@it:/$ echo 'Include /etc/httpd/mod_fastcgi.conf' >> /etc/httpd/httpd.conf
Now for the magic part, enabling it in a vhost.
First we need a php wrapper script:
root@it:/$ mkdir -p /www/fastcgi-wrappers/somevhost.com
root@it:/$ cat > /www/fastcgi-wrappers/somevhost.com/php-fastcgi
#!/bin/sh
export PHP_FCGI_CHILDREN=5
export PHP_FCGI_MAX_REQUESTS=500
umask 0022
exec /usr/bin/php -d apc.shm_size=64
# ctrl-d here
root@it:/$ chown -R vhostuser:users /www/fastcgi-wrappers/somevhost.com
Note the -d apc.shm_size=64 option. This specific option is to set the cache size for APC, but this way you can give php custom settings per vhost. Neat huh? If you want to give this vhost its own php.ini you can add this to the script:
export PHPRC=/etc/php/vhosts/somevhost
PHP will check for a php.ini in that dir. Make sure to check the permissions on these dirs though, especially on the wrapper script. Your users might do stuff you don’t want if you let them alter these things.
Finally to enable FastCGI per vhost, add something to the vhost configuration like this (make sure to disable suphp on that vhost!):
SuexecUserGroup jemoeder users
Alias /cgi-bin/ /www/fastcgi-wrappers/somevhost.com/
AddHandler php5-fcgi .php
Action php5-fcgi /cgi-bin/php-fastcgi
Restart apache and you should see stuff like this in the process list:
root@it:/$ ps auwx | grep jemoeder
jemoeder 5789 0.1 4.3 131388 44940 ? S 16:46 0:07 /usr/bin/php -d apc.shm_size=64
jemoeder 5790 0.1 3.0 130804 31888 ? S 16:46 0:06 /usr/bin/php -d apc.shm_size=64
jemoeder 5791 0.1 4.3 131644 44948 ? S 16:46 0:06 /usr/bin/php -d apc.shm_size=64
jemoeder 5792 0.1 3.3 131620 34160 ? S 16:46 0:06 /usr/bin/php -d apc.shm_size=64
jemoeder 5793 0.0 2.5 126872 26672 ? S 16:46 0:05 /usr/bin/php -d apc.shm_size=64
APC
Now for some speedup. Let’s see apachebench on that magento vhost without APC and FastCGI:
benv@mybrik:~$ ab -n 50 -c 3 -k http://www.somevhost.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking www.somevhost.com (be patient).....done
Server Software: Apache/2.2.14
Server Hostname: www.somevhost.com
Server Port: 80
Document Path: /
Document Length: 21590 bytes
Concurrency Level: 3
Time taken for tests: 43.056 seconds
Complete requests: 50
Failed requests: 0
Write errors: 0
Keep-Alive requests: 0
Total transferred: 1104350 bytes
HTML transferred: 1079500 bytes
Requests per second: 1.16 [#/sec] (mean)
Time per request: 2583.342 [ms] (mean)
Time per request: 861.114 [ms] (mean, across all concurrent requests)
Transfer rate: 25.05 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 10 12 3.5 11 29
Processing: 2077 2560 221.6 2489 3083
Waiting: 1741 2015 116.6 2033 2266
Total: 2093 2572 222.5 2499 3094
Percentage of the requests served within a certain time (ms)
50% 2499
66% 2577
75% 2648
80% 2777
90% 3002
95% 3064
98% 3094
99% 3094
100% 3094 (longest request)
As you can see it takes more than a second to get the page to load. Well, 0.8s mean across all, but 2.5s per request.
Let’s install APC:
root@it:/usr/src$ wget http://pecl.php.net/get/APC-3.0.19.tgz
root@it:/usr/src$ tar zvf APC-3.0.19.tgz
root@it:/usr/src$ cd APC-3.0.19
root@it:/usr/src/APC-3.0.19$ ./configure --prefix=/usr --enable-apc --enable-apc-filehits --enable-apc-sem --enable-apc-mmap
root@it:/usr/src/APC-3.0.19$ make && make install # Or create a package if you're up to it.
# Now to enable it in php, assuming php.ini is /etc/php/php.ini
root@it:/usr/src/APC-3.0.19$ echo >> /etc/php/php.ini
extension="apc.so"
[APC]
apc.enabled = 1
apc.enable_cli = 1
apc.shm_segments = 1
apc.max_file_size = 10M
apc.stat = 1
# Ctrl-D here
Restart apache and voila. Time for a new benchmark.
benv@mybrik:~$ ab -n 50 -c 3 -k http://www.somevhost.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking www.somevhost.com (be patient).....done
Server Software: Apache/2.2.14
Server Hostname: www.somevhost.com
Server Port: 80
Document Path: /
Document Length: 23337 bytes
Concurrency Level: 3
Time taken for tests: 10.963 seconds
Complete requests: 50
Failed requests: 0
Write errors: 0
Keep-Alive requests: 0
Total transferred: 1192600 bytes
HTML transferred: 1166850 bytes
Requests per second: 4.56 [#/sec] (mean)
Time per request: 657.787 [ms] (mean)
Time per request: 219.262 [ms] (mean, across all concurrent requests)
Transfer rate: 106.23 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 10 22 15.9 16 76
Processing: 477 630 71.8 618 844
Waiting: 409 549 54.4 551 708
Total: 493 652 74.8 640 858
Percentage of the requests served within a certain time (ms)
50% 640
66% 654
75% 679
80% 692
90% 764
95% 811
98% 858
99% 858
100% 858 (longest request)
W000t…. that’s a lot quicker, almost 4 times as fast. Nice 🙂
1 Trackback or Pingback for this entry
January 12th, 2010 on 19:23
[…] 2010, under Software A few weeks ago I implemented a FastCGI setup for a magento website. (details Here). That site also runs google analytics. Here’s what analytics had to show about the speed of […]