{"id":273,"date":"2009-04-14T14:21:13","date_gmt":"2009-04-14T14:21:13","guid":{"rendered":"https:\/\/www.mohanjith.net\/blog\/?p=273"},"modified":"2010-01-10T10:28:16","modified_gmt":"2010-01-10T10:28:16","slug":"load-balanced-and-high-availability-cluster-for-your-web-site-under-usd-60-pm-part-2","status":"publish","type":"post","link":"https:\/\/mohanjith.net\/blog\/2009\/04\/load-balanced-and-high-availability-cluster-for-your-web-site-under-usd-60-pm-part-2.html","title":{"rendered":"Load balanced and High Availability cluster for your web site under USD 60 pm &#8211; Part 2"},"content":{"rendered":"<p><strong>Update 2009-09-02:<\/strong> Now I&#8217;m using a single Linode and a Xen VPS from my very own <a href=\"http:\/\/mohanjith.com\/xen-linux-vps.html\">hosting service<\/a>. This means the VPSes have one less thing in common; hosting company.<\/p>\n<p>As I promised, here is the post that will discuss in detail how I configured my cluster of 2 nodes to host my sites.<\/p>\n<p><strong>Setting up SSH tunnels<\/strong><\/p>\n<p>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 <code>\/etc\/ssh\/sshd_config<\/code> and change the line <code>PermitRootLogin<\/code> to <code>PermitRootLogin forced-commands-only<\/code>.<\/p>\n<p>Then generate SSH authentication keys for all your nodes and add the public keys to <code>\/root\/.ssh\/authorized_keys<\/code> on other nodes. Keys can be generated by running <code>ssh-keygen<\/code>. By default your private key is stored in <code>\/root\/.ssh\/id_rsa<\/code> and public key in <code>\/root\/.ssh\/id_rsa.pub<\/code>. Your public key will look similar to bellow (Key shortened for brevity)<\/p>\n<p><code>[source lang='plain' options='toolbar: false; gutter: false;' ]ssh-rsa AAAA...w== root@example.com[\/source]<\/code><\/p>\n<p>To enable tunnel only access via root you need to add <code>tunnel=\"0\",command=\"\/sbin\/ifdown tun0;\/sbin\/ifup tun0\" <\/code> before your public key in <code>\/root\/.ssh\/authorized_keys<\/code>. Your <code>\/root\/.ssh\/authorized_keys<\/code> will look something like bellow.<\/p>\n<p><code>[source lang='plain' options='toolbar: false; gutter: false;' ]tunnel='0',command='\/sbin\/ifdown tun0;\/sbin\/ifup tun0' ssh-rsa AAAA...w== root@example.com[\/source]<\/code><\/p>\n<p>Now setup the actual tunnel. Add following lines to <code>\/etc\/network\/interfaces<\/code> in the &#8220;server&#8221;<\/p>\n<p><code>[source lang='plain']<br \/>\nauto tun0<br \/>\niface tun0 inet static<br \/>\naddress 10.100.2.1<br \/>\nnetmask 255.255.255.0<br \/>\npointopoint 10.100.2.2<br \/>\n[\/source]<\/code><\/p>\n<p>and the following in the &#8220;client&#8221;<\/p>\n<p><code>[source lang='plain']<br \/>\nauto tun0<br \/>\niface tun0 inet static<br \/>\npre-up ssh -S \/var\/run\/ssh-myvpn-tunnel-control -M -f -w 0:0 example.com true<br \/>\npre-up sleep 5<br \/>\naddress 10.100.2.2<br \/>\npointopoint 10.100.2.1<br \/>\nnetmask 255.255.255.0<br \/>\nup route add -net 10.100.2.0 netmask 255.255.255.0 gw 10.100.2.0 tun0<br \/>\npost-down ssh -S \/var\/run\/ssh-myvpn-tunnel-control -O exit example.com<br \/>\n[\/source]<\/code><\/p>\n<p>Now you only have to restart  networking to enable the tunnel. Now your nodes will be in their own VPN.<\/p>\n<p><strong>Setting up document root replication (<code>rsync<\/code>)<\/strong><\/p>\n<p>Share \/var\/www via rsync. You need to install rsync and add following to \/etc\/rsyncd.conf if they are not already there.<\/p>\n<p><code>[source lang='plain']max connections = 2<br \/>\nlog file = \/var\/log\/rsync.log<br \/>\ntimeout = 300<\/p>\n<p>[www]<br \/>\ncomment = DOC Root<br \/>\npath = \/var\/www<br \/>\nread only = yes<br \/>\nlist = yes<br \/>\nuid = www-data<br \/>\ngid = www-data<br \/>\nauth users = replicator<br \/>\nsecrets file = \/etc\/rsyncd.secrets[\/source]<\/code><\/p>\n<p>Add following cron jobs to www-data crontab (crontab -e)<\/p>\n<p><code>[source lang='plain' options='gutter: false; toolbar: false;' ]<br \/>\n1\/10 *  *   *   *    test -r \/tmp\/rsync.docroot.lock || touch \/tmp\/rsync.docroot.lock && rsync -aP rsync:\/\/replicator@10.100.2.2\/www\/ \/var\/www\/ --password-file=\/etc\/rsync.secrets  --contimeout=30  > \/dev\/null 2>1 && rm \/tmp\/rsync.docroot.lock[\/source]<\/code><\/p>\n<p><code>[source lang='plain' options='gutter: false; toolbar: false;' ]<br \/>\n1\/10 *  *   *   *    test -r \/tmp\/rsync.docroot.lock || touch \/tmp\/rsync.docroot.lock && rsync -aP rsync:\/\/replicator@10.100.2.1\/www\/ \/var\/www\/ --password-file=\/etc\/rsync.secrets  --contimeout=30 > \/dev\/null 2>1 && rm \/tmp\/rsync.docroot.lock[\/source]<\/code><\/p>\n<p><strong>Setting up <code>session_mysql<\/code><\/strong><\/p>\n<p>Next let us setup <code>session_mysql<\/code> such that we can forget  about replicating PHP session \ud83d\ude42 .<\/p>\n<p>Install php5-dev and libmysql++-dev, download  <a href=\"http:\/\/websupport.sk\/~stanojr\/projects\/session_mysql\/\" target=\"_blank\"><code>session_mysql<\/code><\/a> and extract it, running following commands as root within the extracted location.<\/p>\n<p><code>[source lang='bash']export PHP_PREFIX='\/usr'<br \/>\n$PHP_PREFIX\/bin\/phpize<br \/>\n.\/configure --enable-session-mysql --with-php-config=$PHP_PREFIX\/bin\/php-config --with-mysql=$PHP_PREFIX<br \/>\nmake<br \/>\nmake install[\/source]<\/code><\/p>\n<p>Create the database to store the session data with the following SQL<\/p>\n<p><code>[source lang='sql']<br \/>\ncreate database phpsession;<br \/>\ngrant all privileges on phpsession.* to phpsession identified by 'phpsession'; -- CHANGE DEFAULT PASSWORD<br \/>\ncreate table phpsession(<br \/>\nsess_key char(64) not null,<br \/>\nsess_mtime int(10) unsigned not null,<br \/>\nsess_host char(64) not null,<br \/>\nsess_val mediumblob not null,<\/p>\n<p>index i_key(sess_key(6)),<br \/>\nindex i_mtime(sess_mtime),<br \/>\nindex i_host(sess_host)<br \/>\n);[\/source]<\/code><\/p>\n<p>Add the following to your <code>php.ini<\/code> (or <code>\/etc\/php5\/conf.d\/session_mysql.ini<\/code>)<\/p>\n<p><code>[source lang='plain']<br \/>\nsession.save_handler = 'mysql'<br \/>\nsession_mysql.db='host=localhost db=phpsession user=phpsession pass=phpsession'<br \/>\n[\/source]<\/code><\/p>\n<p>Do not forget to change the default password. Restart Apache or Lighttpd (or any other web server you are using).<\/p>\n<p><strong>MySQL asynchronous two way replication<\/strong><\/p>\n<p>I&#8217;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).<\/p>\n<p>Stop MySQL from listening only to local connections. Remember to review your user table (<code>mysql.user<\/code>) to make sure you don&#8217;t grant wild card access like <code>'user'@'%'<\/code>. Comment out bind-address in<code>\/etc\/mysql\/my.cnf<\/code> in all nodes. Then add following to node1<\/p>\n<p><code>[source lang='plain']server-id               = 1<br \/>\nreplicate-same-server-id = 0<br \/>\nauto-increment-increment = 2<br \/>\nauto-increment-offset   = 1<br \/>\nlog_bin                 = \/var\/log\/mysql\/mysql-bin.log<br \/>\nexpire_logs_days        = 10<br \/>\nmax_binlog_size         = 100M<\/p>\n<p>master-host = 10.100.2.2<br \/>\nmaster-user = slave_user_0<br \/>\nmaster-password = your$password<br \/>\nmaster-connect-retry = 60[\/source]<\/code><\/p>\n<p>and following to node2<\/p>\n<p><code>[source lang='plain']server-id               = 2<br \/>\nreplicate-same-server-id = 0<br \/>\nauto-increment-increment = 2<br \/>\nauto-increment-offset   = 2<br \/>\nlog_bin                 = \/var\/log\/mysql\/mysql-bin.log<br \/>\nexpire_logs_days        = 10<br \/>\nmax_binlog_size         = 100M<\/p>\n<p>master-host = 10.100.2.1<br \/>\nmaster-user = slave_user_1<br \/>\nmaster-password = your$password<br \/>\nmaster-connect-retry = 60[\/source]<\/code><\/p>\n<p>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 \ud83d\ude00 . 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.<\/p>\n<p><code>[source lang='sql']CREATE USER 'slave_user_1'@'10.100.2.1' IDENTIFIED BY 'your$password';<\/p>\n<p>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 ;<\/p>\n<p>CREATE USER 'slave_user_2'@'10.100.2.2' IDENTIFIED BY 'your$password';<\/p>\n<p>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]<\/code><\/p>\n<p>Now start MySQL and run following in <code>mysql<\/code> prompt on each of the nodes.<\/p>\n<p><code>[source lang='sql']reset master;<br \/>\nstop slave;<br \/>\nstart slave;[\/source]<\/code><\/p>\n<p><strong>Finally<\/strong><\/p>\n<p>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.<\/p>\n<div id=\"fb-like\" style=\"\"><iframe src=\"http:\/\/www.facebook.com\/plugins\/like.php?href=https:\/\/mohanjith.net\/blog\/2009\/04\/load-balanced-and-high-availability-cluster-for-your-web-site-under-usd-60-pm-part-2.html&amp;layout=standard&amp;show_faces=true&amp;width=300&amp;action=like&amp;font=&amp;colorscheme=light&amp;locale=en_US\" scrolling=\"no\" frameborder=\"0\" allowTransparency=\"true\" style=\"border:none; overflow:hidden; width:300px; height:30px\"><\/iframe><\/div>","protected":false},"excerpt":{"rendered":"<p>Update 2009-09-02: Now I&#8217;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 &#8230; <a title=\"Load balanced and High Availability cluster for your web site under USD 60 pm &#8211; Part 2\" class=\"read-more\" href=\"https:\/\/mohanjith.net\/blog\/2009\/04\/load-balanced-and-high-availability-cluster-for-your-web-site-under-usd-60-pm-part-2.html\" aria-label=\"More on Load balanced and High Availability cluster for your web site under USD 60 pm &#8211; Part 2\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[77,26],"tags":[170,178,157,176,389,159,161,174,168,184,396,166,180,182],"class_list":["post-273","post","type-post","status-publish","format-standard","hentry","category-lighttpd","category-linux","tag-authentication-keys","tag-config","tag-high-availability","tag-keygen","tag-lighttpd","tag-mysql","tag-php","tag-public-key","tag-root-logins","tag-rsa","tag-ssh","tag-ssh-tunnel","tag-tunnels","tag-vpn"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5lUHm-4p","jetpack_likes_enabled":false,"_links":{"self":[{"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/posts\/273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/comments?post=273"}],"version-history":[{"count":49,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/posts\/273\/revisions"}],"predecessor-version":[{"id":346,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/posts\/273\/revisions\/346"}],"wp:attachment":[{"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/media?parent=273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/categories?post=273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mohanjith.net\/blog\/wp-json\/wp\/v2\/tags?post=273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}