Twitter Updates for 2009-04-21

Load balanced and High Availability cluster for your web site under USD 60 pm – Part 2

Update 2009-09-02: Now I’m using a single Linode and a Xen VPS from my very own hosting service. This means the VPSes have one less thing in common; hosting company.

As I promised, here is the post that will discuss in detail how I configured my cluster of 2 nodes to host my sites.

Setting up SSH tunnels

You have to setup a SSH tunnel between the nodes. In order to do that you need to allow restricted root logins into your nodes. Using your favourite text editor edit /etc/ssh/sshd_config and change the line PermitRootLogin to PermitRootLogin forced-commands-only.

Then generate SSH authentication keys for all your nodes and add the public keys to /root/.ssh/authorized_keys on other nodes. Keys can be generated by running ssh-keygen. By default your private key is stored in /root/.ssh/id_rsa and public key in /root/.ssh/id_rsa.pub. Your public key will look similar to bellow (Key shortened for brevity)

[source lang='plain' options='toolbar: false; gutter: false;' ]ssh-rsa AAAA...w== [email protected][/source]

To enable tunnel only access via root you need to add tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0" before your public key in /root/.ssh/authorized_keys. Your /root/.ssh/authorized_keys will look something like bellow.

[source lang='plain' options='toolbar: false; gutter: false;' ]tunnel='0',command='/sbin/ifdown tun0;/sbin/ifup tun0' ssh-rsa AAAA...w== [email protected][/source]

Now setup the actual tunnel. Add following lines to /etc/network/interfaces in the “server”

[source lang='plain']
auto tun0
iface tun0 inet static
address 10.100.2.1
netmask 255.255.255.0
pointopoint 10.100.2.2
[/source]

and the following in the “client”

[source lang='plain']
auto tun0
iface tun0 inet static
pre-up ssh -S /var/run/ssh-myvpn-tunnel-control -M -f -w 0:0 example.com true
pre-up sleep 5
address 10.100.2.2
pointopoint 10.100.2.1
netmask 255.255.255.0
up route add -net 10.100.2.0 netmask 255.255.255.0 gw 10.100.2.0 tun0
post-down ssh -S /var/run/ssh-myvpn-tunnel-control -O exit example.com
[/source]

Now you only have to restart networking to enable the tunnel. Now your nodes will be in their own VPN.

Setting up document root replication (rsync)

Share /var/www via rsync. You need to install rsync and add following to /etc/rsyncd.conf if they are not already there.

[source lang='plain']max connections = 2
log file = /var/log/rsync.log
timeout = 300

[www]
comment = DOC Root
path = /var/www
read only = yes
list = yes
uid = www-data
gid = www-data
auth users = replicator
secrets file = /etc/rsyncd.secrets[/source]

Add following cron jobs to www-data crontab (crontab -e)

[source lang='plain' options='gutter: false; toolbar: false;' ]
1/10 * * * * test -r /tmp/rsync.docroot.lock || touch /tmp/rsync.docroot.lock && rsync -aP rsync://[email protected]/www/ /var/www/ --password-file=/etc/rsync.secrets --contimeout=30 > /dev/null 2>1 && rm /tmp/rsync.docroot.lock[/source]

[source lang='plain' options='gutter: false; toolbar: false;' ]
1/10 * * * * test -r /tmp/rsync.docroot.lock || touch /tmp/rsync.docroot.lock && rsync -aP rsync://[email protected]/www/ /var/www/ --password-file=/etc/rsync.secrets --contimeout=30 > /dev/null 2>1 && rm /tmp/rsync.docroot.lock[/source]

Setting up session_mysql

Next let us setup session_mysql such that we can forget about replicating PHP session 🙂 .

Install php5-dev and libmysql++-dev, download session_mysql and extract it, running following commands as root within the extracted location.

[source lang='bash']export PHP_PREFIX='/usr'
$PHP_PREFIX/bin/phpize
./configure --enable-session-mysql --with-php-config=$PHP_PREFIX/bin/php-config --with-mysql=$PHP_PREFIX
make
make install[/source]

Create the database to store the session data with the following SQL

[source lang='sql']
create database phpsession;
grant all privileges on phpsession.* to phpsession identified by 'phpsession'; -- CHANGE DEFAULT PASSWORD
create table phpsession(
sess_key char(64) not null,
sess_mtime int(10) unsigned not null,
sess_host char(64) not null,
sess_val mediumblob not null,

index i_key(sess_key(6)),
index i_mtime(sess_mtime),
index i_host(sess_host)
);[/source]

Add the following to your php.ini (or /etc/php5/conf.d/session_mysql.ini)

[source lang='plain']
session.save_handler = 'mysql'
session_mysql.db='host=localhost db=phpsession user=phpsession pass=phpsession'
[/source]

Do not forget to change the default password. Restart Apache or Lighttpd (or any other web server you are using).

MySQL asynchronous two way replication

I’m sure some of you are asking why I went for asynchronous replication. Main reasons being flexibility and lack of nodes (My cluster is just 2 nodes).

Stop MySQL from listening only to local connections. Remember to review your user table (mysql.user) to make sure you don’t grant wild card access like 'user'@'%'. Comment out bind-address in/etc/mysql/my.cnf in all nodes. Then add following to node1

[source lang='plain']server-id = 1
replicate-same-server-id = 0
auto-increment-increment = 2
auto-increment-offset = 1
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M

master-host = 10.100.2.2
master-user = slave_user_0
master-password = your$password
master-connect-retry = 60[/source]

and following to node2

[source lang='plain']server-id = 2
replicate-same-server-id = 0
auto-increment-increment = 2
auto-increment-offset = 2
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M

master-host = 10.100.2.1
master-user = slave_user_1
master-password = your$password
master-connect-retry = 60[/source]

Now create the users only granting them with replication rights. Also make sure you specify the hostname or the IP to make sure someone is not offloading your data 😀 . Following SQL will create the users given in the example. You will have to run the command in both nodes as the data in either node is identical.

[source lang='sql']CREATE USER 'slave_user_1'@'10.100.2.1' IDENTIFIED BY 'your$password';

GRANT REPLICATION SLAVE ON * . * TO 'slave_user_1'@'10.100.2.1' IDENTIFIED BY 'your$password' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;

CREATE USER 'slave_user_2'@'10.100.2.2' IDENTIFIED BY 'your$password';

GRANT REPLICATION SLAVE ON * . * TO 'slave_user_2'@'10.100.2.2' IDENTIFIED BY 'your$password' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;[/source]

Now start MySQL and run following in mysql prompt on each of the nodes.

[source lang='sql']reset master;
stop slave;
start slave;[/source]

Finally

Now you have a cluster of 2 nodes where you can run your PHP site. Your databases are replicated, your user session data is replicated and your document root is replicated. Have fun, if you have issues please post it as a comment.

Load balanced and High Availability cluster for your web site under USD 60 pm

Update 2009-09-02: Now I’m using a single Linode and a Xen VPS from my very own hosting service. This means the VPSes have one more thing less in common, hosting company.

Until recently I used one Linode VPS for hosting all my sites. On 26th March, there was a DDoS attack on one of the Linode customers in the Fremont Datacenter (where my node was as well). This made my sites inaccessible for couple of hours. This got me thinking, what could be done to mitigate such downtime. Answer of course is having a load balanced and high availability cluster. However I couldn’t afford 2 dedicated servers to do this, but I of course can afford 2 Linodes 🙂 . I’ll try to explain how I set up a load balanced, high availability and shared nothing cluster using Linodes (you can use any VPS or dedicated server). I used two Linode 540 s for the job.

All of my web sites are either using PHP, Python or Perl. All of them are using MySQL as the database. Problems I had to solve were;

  1. replicate files across the nodes
  2. replicate databases across the nodes
  3. replicate session (PHP session variables) across the nodes

All the replication needs to be done securely, so I went for a SSH tunnel between the nodes of the cluster. Over which I’ll;

  1. use rsync to replicate/synchronize the document root
  2. use MySQL asynchronous replication (not a NDBCLUSTER) to synchronize data across the nodes
  3. use session_mysql PECL extension to store PHP session in MySQL database transparent to all applications

Check back next week when I’ll post with configuration examples on how I configured my server. If you are in a hurry above pointers are good enough to get you started.

How to customize Web Invoice e-mail templates

In the latest release of Web Invoice – Invoicing and Billing for WordPress plugin you can customize the e-mails sent to your clients. You can customize the subject as well as the e-mail content. Please bear in mind that Web Invoice only supports plain text e-mails, but you can do wonders with plain text 🙂 .  This can definitely come in handy if you want to localize your invoices or just does not like what comes as default.

Now to get to the point, the e-mail templates are found in ‘Web Invoice’ -> ‘E-mail templates’.  There are three templates,

  • Invoice e-mail – First e-mail your client receives about the invoice
  • Reminder e-mail – E-mail sent when send reminder(s) is selected
  • Receipt e-mail – Sent when the client makes a payment. You also need to set ‘Send Payment Confirmation’ to yes in Settings

All variables are of the format %variable_name. Bellow you will find the list of variables available in the current version.

  • %call_sign – First name + Last name of the client
  • %business_name – Business name as set in the ‘Settings’
  • %recurring – Recurring or not
  • %amount – Amount, with currency symbol and currency formatted
  • %link – Link to the invoice
  • %business_email – Business e-mail as set in the ‘Settings’
  • %subject – Invoice subject
  • %description – Invoice description

You can definitely use the default templates as a guideline for your templates. IMHO, default templaes are awesome 😉

Don’t go away, there is more. You can write your own plugin to add more variables. e.g. you want to use web invoice along with a booking system. In your plugin add a filter for web_invoice_email_variables and add to the global array (variable) $web_invoices_email_variables.

I want to hear what you think about this feature or Web Invoice in general. Please post your ideas and comments in the Web Invoice community forum. Do not forget to subscribe to my RSS feed to get latest news about WordPress plugins and all things web 🙂

Web Invoice exceeds 1000 downloads

Web Invoice – Invoicing and billing for WordPress plugin has exceeded 1000 downloads. It has 1172 dowloads to date. First release of the plugin was made on March 15, 2009 and now in less than 1 month it has more than 1000 downloads.

Web Invoice is ideal for web developers, SEO consultants, general contractors, or anyone with a WordPress blog and clients to bill. If you come under any of the above, you should give Web Invoice a try.

Download or read more about Web Invoice.

How to migrate from WP-Invoice to Web Invoice?

Web Invoice and WP-Invoice are two WordPress plugins that allow sending invoices and bills. Web Invoice is a fork of WP-Invoice with lot of new features that most people will want. In the current release (1.6.2) they are:

  • Send invoice reminders to customers with a secured link back to the
    web invoice
  • Moneybookers, AlertPay or PayPal available if you don’t have a
    credit card processing account
  • Setup recurring billing using Authorize.net’s ARB (Automatic Recurring Billing) feature or Moneybookers
  • Automatically mark invoices paid via Moneybookers as paid (Requires
    merchant status)
  • Automatically mark invoices paid via AlertPay as paid (Requires
    business status)
  • Split gateway support (Your client is given the option of choosing the preferred gateway from the list of gateways you support). e.g PayPal and Moneybookers
  • All user interfaces are internationalized

If you are already using WP-Invoice and would like to leverage these new features, it’s simple. Follow the 3 step process to switch from WP-Invoice to Web Invoice.

  1. Rename the tables invoice_log to web_invoice_log, invoice_main to web_invoice_main, invoice_meta to web_invoice_meta. (Assuming $table_prefix is empty)
  2. Download Web Invoice WordPress plugin and upload it to your server
  3. Activate Web Invoice

That’s all, you can use Web Invoice without losing that you generated with WP-Invoice.

SyntaxHighlighter2 WordPress plugin released

SyntaxHighlighter2 WordPress plugin was released yesterday on WordPress Extend plugin repository. SyntaxHighlighter2 allows you to easily post syntax highlighted code all without loosing it’s formatting or making an manual changes. Main addition is upgrade to SyntaxHighlighter JavaScript 2.0 by Alex Gorbatchev. Also in this release the web master/blogger is given the ability to choose from 6 themes for the SyntaxHighlighter.

If you already using SyntaxHighlighter WordPress plugin and using XHTML, you should upgrade to SyntaxHighlighter2 because SyntaxHighlighter uses invalid XHTML. Bellow you will find the sample WordPress configuration file with SyntaxHighlighter2 🙂 .

[sourcecode language=’php’ ]
[/sourcecode]

WordPress object cache with memcached backend for vanilla WordPress

WordPress object cache with memcached backend is mainly meant for WPMU and doesn’t work well if you happened to have many vanilla WordPress installations using the same memcached backend like it is pointed out here and here.

I have created a simple patch which you can apply against object-cache.php (downloadable from WordPress plugin repository) or here is the file already patched. The patch prepends the $blog_id to the object key. You will have to specify global variable $blog_id in wp-config.php and give it a unique id (like your blog url).
[sourcecode language=’php’]global $blog_id;
$blog_id = ‘mohanjith_net’;[/sourcecode]

Read only Facebook – Database write failed

To my despair Facebook went read only for a brief period of time (aproximately 10 minutes). I was not able to comment on my friend’s status messages, or like them, or post my own status message; I was confronted with the message bellow. However adding friends, approving requests were working fine. I believe the issue was isolated to status messages and related functionality (IMHO most popular).

facebook-home_1235722315186

Facebook do not dare to do something like this again, I need Facebook to keep in touch with my friends. I’m not a Facebook addict, just that the issue happened to coincide with the time of day I would get on Facebook to get an update on my friends andmake my own online presence.

I would like to know whether you had a similar issue, or was it just me. At the time of writing the issue is fixed. Thanks Facebook for the quick fix.