PHP in a hosted environment

A question was asked on our forums about how to use PHP and FastCGI in a hosted environment. The goal was to give each user their own PHP process in order to protect each user from potential security and performance problems associated with sharing a PHP engine with several virtual servers. I started thinking about this and took a whack at a quick solution using Web Server 7.0's environment variables to use the same PHP binary with a separate engine bound to each virtual server (keying off of the Host name of the domain). I also extended this to allow each VS to have its own php.ini file:
Service fn=responder-fastcgi
        app-path="/path/to/php/php_fcgi"
        bind-path="$(lc($urlhost))"
        req-retry=5
        type="\*magnus-internal/fastcgi\*"
        app-env="PHPRC=/path/to/users/$(lc($urlhost))/config"
        app-env="PHP_FCGI_CHILDREN=5"
        app-env="PHP_FCGI_MAX_REQUEST=200"
        min-procs=1
        restart-interval=10
        bucket="php-bucket"
        rlimit_cpu=60
Some quick experimentation showed me that I was getting an approrpriate number of PHP children, and a check of the Web Server tmp directory showed Unix Domain Sockets named after the individual Virtual Servers processing PHP requests. That's so cool! But this is still using a single PHP FastCGI binary for all users (which is to say that while it is executed as many times as needed to give everyone a private heap space, the single binary needs to have all the needed plugins compiled with it leading to potential bloat, etc). Simple answer is to let each user have their own copy of the PHP binary tailer-built to their needs:
Service fn=responder-fastcgi
        app-path="/path/to/users/$(lc($urlhost))/php_fcgi"
        bind-path="$(lc($urlhost))"
        req-retry=5
        type="\*magnus-internal/fastcgi\*"
        app-env="PHPRC=/path/to/users/$(lc($urlhost))/config"
        app-env="PHP_FCGI_CHILDREN=5"
        app-env="PHP_FCGI_MAX_REQUEST=200"
        min-procs=1
        restart-interval=10
        bucket="php-bucket"
        rlimit_cpu=60
Sweet. Yet ... Is it possible to manipulate things to allow me different PHP binaries for each application too? This requires a bit of control over how the URI space is constructed, but it's still possible. For my little exercise I'll pretend that the URI space is structured as
/app/foo.php
where
/app/
is the name of the overall application ("wiki" "gallery" "calendar" "whatever") and it is always the first directory in the URI structure ending in a PHP file.
<If uri~=\^/(\\w+)/\\w+\\.php$>
Service fn=responder-fastcgi
        app-path="/path/to/users/$(lc($urlhost))/$1/php_fcgi"
        bind-path="$(lc($urlhost))_$1"
        req-retry=5
        type="\*magnus-internal/fastcgi\*"
        app-env="PHPRC=/path/to/users/$(lc($urlhost))/config"
        app-env="PHP_FCGI_CHILDREN=5"
        app-env="PHP_FCGI_MAX_REQUEST=200"
        min-procs=1
        restart-interval=10
        bucket="php-bucket"
        rlimit_cpu=60
</If>
Using the If statement to populate the $1 back-reference now allows me to invoke a specially built PHP FastCGI binary and bind it to a uniquely named Unix Domain Socket. Now no skunky locks in one PHP process can interfere with the apps of another PHP app OR another virtual server. Of course you run a risk of eating up a lot of memory with all of these PHP processes hanging around. You'll want to estimate each process' memory use and adjust PHP_FCGI_CHILDREN accordingly. Having so many apps hanging around that you have to start dipping into swap quickly becomes a Bad Thing.
Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

jmccabe

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today