Tuesday, November 1, 2011

Supercharge WordPress

SkyHi @ Tuesday, November 01, 2011
WordPress, the popular content managing system (CMS), is easy to set up and use, and well supported by both its community and professional consultants. WordPress depends upon a complete stack that comprises an operating system, database, web server, and PHP. If you can optimize this stack, you can enhance the performance of your site. Here are some tricks and best practices for a setup that will improve your throughput without forcing you to upgrade your hardware.
Let’s start with a look at the Linux operating system, MySQL database, and nginx web server. Later we’ll get to PHP, some setup and plugin work with WordPress itself, and Varnish Cache, a web application accelerator.


Operating System

In theory you could run WordPress on a Windows server using software like EasyPHP, a package that allow you to take advantage of all the power and flexibility that the dynamic PHP language offers with Windows. The package includes an Apache server, a MySQL database, phpMyAdmin, and development tools for websites and applications.
But for its better license, performance, and optimization capabilities, we’ll go with GNU/Linux, and in particular with CentOS 6. It’s a stable and free Linux distribution based on Red Hat Enterprise 6, so all the packages and commands work exactly the way they do on RHEL 6. If you prefer another distribution, such as Debian or Arch, you’ll have some files located in paths slightly different or packages with different names or version, but the concepts here are applicable on any GNU/Linux distribution.
Regarding CentOS 6 optimization, and in general for any GNU/Linux distribution, I have three tips:
  • If the server (real or virtual) where you are installing CentOS 6 has more than 3GB of RAM, install a 64-bit system; otherwise, choose the 32-bit version – this will save you some RAM.
  • Usually CentOS 6 uses ext4 as its filesystem type. Ext4 is a journaled filesystem, which registers all changes, so if the system crashes it can recover the affected files. By default Linux records file access timestamps, meaning every time a process accesses a file there is a small overhead to write the timestamp. In a busy environment, removing this feature can speed up operations.
    To remove file access timestamps, edit the /etc/fstab file and add, in the line that refers to your WordPress filesystem, the noatime option after the default options:
    /dev/sda7          /srv          ext4          defaults,noatime          1  2
  • This last tip is true for all the software in the stack, but I’ll write it only here: Be sure to run the latest version of your software. This should be bug-free, and so save you from any known problems.
    Under Linux we want to manage all our software stack with yum, so we’ll add the EPEL (Extra Packages for Enterprise Linux) repository to our system. The EPEL repository was developed by the Fedora community to provide extra add-on packages for RHEL, which means it’s compatible with CentOS.
    To add EPEL on a 32-bit system, run this command as root from a terminal window:
    rpm -ivh http://download.fedora.redhat.com/pub/epel/6/i386/epel-release-6-5.noarch.rpm
    If you have a 64-bit system, run:
    rpm -ivh http://download.fedora.redhat.com/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm
    When adding extra repositories, keep in mind that many include newer versions of packages that are available through the standard channels. This can cause problems, as packages may be automatically upgraded when you don’t want them to be and cease to function as expected. To mitigate this problem, use yum priority and give to the EPEL repository a lower priority than the official CentOS repository.
    Finally, check and update the system with:
    yum check-update
    yum update


Database Server

The database server is the heart of any CMS system. Because it’s where all the content and setup information is stored, it can get a lot of requests per second, so improving this layer can yield huge benefits.
Begin by installing the latest release of MySQL server with the command yum install MySQL-server. A default installation of WordPress 3.2 installs the database tables as MyISAM, so to save some memory you can keep MySQL from loading InnoDB, MySQL’s other default storage engine, which can save up to 100MB of RAM on your server. Now take a look at some of the most important parameters:
The key buffer stores table indexes in memory, allowing for fast lookups and joins. The parameter accept a value that indicates of how much memory allocate for this operation. I’ll suggest a generic value below, but you can adjust it later with MySQLTuner.
Opening tables can be expensive. For example, with MyISAM, MySQL marks MYI header fields to indicate a table is currently in use. You do not want to open too many tables too frequently; it’s better to keep them open for performance. You can achieve this by adjusting the table cache to a size that is large enough to keep most of your tables open. Start with the number I’ve put in the profiles below and tune it with MySQLTuner over time.
MySQL has a query cache that stores results up to a certain size in memory. The cache is handy for quickly returning commonly accessed data when all other forms of caching (including reverse proxies, page cache, and WordPress caches) have not been invoked.
Here are a few example configuration settings for servers of different memory sizes running both MySQL and a web server on the same machine. These are not perfect, but they are good starting points.
For servers with 512MB RAM:

For servers with 1GB RAM:

For servers with 2GB RAM:

Once WordPress has been up and running for some time you can tweak your settings by running MySQLTuner, a Perl script that analyzes your MySQL performance and, based on the statistics it gathers, gives recommendations about which variables you should adjust to increase performance. With MySQLTuner, you can tune your my.cnf file to tease out the last bit of performance from your MySQL server and make it work more efficiently.



HTTP Server

My HTTP server of choice is nginx; in a recent article I talked about some advantages of using this HTTP server with PHP5 in FPM mode versus Apache and mod_php module. If you’ve already added the EPEL repository, run yum install nginx to install nginx 0.8.54-1.el6. While the latest stable release of nginx is currently 1.0.8, installing a package from an official repository gives you a more stable but older version.
Now you should look at a few parameters in the /etc/nginx/nginx.conf configuration file.
worker_processes controls – surprise – the number of worker processes to spawn. A worker is similar to a child process in Apache. Nginx has the ability to put worker processes to use on SMP multiprocessor machines to decrease latency when workers are blocked by disk I/O, or to limit the number of connections per process when select() or poll() is used. The general rule of the thumb is to set the number of nginx workers to two, or the number of CPUs your server has. If you are going to serve sites with a lot of static content, add more workers – up to one per disk.
If your disk subsystem is poor or the load is too high, nginx worker processes may become locked on I/O operations and unable to serve other requests. Run ps ax and examine its output; workers that are in “D” state are locked. Increase the number of worker processes until ps ax returns a number of worker processes not blocked equal to the number of CPUs on your system. You can also add some memory for disk cache to solve this problem.
Along with worker_proceses, worker_connections allows you to calculate a max_clients value:
max_clients = worker_processes * worker_connections
I suggest not making this value too high. If your ulimit -n output is something like 1024, then your worker connections would need to be set to 1024 or less (maybe even 768), and its unlikely that you’ll have “worker_processes x 1024″ simultaneous connections.
Timeouts values specify the amount of time in seconds that nginx will wait for the client to complete the specified action.

client_body_timeout N is the read timeout for the request body from client. If after this time the client sends nothing, nginx returns the error “Request time out” (408).

client_header_timeout N is the timeout reading the title of the request of the client. If after this time the client send nothing, nginx returns the error “Request time out” (408).

keepalive_timeout N N – the first value is for keep-alive connections with the client. The second parameter assigns the value “Keep-Alive: timeout=time” in the header of answer.
Regarding these value I usually keep 30 seconds for the client timeouts and not more than 10 seconds for the keepalive. You can lower the keepalive, to 3 perhaps, if you have a busy site and you notice that you have a lot of connection open. Others suggest setting it to 0, thus making the client reopen a connection.

sendfile on|off Use sendfile when the nginx server can actually ignore the contents of the file it is sending. If set to on, it uses the kernel sendfile() support instead of its own resources on the request.

tcp_nopush on|off enables or disables the TCP_NOPUSH (FreeBSD) or TCP_CORK (Linux) socket option. Note that this option only applies if the sendfile directive is enabled. If tcp_nopush is set to on, nginx will attempt to transmit the entire HTTP response headers in a single TCP packet. Most sources suggest setting it to off.

tcp_nodelay on|off disables the Nagle buffering algorithm. You can use it when the server doesn’t require a response from the client. General web use does need a response, so this should be turned off, unless you need to send out small bursts of information, like tracking mouse movements.

multi_accept on|off tries to accept() as many connections as possible after nginx gets notification about a new connection.
You should also enable all the compression options of nginx. Insert into your nginx.conf file these options:
   worker_processes      2;
   events {
    worker_connections   768;
    use epoll;
   sendfile              on;
   tcp_nopush            off;
   client_body_timeout   30;
   client_header_timeout 30;
   keepalive_timeout     10 10;
   tcp_nodelay           off;
   multi_accept          on;
   gzip on;
   gzip_proxied any;
   gzip_comp_level 2;
   gzip_disable "MSIE [1-6].(?!.*SV1)";
   gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
These are the general options. You can also tune the specific WordPress site options. To do this, change the specific Virtuahost file. I usually give them the name of the site I’m setting up – for example, www.example.com – and I place them in the /etc/httpd/conf.d directory. Below are some suggested options if you are using php5-FPM.
location ~ .php$ {
      fastcgi_split_path_info ^(.+.php)(.*)$;
      fastcgi_pass   backend;
      fastcgi_index  index.php;
      fastcgi_param  SCRIPT_FILENAME  /var/www/$fastcgi_script_name;
  fastcgi_connect_timeout 60;
  fastcgi_send_timeout 180;
  fastcgi_read_timeout 180;
  fastcgi_buffer_size 128k;
  fastcgi_buffers 4 256k;
  fastcgi_busy_buffers_size 256k;
  fastcgi_temp_file_write_size 256k;
  fastcgi_intercept_errors on;

With the changes and additions to the configuration files above, you should have a good, basic optimization of nginx.
At this point you have Nginx up and running with an optimized database server. Next, we’ll see how to optimize php5-fpm. We’ll install and configure APC, the Alternative PHP Cache. We’ll look at plugins that can help your WordPress site respond faster. And we’ll put in front of the web server Varnish, a great application server cache that can do wonders to improve your site’s responsiveness.