BenV's notes

Apache 2.2 and PHP configuration fun on Slackware 13

by 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
<Location "/cgi-bin/php-fastcgi">
        Order Deny,Allow
        Deny from All
        Allow from env=REDIRECT_STATUS
        Options ExecCGI
        SetHandler fastcgi-script
</Location>
# 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

Leave a Reply

You must be logged in to post a comment.

Archives

  • 2012 (12)
  • 2011 (26)
  • 2010 (25)
  • 2009 (68)