January 27, 2012

Drupal Performance Tuning ++

Evan wrote a good article about the various high level options you have when looking at performing some lightweight drupal performance tuning. Good if you're just looking to optimize page load times and resource utilization on a server you may not have command line access to. What I'm going to share is the learning we've done over the course of many Drupal deployments in VPS and Dedicated hosting environments where we DO have access to some of the more low level options and settings made available with root or administrator access.

For this post, I'm just going to cover Apache and APC. I'll cover MySQL and PHP in a follow up post.

Typically we deploy sites on the Debian based Ubuntu so all config options and file locations are specific to that, but you should have no trouble translating the info here to other Linux distributions.

First at bat: Apache.

I won't go into "non-standard" web server configurations like running nginx as a proxy (though, it's a great option and I highly suggest it if you can) CDN's or running anything other than mod_php. Just standard Apache with mod_php enabled.

First off, take a look at /etc/apache2/mods-enabled

For every thread that Apache spins up to handle a request, all those modules get loaded and they all take up your precious RAM. Take a look, I bet you don't recognize half of those. If you do, good for you, but then you probably also know that we definitely don't need all of them enabled to run a

From a standard apache package install on ubuntu i've removed the following:

  • sudo a2dismod cgi
    when was the last time you ever put anything in cgi-bin?
  • sudo a2dismod negotiation
    we'll generally handle our content negotiations within php
  • sudo a2dismod autoindex
    prettifies a directory without a directory index file (index.html, index.php) present
  • sudo a2dismod env
    provides environment variables to cgi scripts and ssi pages. won't need those for Drupal
  • sudo a2dismod reqtimeout
    not 100% sure, but i turned it off and everything seems fine
  • sudo a2dismod setenvif
    again, not something we really need for PHP and Drupal

The following moduels are useful for restricting access at the web server level. If you don't need any access restriction that Drupal already provides, then you can turn these off without issue as well.

  • sudo a2dismod auth_basic
    provides authentication based on the following modules
  • sudo a2dismod authn_file
    provides a plaintext user/pass lookup for authenticating users
  • sudo a2dismod authz_default
    fallback module to deny all access if groupfile/host and user are not enabled
  • sudo a2dismod authz_groupfile
    provides authentication based on groups defined on the server itself
  • sudo a2dismod authz_host
    provides ip or hostname authentication
  • sudo a2dismod authz_user
    similair to the group authentication, but for users

Disabling modules won't make the biggest impact on speed, but it will free up a bit of memory that you can use else where. Extra threads for Apache or more RAM allocated to APC.

Next up is the apache config file. /etc/apache2/apache2.conf

I'll just list a few key variables that should be changed from their defaults.

  • Timeout 300
    Timeout tells Apache how long it can wait, process and return a single response from a client. A timeout of 300 is way too high. You'll want to drop this to something like 30.
  • MaxKeepAliveRequests 100
    With KeepAlive on by default, we're telling Apache to serve files using persistent connections. So one client will have one thread serving multiple resources needed to display a page load. With Drupal, generally we see pages that can easily load a hundred files or more. CSS, JS, images and fonts. Setting the MaxKeepAliveRequests to something higher will allow Apache to serve more content within a single persistent connection. 
  • KeepAliveTimeout 5
    KeepAliveTimeout tells your persistent connection how long it should sit around and wait for the next request from a client. I would set this a little lower, but not too low. Something around 2 or 3 seconds should be sufficient.

Your mpm.

Out of the box, you're most likely running the prefork mpm. The defaults are pretty good for your standard Drupal install, but we've found bumping up the min and max spare servers can help when dealing with a sudden small spike in traffic. Changes to this usually require some research in how your server is already performing, but the thing to keep in mind is the more "Servers" you have running the more RAM you'll be using. Then again, what is the RAM for on a dedicated web server if not for serving web pages.

StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxClients 150 MaxRequestsPerChild 0

What we generally use for a moderate site that handles a few thousand hits per day.

StartServers 5 MinSpareServers 10 MaxSpareServers 20 MaxClients 200 MaxRequestsPerChild 0

Next at bat: APC

Like Evan mentioned in his article. APC is a must. Not a "it would be nice", but an absolute must. The thing about APC that we learned a while ago is that installing it is not enough. In fact, it can actually make your site run A LOT slower in it's default state. The key is getting in and configuring it properly. This can take a little trial and error, but it's definitely worth it. Below is our apc configuration and on our systems it's located at /etc/php5/conf.d/apc.ini

extension=apc.so apc.enabled=1 apc.shm_segments=1 apc.shm_size=64M apc.optimization=0 apc.num_files_hint=512 apc.user_entries_hint=1024 apc.ttl=0 apc.user_ttl=0 apc.gc_ttl=600 apc.cache_by_default=1 apc.filters="apc\.php$" apc.slam_defense=0 apc.use_request_time=1 apc.mmap_file_mask=/dev/zero apc.file_update_protection=2 apc.enable_cli=0 apc.max_file_size=2M apc.stat=1 apc.write_lock=1 apc.report_autofilter=0 apc.include_once_override=0 apc.rfc1867=0 apc.rfc1867_prefix="upload_" apc.rfc1867_name="APC_UPLOAD_PROGRESS" apc.rfc1867_freq=0 apc.localcache=1 apc.localcache.size=512 apc.coredump_unmap=0 apc.stat_ctime=0

This configuration is for a moderately sized Drupal install. What you'll want to do is find the apc.php file at /usr/share/php/apc.php and copy it into your web server root. This file will help you determine how effectively APC is working on your system and whether you need to make adjustments to the amount of RAM you make available to APC.

Good APC

The graph above is from our development server so we've set the RAM a little higher, but the pertinent info is the same. What you want to pay attention to is your hits/misses and your fragmentation. For hits and misses you want to make sure your hits are quite a bit higher than your misses. You'll get a miss the first time you hit a page, but beyond that you should be getting hits. Fragmentation is more important. Fragmentation happens when you have a new item that needs to be stored in cache and you have no available room in your cache to store it. APC will discard a cached item to make room for the new item. Fragmentation occurs when the item being discarded is larger than the new item coming in. That left over space can only be used if a file small enough can fill the spot, otherwise it sites un-used. FRAGMENTATION. To prevent fragmentation, you'll want to monitor APC's memory usage as page loads come in and once you start to see fragmentation, increase the RAM allocated to APC by a few MB's and try again. The graph below is something you never want to see.


These tweaks should get you quicker page loads with less RAM being taken up by Apache. One of the big slowdowns in Drupal can be MySQL so if you haven't got the performance bump you hoped for here, then wait for the MySQL/PHP post coming soon.

If there's anything I missed or anything that I inevitably got completely wrong, feel free to post a comment and publicly scold me.