<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Brandon&#039;s Blog &#187; apache</title>
	<atom:link href="http://www.brandonturner.net/blog/tag/apache/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.brandonturner.net/blog</link>
	<description>Random thoughts on web applications, software development and Linux</description>
	<lastBuildDate>Thu, 08 Apr 2010 01:47:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>FastCGI with a PHP opcode cache benchmarks</title>
		<link>http://www.brandonturner.net/blog/2009/07/fastcgi_php_opcode_cache_benchmarks/</link>
		<comments>http://www.brandonturner.net/blog/2009/07/fastcgi_php_opcode_cache_benchmarks/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 05:32:17 +0000</pubDate>
		<dc:creator>Brandon</dc:creator>
				<category><![CDATA[Server Admin]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[apc]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[suEXEC]]></category>

		<guid isPermaLink="false">http://www.brandonturner.net/blog/?p=377</guid>
		<description><![CDATA[mod_php and FastCGI performance is very comparable when an opcode cache is utilized.  These benchmarks cover performance and memory usage of mod_php vs FastCGI.]]></description>
			<content:encoded><![CDATA[<p>In my previous post, I described how to implement <a href="/blog/2009/07/fastcgi_with_php_opcode_cache/">FastCGI with a PHP opcode cache</a> on an Apache webserver.  My primary motivation for moving to FastCGI was to take advantage of the extra security provided by FastCGI with suEXEC over mod_php.  In this post, I&#8217;ll compare the two environments and provide a few benchmark results.</p>
<h4>Benchmark Setup</h4>
<p>To compare mod_php to FastCGI with a PHP opcode cache, I used the <a href="http://httpd.apache.org/docs/2.2/programs/ab.html">ab tool</a> on my desktop running PHP 5.3.0, mod_fastcgi 2.4.6 and Apache 2.2.11 on Gentoo linux.  Apache had a limited number of modules enabled (actions alias authz_host dav deflate dir expires filter headers log_config mime rewrite setenvif status vhost_alias).  The hardware consisted of a Intel Core2 Quad Q6600 (2 x 4MB L2 cache) with 2GB of RAM.</p>
<p>I chose to benchmark against the main page of a stock WordPress 2.8.2 install with no plugins enabled.  I chose WordPress because it&#8217;s extremely easy to set up and many people are familiar with it.  I&#8217;ve read that WordPress is very liberal with queries to the database (especially when plugins are involved).  Without database queries, I would expect to see a bigger difference between results with an opcode cache and results without one.  That being said, most PHP applications today make database queries as part of a typical request and thus I think these results are realistic of what you might experience on your server.</p>
<p>I executed the following command:</p>
<div class="prompt">
$ <span class="cmd">ab -n 1000 -c 30 http://localhost/</span>
</div>
<p>in four different environments:</p>
<ul>
<li>mod_php with a 30MB APC opcode cache (Apache MPM prefork)</li>
<li>mod_php without an opcode cache (Apache MPM prefork)</li>
<li>mod_fastcgi, suEXEC with a 30MB APC opcode cache (Apache MPM worker)</li>
<li>mod_fastcgi, suEXEC without an opcode cache (Apache MPM worker)</li>
</ul>
<p>Between each benchmark, I reconfigured and restarted the server.  I then loaded the main page in a web browser to prime the APC cache.</p>
<h4>Benchmark Results</h4>
<h5>Requests</h5>
<div id="attachment_385" class="wp-caption aligncenter" style="width: 590px"><a href="http://www.brandonturner.net/blog/wp-content/uploads/2009/07/requests_per_second.png"><img src="http://www.brandonturner.net/blog/wp-content/uploads/2009/07/requests_per_second.png" alt="Graph displaying requests per second of each benchmark scenario" title="Requests per second" width="580" height="300" class="size-full wp-image-385" /></a><p class="wp-caption-text">Requests per second</p></div>
<p>The graph above shows that mod_php and FastCGI behaved very similar in terms of requests per second.  Using an opcode cache increased the request rate by almost 3 times in both scenarios.  As I mentioned earlier, I&#8217;d expect to see an even bigger increase with fewer database queries.</p>
<p>I saw similar results when increasing the number of requests to 10,000, or changing the concurrency level.  I never saw any failed requests.  I was happy to see there wasn&#8217;t a significant performance difference moving from mod_php to FastCGI.</p>
<h5>Memory Usage</h5>
<div id="attachment_384" class="wp-caption aligncenter" style="width: 590px"><a href="http://www.brandonturner.net/blog/wp-content/uploads/2009/07/ram.png"><img src="http://www.brandonturner.net/blog/wp-content/uploads/2009/07/ram.png" alt="Graph displaying memory usage of each benchmark scenario" title="Memory usage" width="580" height="400" class="size-full wp-image-384" /></a><p class="wp-caption-text">Memory usage</p></div>
<p>The graph above shows the approximate memory usage for each of the 4 benchmarking scenarios.  This data was gathered by running the <a href="http://www.pixelbeat.org/scripts/ps_mem.py">ps_mem.py</a> script immediately after the ab tool completed.  This script outputs the private and shared memory usage for each program running on the system.  Because I ran my benchmarks in isolation, with nothing else accessing the host, these results should be pretty accurate.</p>
<p>The results surprised me.  From the graph you can see that disabling the opcode cache resulted in <em>more</em> memory usage.  I think this is misleading.  Typically, Linux keeps memory resident until it is needed by another process.  I believe the increased memory usage for the non-opcode cache scenarios is actually discarded memory that the Linux kernel hasn&#8217;t reclaimed.  Since each process is having to recompile the PHP script on each request, you see a lot more built up memory reported as private but most likely free to be reused.  I suspect running this on a low memory server would yield slightly different results.</p>
<p>You can clearly see the difference in memory usage for the Apache processes (green in the graph) between FastCGI and mod_php.  The FastCGI Apache processes are much leaner.  Since Apache doesn&#8217;t need the PHP bloat, it can serve static content much more effectively and quickly.</p>
<p>The scenarios with an opcode cache each used a similar amount of RAM.  There is a catch to these results however: I only benchmarked a single user.  In a shared environment, each user gets their own PHP instance and opcode cache.  With mod_php, all users share the same.  Thus I would expect every user to roughly double the RAM used in a FastCGI scenario, while mod_php memory usage would stay relatively constant.  This increased memory usage is the main trade-off when going with FastCGI.</p>
<h4>Raw Results</h4>
<p>For those who want even more detail, below are the raw results from my benchmark tests:</p>
<p>mod_php without an opcode cache:</p>
<pre class="brush: plain;">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        5474 bytes

Concurrency Level:      30
Time taken for tests:   28.507 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      5717000 bytes
HTML transferred:       5474000 bytes
Requests per second:    35.08 [#/sec] (mean)
Time per request:       855.200 [ms] (mean)
Time per request:       28.507 [ms] (mean, across all concurrent requests)
Transfer rate:          195.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:   138  849 214.5    842    1608
Waiting:      138  848 214.3    842    1601
Total:        139  849 214.4    843    1608

Percentage of the requests served within a certain time (ms)
  50%    843
  66%    911
  75%    975
  80%   1004
  90%   1112
  95%   1230
  98%   1355
  99%   1426
 100%   1608 (longest request)
</pre>
<p>mod_php with opcode cache:</p>
<pre class="brush: plain;">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        5474 bytes

Concurrency Level:      30
Time taken for tests:   10.784 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      5717000 bytes
HTML transferred:       5474000 bytes
Requests per second:    92.73 [#/sec] (mean)
Time per request:       323.530 [ms] (mean)
Time per request:       10.784 [ms] (mean, across all concurrent requests)
Transfer rate:          517.70 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:    66  319  62.9    316     580
Waiting:       66  318  62.9    316     580
Total:         67  319  62.8    316     580

Percentage of the requests served within a certain time (ms)
  50%    316
  66%    339
  75%    354
  80%    364
  90%    395
  95%    424
  98%    464
  99%    482
 100%    580 (longest request)
</pre>
<p>FastCGI without an opcode cache:</p>
<pre class="brush: plain;">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        5474 bytes

Concurrency Level:      30
Time taken for tests:   28.170 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      5695000 bytes
HTML transferred:       5474000 bytes
Requests per second:    35.50 [#/sec] (mean)
Time per request:       845.104 [ms] (mean)
Time per request:       28.170 [ms] (mean, across all concurrent requests)
Transfer rate:          197.43 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:   105  832  82.9    832    1004
Waiting:      105  832  82.9    832    1004
Total:        107  832  82.8    832    1004

Percentage of the requests served within a certain time (ms)
  50%    832
  66%    856
  75%    873
  80%    882
  90%    908
  95%    927
  98%    954
  99%    971
 100%   1004 (longest request)
</pre>
<p>FastCGI with opcode cache:</p>
<pre class="brush: plain;">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        5474 bytes

Concurrency Level:      30
Time taken for tests:   10.917 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      5695000 bytes
HTML transferred:       5474000 bytes
Requests per second:    91.60 [#/sec] (mean)
Time per request:       327.509 [ms] (mean)
Time per request:       10.917 [ms] (mean, across all concurrent requests)
Transfer rate:          509.44 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:    46  323  36.6    325     426
Waiting:       46  323  36.6    325     426
Total:         47  323  36.5    325     426

Percentage of the requests served within a certain time (ms)
  50%    325
  66%    335
  75%    342
  80%    346
  90%    359
  95%    368
  98%    383
  99%    389
 100%    426 (longest request)
</pre>
<p>Memory Usage (in MB):</p>
<table>
<tr>
<th rowspan="2">Benchmark</th>
<th colspan="3" style="text-align: center;">Apache</th>
<th colspan="3" style="text-align: center;">PHP</th>
<th rowspan="2">Total</th>
</tr>
<tr>
<th>private</th>
<th>shared</th>
<th>total</th>
<th>private</th>
<th>shared</th>
<th>total</th>
</tr>
<tr>
<th>mod_php, no cache</th>
<td>202.3</td>
<td>7.7</td>
<td>210</td>
<td></td>
<td></td>
<td></td>
<td><b>210</b></td>
</tr>
<tr>
<th>mod_php, with cache</th>
<td>45.3</td>
<td>15.5</td>
<td>60.8</td>
<td></td>
<td></td>
<td></td>
<td><b>60.8</b></td>
</tr>
<tr>
<th>FastCGI, no cache</th>
<td>9.2</td>
<td>3.3</td>
<td>12.5</td>
<td>68.3</td>
<td>5.0</td>
<td>73.3</td>
<td><b>85.8</b></td>
</tr>
<tr>
<th>FastCGI, with cache</th>
<td>9.2</td>
<td>3.6</td>
<td>12.8</td>
<td>23.4</td>
<td>13.8</td>
<td>37.2</td>
<td><b>50</b></td>
</tr>
</table>
<h4>Conclusion</h4>
<p>I run my blog on a <a href="http://www.linode.com/">Linode VPS</a> with a very limited amount of RAM.  I also host other applications and a few friends&#8217; sites on this same server.  Thus, I am very concerned about memory usage.  Still, the security advantages provided by FastCGI far outweigh the increased RAM usage.</p>
<p>I hope you have seen that the performance of FastCGI with an opcode cache is just as good as with mod_php.  It&#8217;s also evident how important an opcode cache is to running PHP scripts.</p>
<p>If you have any thoughts about these benchmarks, or the performance of FastCGI vs mod_php, feel free to leave a comment or question below!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.brandonturner.net/blog/2009/07/fastcgi_php_opcode_cache_benchmarks/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>FastCGI with a PHP APC Opcode Cache</title>
		<link>http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/</link>
		<comments>http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/#comments</comments>
		<pubDate>Tue, 07 Jul 2009 05:07:48 +0000</pubDate>
		<dc:creator>Brandon</dc:creator>
				<category><![CDATA[Server Admin]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[apc]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[suEXEC]]></category>

		<guid isPermaLink="false">http://www.brandonturner.net/blog/?p=320</guid>
		<description><![CDATA[Hosting PHP web applications in a shared environment usually involves a choice between two exclusive options: host a fast application by using a persistent opcode cache, or host an application that your shared neighbors can&#8217;t snoop around or destroy. In this post I discuss a way to get the best of both worlds, by combining [...]]]></description>
			<content:encoded><![CDATA[<p>Hosting PHP web applications in a shared environment usually involves a choice between two exclusive options: host a fast application by using a persistent opcode cache, or host an application that your shared neighbors can&#8217;t snoop around or destroy.  In this post I discuss a way to get the best of both worlds, by combining FastCGI with a single opcode cache per user.</p>
<p>This is a long post, ready to jump right in?  <a href="#implementation">Skip the history!</a></p>
<p><a name="mod_php"></a></p>
<h4>The evolution of mod_php to FastCGI</h4>
<p>In the early days of all-you-can eat shared hosting, administrators served PHP via mod_php.  mod_php loads the PHP interpreter into every web server process during server startup, thus alleviating the expense of starting an interpreter each time a script executes.  This allowed executing PHP scripts relatively fast.</p>
<p>mod_php came with a few drawbacks:</p>
<ul>
<li>Every server process, even those serving static files such as images and CSS scripts, contained the PHP interpreter.  This caused a lot of bloat in the web server&#8217;s memory footprint.  It also eliminated the ability to use mutil-threaded web servers as many PHP extensions are not thread safe.</li>
<li>Every PHP script ran as the same user as the web server.  While web servers typically run as a non-privileged user such as <em>nobody</em>, multiple mutually untrusting shared accounts could easily access, disrupt or destroy each other by executing a PHP script.</li>
</ul>
<p>FastCGI loads the PHP interpreter into a separate process.  This process is still persistent across connections, but, using a mechanism such as suEXEC, can run as a different user.  Static files can be served by a lightweight multi-threaded web server process while PHP scripts are served by a single-threaded FastCGI process.  What&#8217;s more, if PHP crashes, it doesn&#8217;t bring down the entire web server.</p>
<p>In the shared hosting context, each user&#8217;s PHP scripts are executed with the user&#8217;s credentials.  This leads to a more secure environment for both the host and the shared user.<br />
<a name="opcode"></a></p>
<h4>The opcode cache</h4>
<p>One of the easiest and most effective things you can do to speed up your PHP scripts is to enable an opcode cache such as <a href="http://www.php.net/apc/">APC</a>, <a href="http://xcache.lighttpd.net/">XCache</a> or <a href="http://eaccelerator.net/">eAccelerator</a>.  An opcode cache caches the compiled state of PHP scripts in shared memory.  Thus each time a PHP script is run, the server doesn&#8217;t have to waste time compiling the source code.  Opcode caches can speed up execution of scripts by up to 5 times and decrease server load.</p>
<p>In my opinion running PHP on a webserver without an opcode cache is like restarting your car&#8217;s engine at every stop sign.  You can still get where you&#8217;re going but it&#8217;s going to take longer and put a lot more wear and tear on your engine.  An opcode cache is so important that APC is going to be included in the core of PHP 6.</p>
<p>An opcode cache requires that the PHP interpreter process persist between connections.  Both mod_php and FastCGI satisfy this requirement.  An opcode cache requires RAM, a precious commodity on a shared hosting server.  By default, each cache allocates 30MB of shared memory.  This can be easily configured up or down depending on the scripts you are running.<br />
<a name="combine"></a></p>
<h4>Combining FastCGI with an opcode cache</h4>
<p>So if we agree that FastCGI and opcode caches are good (a must IMHO), why do most shared hosting providers only enable one?  The answer is two-fold:</p>
<ol>
<li>RAM.  Each opcode cache is typically 30MB.  Each PHP process gets its own opcode cache.  Each user must run its own PHP process for security.  Thus each user requires <em>at least</em> 30MB of RAM on top of the RAM required for the PHP interpreter (a lot).  All you can eat shared hosting companies typically oversell their servers.  Overselling usually works when it comes to bandwidth, I/O and CPU time, however overselling RAM is harder.  Remember the PHP processes stay in memory between connections.  So a small site only getting a 100 hits a day still hogs the same amount of RAM as a busy site.  This breaks the overselling model.</li>
<li>FastCGI.  In a typical configuration, FastCGI spawns many separate PHP processes per user.  Each PHP process needs its own opcode cache.  Instead of maintaining one opcode cache (per user), the server maintains multiple caches.  This reduces the effectiveness of the cache and increases the strain on server resources.</li>
</ol>
<p>Solving problem #1 is hard.  Some have suggested a single cache that can be shared across multiple processes and users and still provide assurance that different users cannot mess with each other.  This blog post is not about #1.  There are many reasons to use <em>unlimited</em> shared hosting providers.  Opcode caches are not one.</p>
<p>This blog post is about how to solve problem #2.  The goal is to have a reasonable system that utilizes suEXEC, FastCGI and the APC opcode cache.  Each user should have one and only one opcode cache.  The administrator should be able to adjust the size of the cache for each individual user based on their needs (and monthly fee).  Finally, the solution should decrease script load time and increase server performance while maintaining security and privacy between accounts.</p>
<p><a name="mod_fastcgi"></a></p>
<h4>mod_fastcgi vs mod_fcgid</h4>
<p>I run Apache on my server.  Many people suggest running a more lightweight server such as <a href="http://www.lighttpd.net/">lighttpd</a>.  One day I may switch, but for now I&#8217;ve tuned my Apache server to be as fast as I need.</p>
<p>There are two modules to implement FastCGI on Apache &#8211; <a href="http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html">mod_fastcgi</a> and the newer <a href="http://fastcgi.coremail.cn/">mod_fcgid</a>.  Both are binary compatible with each other and do basically the same thing.  mod_fcgid sports better control over spawning processes and error detection.  mod_fastcgi has been around longer.  Both support suEXEC, and both separate PHP from Apache, thus allowing Apache to run threaded workers if desired.</p>
<p>As I mentioned in the <a href="#combine">combining FastCGI with an opcode cache</a> section, the typical behavior of FastCGI is to spawn multiple PHP interpreters.  The FastCGI process monitors each child process, kicking out processes with errors, restarting failed processes and sending incoming requests to the least busy child.  This is usually the preferred behavior, and mod_fcgid implements it particularly well.</p>
<p>Opcode caches throw a wrench in this however, because of their inability to share the cache across FastCGI processes.  Hopefully one day this will be <a href="http://pecl.php.net/bugs/bug.php?id=11988">remedied</a>.  Luckily, in the meantime, PHP is capable of playing &#8220;process manager&#8221; and a single PHP process can spawn several children to handle requests.  This way the parent PHP process can instantiate the opcode cache and its children can share it.  You&#8217;ll see this later when we set the PHP_FCGI_CHILDREN environment variable.</p>
<p>Both mod_fcgid and mod_fastcgi can be told to limit the number of PHP processes to 1 per user.  The PHP process can then be told how many children to spawn.  Unfortunately mod_fcgid will only send <strong>one request per child process</strong>.  The fact that PHP spawns its own children is ignored by mod_fcgid.  If we use mod_fcgid with our setup, we can only handle one concurrent PHP request.  This is not good.  A long running request could easily block multiple smaller requests.</p>
<p>mod_fastcgi <em>will</em> send multiple simultaneous requests to a single PHP process if the PHP process has children that can handle it.  This is the reason we <strong>must use mod_fastcgi</strong> to achieve our goal of one cache per user.</p>
<p><a name="implementation"></a></p>
<h4>Implementation</h4>
<p>This section describes the steps I took to enable suEXEC FastCGI with a single APC opcode cache per user on Apache 2.2.  These instructions may vary by Linux distribution and are not intended to be a cut-and-paste howto.  I use Gentoo, so most steps will be geared towards a Gentoo install but the general idea should work on any distribution.</p>
<p><a name="step_install_php_cgi"></a></p>
<h5>1.  Install php-cgi and disable mod_php</h5>
<p>The PHP interpreter can run in three different modes: as an Apache module, as a CGI binary or as a command line command.  Typically, separate binaries are built for the CGI and CLI modes, <code>php-cgi</code> and <code>php</code> respectively.  On Gentoo, each mode is associated with a USE flag: <code>apache2</code> for mod_php, <code>cgi</code> for a CGI binary, and <code>cli</code> for command-line PHP.  The <code>cgi</code> USE flag must be enabled.  If it isn&#8217;t, add it to <code>/etc/make.conf</code> or <code>/etc/portage/package.use</code> and recompile PHP.  On other distributions, search for a php-cgi binary.</p>
<p>You will want to disable mod_php (if it was enabled) before implementing FastCGI.  This can be done by commenting out the appropriate LoadModule line in your Apache configuration file:</p>
<div class="file">
# LoadModule php5_module modules/libphp5.so
</div>
<p>On Gentoo, this can be easily done by removing <code>PHP5</code> from the <code>APACHE2_OPTS</code> variable in <code>/etc/conf.d/apache2</code>.</p>
<p><a name="step_install_mod_fastcgi"></a></p>
<h5>2. Install and enable mod_fastcgi Apache module</h5>
<p>We already discussed why we <a href="#mod_fastcgi">must use mod_fastcgi instead of mod_fcgid</a>.  On Gentoo, installing mod_fastcgi can easily be done by running:</p>
<div class="prompt">
$ <span class="cmd">sudo emerge mod_fastcgi</span>
</div>
<p>For other distributions, try installing a mod_fastcgi package or see the <a href="http://www.fastcgi.com/mod_fastcgi/INSTALL.AP2">FastCGI Installation Notes</a>.</p>
<p>Make sure your Apache conf file contains the line:</p>
<div class="file">
LoadModule fastcgi_module modules/mod_fastcgi.so
</div>
<p>On Gentoo, this line is found in <code>/etc/apache/modules.d/20_mod_fastcgi.conf</code>.  mod_fastcgi is enabled by adding FASTCGI to the <code>APACHE2_OPTS</code> variable in <code>/etc/conf.d/apache2</code>.</p>
<p><a name="step_install_apc"></a></p>
<h5>3. Install and configure the APC Opcode Cache</h5>
<p>To install APC on Gentoo, simply run:</p>
<div class="prompt">
$ <span class="cmd">sudo emerge pecl-apc</span>
</div>
<p>For other distributions, see the <a href="http://www.php.net/manual/en/apc.installation.php">Alternative PHP Cache installation instructions</a>.</p>
<p>Once installed, look for the <code>apc.ini</code> file in your php extension configuration directory (e.g.  <code>/etc/php/cgi-php5/ext-active</code>).  The default apc.ini works with one exception.  You need to comment out <code>apc.shm_size="30"</code> (line 5 below).  Commenting this line will enable us to set it per user later.</p>
<p>My <code>apc.ini</code> file looks like:</p>
<pre class="brush: plain; highlight: [5];">
extension=apc.so
apc.enabled=&quot;1&quot;
apc.shm_segments=&quot;1&quot;
;commenting this out allows you to set it in each fastcgi process
;apc.shm_size=&quot;30&quot;
apc.num_files_hint=&quot;1024&quot;
apc.ttl=&quot;7200&quot;
apc.user_ttl=&quot;7200&quot;
apc.gc_ttl=&quot;3600&quot;
apc.cache_by_default=&quot;1&quot;
;apc.filters=&quot;&quot;
apc.mmap_file_mask=&quot;/tmp/apcphp5.XXXXXX&quot;
apc.slam_defense=&quot;0&quot;
apc.file_update_protection=&quot;2&quot;
apc.enable_cli=&quot;0&quot;
apc.max_file_size=&quot;1M&quot;
apc.stat=&quot;1&quot;
apc.write_lock=&quot;1&quot;
apc.report_autofilter=&quot;0&quot;
apc.include_once_override=&quot;0&quot;
apc.rfc1867=&quot;0&quot;
apc.rfc1867_prefix=&quot;upload_&quot;
apc.rfc1867_name=&quot;APC_UPLOAD_PROGRESS&quot;
apc.rfc1867_freq=&quot;0&quot;
apc.localcache=&quot;0&quot;
apc.localcache.size=&quot;512&quot;
apc.coredump_unmap=&quot;0&quot;
</pre>
<p><a name="step_enable_suexec"></a></p>
<h5>4. Install/enable Apache suEXEC</h5>
<p>Apache 2.2 contains built-in support for executing CGI programs as a different user id and group id than the webserver.  This support must be compiled into Apache.  On Gentoo, use the <code>suexec</code> USE flag and recompile apache.  On other distributions, see <a href="http://httpd.apache.org/docs/2.2/suexec.html#install">Configuring &#038; Installing suEXEC</a>.</p>
<p><a name="step_create_wrappers"></a></p>
<h5>5. Create wrapper scripts</h5>
<p>The <a href="http://httpd.apache.org/docs/2.2/suexec.html#model">Apache suEXEC security model</a> requires that the CGI binary meet some pretty stringent requirements concerning file ownership and permissions.  Rather than copying the php-cgi binary for each user, we create multiple wrapper scripts around the php-cgi binary.  These wrapper scripts allow us to set options on a per-user basis.</p>
<p>I keep my wrapper scripts in <code>/var/www/bin</code>, though you may keep yours wherever you want.  Each user has a directory in <code>/var/www/bin</code>, for example:</p>
<div class="prompt">
$ <span class="cmd">ls -l /var/www/bin</span><br />
dr-xr-xr-x 2 bob bob 104 Jun 24 13:56 bob/<br />
dr-xr-xr-x 2 sue     sue     104 Jun 24 13:56 sue/<br />
dr-xr-xr-x 2 joe      joe      104 Jun 24 13:53 joe/
</div>
<p>Inside each user&#8217;s bin directory is a single wrapper script, <code>php-fastcgi</code>:</p>
<div class="prompt">
$ <span class="cmd">ls -l /var/www/bin/bob/</span><br />
-r-xr-x&#8208;&#8208;&#8208; 1 bob bob 145 Jun 24 13:56 php-fastcgi
</div>
<p>I&#8217;ve shown the <code>ls -l</code> output to show the file and directory ownership and permissions.  These are important, and Apache suEXEC will not work correctly if the owner and permissions are not correct.</p>
<p>The contents of the <code>php-fastcgi</code> file in each user&#8217;s bin directory (see below for an explanation):</p>
<pre class="brush: bash;">
#!/bin/sh

PHP_FCGI_CHILDREN=5
export PHP_FCGI_CHILDREN
PHP_FCGI_MAX_REQUESTS=500
export PHP_FCGI_MAX_REQUESTS

umask 0022
exec /usr/bin/php-cgi -d apc.shm_size=25
</pre>
<p><b>PHP_FCGI_CHILDREN</b><br />
This variable tells PHP how many child processes it should spawn.  As we discussed earlier, our PHP process will act as &#8220;process manager&#8221; and pass incoming requests to its children.  The parent will maintain a single opcode cache which each child will share.  The PHP_FCGI_CHILDREN variable tells PHP how many children to spawn.  Another way to think of this is the number of concurrent PHP requests that can be handled per user.</p>
<p><b>PHP_FCGI_MAX_REQUESTS</b><br />
PHP is known for memory leaks in long running processes.  This variable causes each child process to be restarted once it has served a given number of requests (e.g. 500).  Only the child process is restarted, the parent process remains.  Since the parent process maintains the opcode cache, the opcode cache persists.</p>
<p><b>umask 0022</b><br />
This sets the umask the PHP binary will run under.  Some people may prefer a stricter umask such as 0077, however I&#8217;ve found 0022 works best as it allows the Apache server running as <em>nobody</em> to read static files written earlier by a suEXEC&#8217;d PHP process.  Some PHP applications (WordPress plugins) do not do a good job with permissions, and a strict umask can cause applications to fail.</p>
<p><b>exec /usr/bin/php-cgi -d apc.shm_size=25</b><br />
This line calls the php-cgi binary and modifies the APC cache size.  It is possible to configure this to use a separate <code>php.ini</code> file instead of setting configuration parameters on the command line, however I like the ability to share a single php.ini file.</p>
<p><a name="step_global_apache_settings"></a></p>
<h5>6. Edit global Apache settings</h5>
<p>There are two sets of settings you must configure in Apache: those that affect all users and those that affect a specific user.  This section describes global settings that affect all users.</p>
<p>I like to keep my global settings in my <code>/etc/apache/modules.d/20_mod_fastcgi.conf</code> file, but these can go in any part of your <code>http.conf</code> file.  Most of the time you do not want this in a <code>VirtualHost</code> section.  My global mod_fastcgi settings look like this (see below for an explanation):</p>
<pre class="brush: xml;">
&lt;IfDefine FASTCGI&gt;
LoadModule fastcgi_module modules/mod_fastcgi.so

FastCgiConfig -idle-timeout 20 -maxClassProcesses 1
FastCgiWrapper On

AddHandler php5-fcgi .php
Action php5-fcgi /cgi-bin/php-fastcgi

&lt;Location &quot;/cgi-bin/php-fastcgi&quot;&gt;
   Order Deny,Allow
   Deny from All
   Allow from env=REDIRECT_STATUS
   Options ExecCGI
   SetHandler fastcgi-script
&lt;/Location&gt;

&lt;/IfDefine&gt;
</pre>
<p><b>FastCgiConfig</b><br />
The <a href="http://www.fastcgi.com/drupal/node/25#FastCgiConfig">FastCgiConfig</a> configuration directive sets parameters for all dynamic FastCGI processes.  The idle-timeout causes FastCGI to abort a request if there is no activity for more than 20 seconds.  The maxClassProcesses option is very important: it tells FastCGI to only spawn one php-cgi process regardless of how many requests are pending.  Remember that our PHP process will spawn its own children, so FastCGI only needs to spawn one.  Until <a href="http://pecl.php.net/bugs/bug.php?id=11988">this APC bug</a> is fixed, this is necessary to allow sharing the APC cache among children.</p>
<p><b>FastCgiWrapper</b><br />
The <a href="http://www.fastcgi.com/drupal/node/25#FastCgiWrapper">FastCgiWrapper</a> configuration directive is needed to allow suEXEC to work.</p>
<p><b>AddHandler / Action</b><br />
The <a href="http://httpd.apache.org/docs/2.2/mod/mod_mime.html#addhandler">AddHandler</a> and <a href="http://httpd.apache.org/docs/2.2/mod/mod_actions.html#action">Action</a> configuration directives tell Apache to handle all files ending in <code>.php</code> with the <code>php-fastcgi</code> script in <code>cgi-bin</code>.  In the <a href="#step_user_apache_settings">next step</a>, you&#8217;ll see how we alias this cgi-bin directory for each individual user.</p>
<p><b>Location</b><br />
The <a href="http://httpd.apache.org/docs/2.2/mod/core.html#location">Location</a> directive tells Apache how to handle requests to <code>/cgi-bin/php-fastcgi</code>.  The <code>Allow from env=REDIRECT_STATUS</code> on line 13 prevents users from executing this script directly.  With this line, the only way to execute <code>php-fastcgi</code> is by requesting a file ending in <code>.php</code>.</p>
<p><a name="step_user_apache_settings"></a></p>
<h5>7. Edit per-user Apache settings</h5>
<p>On my host, every virtual host is associated with one user.  And every user has exactly one opcode cache.  A single user can have multiple virtual hosts, but these virtual hosts share the same opcode cache.</p>
<p>For each virtual host, I add the following lines, customized for the user associated with that virtual host:</p>
<pre class="brush: xml; highlight: [4,5,6,7];">
&lt;VirtualHost *:80&gt;
ServerName www.sue.bltweb.net
...
&lt;IfModule mod_fastcgi.c&gt;
   SuexecUserGroup sue sue
   Alias /cgi-bin/ /var/www/bin/sue/
&lt;/IfModule&gt;
...
&lt;/VirtualHost&gt;
</pre>
<p>When combined with the <a href="#step_global_apache_settings">global apache settings</a> and the <a href="#step_create_wrappers">wrapper scripts</a>, this will launch the php-cgi binary using suEXEC to execute as the appropriate user and group whenever a <code>.php</code> file is requested.</p>
<p>There are several different ways to call the FastCGI binary.  On my hosts, users don&#8217;t have access to their cgi-bin directory.  The /var/www/bin directory is not accessible by ordinary users.  This doesn&#8217;t have to be the case, the cgi-bin directory could be stored in the user&#8217;s directory.  It is important to note that allowing the user to modify php.ini values allows them to modify their opcode cache size, which could have severe repercussions on RAM usage.</p>
<h4>Pros and Cons</h4>
<p>The implementation described above is only one of many ways to implement FastCGI and APC.  In my opinion, it is the best way to meet my goals, but in this section I&#8217;ll try to outline some of the advantages and disadvantages of my setup.</p>
<h5>Advantages</h5>
<ul>
<li>Different users can have different APC cache sizes</li>
<li>Multiple concurrent PHP requests can be handled simultaneously</li>
<li>RAM usage is predictable as a product of the number of users on the host</li>
<li>Server is better secured against attacks from the inside since PHP processes run as the user who owns the script</li>
<li>Resource usage can be monitored since each user has a separate PHP process</li>
<li>A PHP crash doesn&#8217;t mean an Apache crash.  If a PHP process crashes it is restarted automatically.
</ul>
<h5>Disadvantages</h5>
<ul>
<li>The process manager built into mod_fastcgi isn&#8217;t used.  One of the motivations behind mod_fcgid was to improve upon the process manager in mod_fastcgi.</li>
<li>The newer mod_fcgid cannot be used as it will only send one request at a time to the PHP process, thus multiple requests can&#8217;t be handled simultaneously</li>
<li>Maintaining separate opcode caches per user uses a considerably larger amount of RAM than a single opcode cache used with mod_php</li>
<li>Users cannot alter php.ini files</li>
<li>If a PHP script crashes it has potential to take down all of the PHP requests currently being processed for that user
</ul>
<h4>Performance</h4>
<p>In my next post I&#8217;ll try to cover RAM usage, performance, benchmarks, compatibility and best practices.  This post is already way too long; I&#8217;m surprised you are even still reading it!</p>
<p>Stay tuned for more information on using FastCGI with a PHP APC opcode cache.  In the meantime let me know what you think of this approach.  Have you tried it?  Know of a better way?  Found any bugs or problems?  Leave a comment below!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
	</channel>
</rss>
