Wordpress Cluster Powered

Written by
Date: 2014-12-21 19:37:35 00:00

Wordpress installation on a cluster of servers

Introduction

Blogging is very popular even though a lot of people believe it is not anymore, and Wordpress is still the most used technology, to share your thoughts or work online.

Now if you want to have a server with multiple installations of Wordpress maybe for your family and friends, and want to be sure it will support the load now, and any future load if one or more of the hosted blogs become popular. Then, you want to prepare the infrastructure to be able to handle a big load.

Today we are going to see how to accomplish that.

The ingredients

What we are going to use for this setup is:

  • Ubuntu LTS (14.04 at the time of this writing)
  • Nginx (As a web server and cache)
  • MySQL (To host the databases)
  • PHP-FPM to handle the code.

The setup

We will have and Nginx server in front of the cluster of servers, one or more PHP-FPM servers behind and MySQL server behind all of that, the MySQL server can be a cluster too, but that is beyond the scope of this tutorial.

MySQL as StandAlone

Let's first prepare the MySQL server, I recommend you using Linode or Digital Ocean.

sudo apt-get install mysql-server

Then run security installation program.

sudo mysql_secure_installation

Then you have to make MySQL to listen to the NIC instead of only localhost, to do that edit file: /etc/mysql/my.conf and change this line:

bind-address            = 127.0.0.1

To a new one with the IP you need it to listen on, something like this:

bind-address            = 10.1.1.2

You better use private network IPs, both Digital Ocean and Linode offer you that.

PHP server

Usually PHP is installed with Apache and mod_php module, which is far from ideal, because it forces Apache to run in prefork MPM (here you can read prefork vs worker

What we are going to do now is to install PHP-FPM which is a standalone PHP server that can be invoked by Nginx or any other web server, proceses the code the return back the result.

sudo apt-get update && sudo apt-get install php5-fpm php5-cli php5-mysql

Now edit the file /etc/php5/fpm/php.ini and be sure you have this line.

upload_max_filesize = 8M

And this one too:

post_max_size = 8M

That is going to set the upload size limit for the files uploaded, like pictures to 8M, you can increase it even more if you want.

Set the port to listen for request, better if on a private IP where only your server can access.

Edit the file: /etc/php5/fpm/pool.d/www.conf

Look for this line: listen = /var/run/php5-fpm.sock and change to something like this: listen = 10.132.150.129:9000, be sure to set the right IP

Finally restart PHP-FPM

sudo service php5-fpm restart

Do it again with all other PHP-FPM servers you are installing.

Nginx for Wordpress with caching enabled

It is now the turn for the Nginx server, we will configure it to also cache responses for not logged users, Nginx is great at serving static content and also as a reverse cache proxy server.

We will be installing the latest version available from PPA maintained by the official Nginx team.

sudo add-apt-repository ppa:nginx/stable
sudo apt-get update && sudo apt-get upgrade
sudo apt-get install nginx

It is now time to configure Nginx to work with Wordpress, I will be following the instructions in Wordpress Site.

First create the global folder inside the Nginx configuration main folder.

mkdir /etc/nginx/global

And put this two files there:

restrictions.conf

# Global restrictions configuration file.
# Designed to be included in any server {} block.</p>
location = /favicon.ico {
	log_not_found off;
	access_log off;
}

location = /robots.txt {
	allow all;
	log_not_found off;
	access_log off;
}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
	deny all;
}

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
	deny all;
}

And wordpress.conf

# WordPress single blog rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
	try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|   gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
       access_log off; log_not_found off; expires max;
}

# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ [^/]\.php(/|$) {
	fastcgi_split_path_info ^(.+?\.php)(/.*)$;
	if (!-f $document_root$fastcgi_script_name) {
		return 404;
	}
	# This is a robust solution for path info security issue and works with    "cgi.fix_pathinfo = 1" in /etc/php.ini (default)

	include fastcgi.conf;
	fastcgi_index index.php;
#	fastcgi_intercept_errors on;
	fastcgi_pass php;
    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache $no_cache;

    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 5m;
}

These files are for single Wordpress installations, and not for multisite ones, if you are planning to install multisite read here again.

In the above files fast CGI cache is already included.

We now have almost everything ready to create our first site, so let's create it, go to /etc/nginx/conf.d/ and create a file called www.your-url.com.conf (change name to reflect a real url you own and can use, actually the name is not important, but for better debugging in the future this name convention is a good one).

There are still few things to tweak.

mkdir -p /var/cache/nginx/cache/wordpress.garron.me

To create the place where cache files are going to be stored. And make sure the /etc/nginx/nginx.conf file looks like this one:

user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
	worker_connections 768;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# SSL Settings
	##

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Gzip Settings
	##

	gzip on;
	gzip_disable "msie6";

	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '

                   '"$status" $body_bytes_sent "$http_referer" '

                   '"$http_user_agent" "$http_x_forwarded_for"';

	upstream php {
    server 10.132.150.129:9000;
    server 10.132.150.130:9000;
}

	##
	# Virtual Host Configs
	##

        fastcgi_cache_path /var/cache/nginx/cache/wordpress.garron.me levels=1:2 keys_zone=WORDPRESS:50m inactive=60m;
		     fastcgi_cache_key "$scheme$request_method$host$request_uri";
		     fastcgi_cache_use_stale error timeout invalid_header http_500;


	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

Be sure that this part is correctly configured:

	upstream php {
    server 10.132.150.129:9000;
    server 10.132.150.130:9000;
}

It should point to all the PHP server you have available.

Synchronizing servers

Nginx does not sent the whole file to the PHP server it just sent the location of the file, so PHP server can parse it and return with a result.

Because of that all PHP-FPM stand alone servers need to have the same info in the same places, therefore we need to copy all wordpress installation folder from the main Nginx server to all PHP-FPM servers.

We will use NFS share to sync data between servers, there are other ways to do this, but this way is simpler.

On the server side:

sudo apt-get update
sudo apt-get install nfs-kernel-server

Then share the web root folder

vim /etc/exports

Add this to the file:

/var/www/wordpress.garron.me 10.132.150.129(rw,sync,no_root_squash,no_subtree_check)
/var/www/wordpress.garron.me 10.132.150.130(rw,sync,no_root_squash,no_subtree_check)

Create the table of your exports.

sudo exportfs -a

And start the NFS server:

sudo service nfs-kernel-server start

On the client side

This will have to be done twice, or more, one per each PHP server you have.

sudo apt-get update
sudo apt-get install nfs-common

Create the folder where the shares are going to be mounted, should be the same place where they are in the Nginx server.

sudo mkdir -p /var/www/wordpress.garron.me

That is for this example only, be sure to match the Nginx path.

Finally configure the client to mount the shares every time it is booted.

vim /etc/fstab

And add something like this:

1.2.3.4:/home    /var/www/wordpress.garron.me   nfs auto,noatime,nolock,bg,nfsvers=4,intr,tcp,actimeo=1800 0 0

Be sure to change 1.2.3.4 to match the IP of your Nginx server. (better using internal private IPs)

Installing Wordpress

You can now follow the famous 5 minutes installation guide from the Wordpress site:

Just be sure to create the Database using something like the command below in order to permit remote access, remember that the PHP server is not the same as the MySQL one.

GRANT ALL PRIVILEGES ON *.* TO 'USERNAME'@'%' IDENTIFIED BY 'PASSWORD' WITH GRANT OPTION;

Here is the detailed installation instructions for Wordpress.