NginX: Load Balancing, Failover, and Geo Location (Part 3)

by admin on January 24, 2012

in Http Maid Note

Okay we continue to the third part of this post title, on the previous part we use NginX as load balancer and failover, now we use NginX with Geo IP based to determine the best backend for the visitors to put, here is the illustration.

For instance, we have two backend servers located in UK and DE, then we put the visitors from United Kingdom to the UK backend, visitors from Germany to DE backend, and the rest will be divided into those two backend servers, let’s deal with it.

I assume you have installed NginX in your frontend and two backend servers, you can check the previous post for NginX installation. This GeoIP based location needs GeoIP database for the frontend server to determine where to put the visitor, so first we download and extract Lite version of GeoIP database from Maxmind with geo2nginx.pl script from http://markmaunder.com.

wget http://serversreview.net/pkgs/txt/geo2nginx.pl
chmod 755 geo2nginx.pl
wget http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
unzip GeoIPCountryCSV.zip
./geo2nginx.pl < GeoIPCountryWhois.csv > geo.conf
mv geo.conf /etc/nginx/

GeoIP database has been added to NginX directory, now to the configuration, here is the example of main configuration

user www www;
worker_processes  1;

events {
	worker_connections  1024;
}

http {
	include	mime.types;
	default_type	application/octet-stream;
	sendfile	on;
	keepalive_timeout	65;
	gzip	on;

	geo $geo {
		default default;
		include geo.conf;
	}
    upstream default.backend {
    	server 178.22.70.2:8080 weight=2;
		server 206.253.165.98:8080;
    }
    upstream UK.backend {
    	server 178.22.70.2:8080;
    }
    upstream DE.backend {
    	server 206.253.165.98:8080;
    }

	server {
		listen 80;
		server_name serversreview.net;
		location / {
			proxy_pass http://$geo.backend;
		}
	}
}

Let’s just move to the http section, as you can see geo.conf is declared with $geo as variable for the upstream, so

e.g. 1.1.1.1 = UK ; 2.2.2.2 = CN

  • if visitor comes from 1.1.1.1, then $geo = UK, so the visitor will be sent to UK.backend upstream
  • if visitor comes from 2.2.2.2, then $geo = default, so the visitor will be sent to either the first or the second backend

Backend setting is as the same as usual, you can check the previous post for backend setting. Simple and easy right? Next thing you do is trial and error to meet your best configuration.

Take a look once again at the illustration, besides NginX GeoIP based, there is also two different location of databases, here i want to talk about MySQL (the most widely used database) replication. Nowadays, websites are not only using static files, they are using dynamic script with database to save the resource of space, and most of them are using MySQL Community as a free and opensource database. So it is also good to have MySQL live backup for the prevention of -for instance- MySQL failure or another MySQL problem that could happened, more interesting, right? Told you so :p

I will use two CentOS 5 32bit VPS for MySQL master and slave, first we install MySQL in both master and slave server.

yum install libaio libaio-devel
wget http://pkgs.serversreview.net/files/mysql-5.5.20-linux2.6-i686.tar.gz
tar -zxvf mysql-5.5.20-linux2.6-i686.tar.gz
mv mysql-5.5.20-linux2.6-i686 /usr/local/mysql
groupadd mysql
useradd -r -g mysql mysql
cd /usr/local/mysql
chown -R mysql .
chgrp -R mysql .
/usr/local/mysql/scripts/mysql_install_db --user=mysql –-ldata=/usr/local/mysql
chown -R root .
chown -R mysql data

choose one of MySQL configuration that meet your needs

/usr/local/mysql/support-files/my-huge.cnf
/usr/local/mysql/support-files/my-innodb-heavy-4G.cnf
/usr/local/mysql/support-files/my-large.cnf
/usr/local/mysql/support-files/my-medium.cnf
/usr/local/mysql/support-files/my-small.cnf

I will use my-small.cnf for example

mv /usr/local/mysql/support-files/my-small.cnf /etc/my.cnf
cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql
chmod +x /etc/init.d/mysql
echo "/etc/init.d/mysql start" >> /etc/rc.d/rc.local
/etc/init.d/mysql start

Okay, MySQL has been installed on the first and second VPS, now we will configure the first and the second server to be master and slave.

MASTER
Login to your mysql root and use the following command to add and grant user for slave.

/usr/local/mysql/bin/mysql -uroot -pyourmysqlrootpassword
mysql> GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'%' IDENTIFIED BY 'slave_password';
mysql> FLUSH PRIVILEGES;
mysql> exit

Where “slave_user” is your preferred user for slave user and “slave_password” is your preferred password for slave user password.

And then edit your master’s my.cnf, make sure “server-id” value in my.cnf is set to “1”

nano /etc/my.cnf

[mysqld]
log-bin=mysql-bin
server-id=1

And then restart MySQL

/etc/init.d/mysql restart

SLAVE
Edit your slave’s my.cnf “server-id” value to “2”

nano /etc/my.cnf

server-id=2

Restart MySQL

/etc/init.d/mysql restart

MASTER
We will backup target database for replication in master, but before that we need to close all open tables and lock it with command

/usr/local/mysql/bin/mysql -uroot -p	
mysql> FLUSH TABLES WITH READ LOCK;
mysql> exit

and then backup the target database

/usr/local/mysql/bin/mysqldump selected_database -uroot -pmysqlrootpassword > database.sql;
gzip database.sql

After that, move that backup database to your slave server, you can use curl, scp, or wget from slave server.

SLAVE
Create new MySQL database

/usr/local/mysql/bin/mysql -uroot -p
mysql> CREATE DATABASE `my_database`;
mysql> FLUSH PRIVILEGES;
mysql> exit

extract MySQL database you have downloaded from master server, and then import to your newly created database in

gunzip database.sql.gz
/usr/local/mysql/bin/mysql -uroot -pmysqlrootpassword my_database  < database.sql

MASTER
Login to your MySQL root and then type command show master status

/usr/local/mysql/bin/mysql -uroot -p
mysql> SHOW MASTER STATUS;
	+------------------+----------+--------------+------------------+
	| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
	+------------------+----------+--------------+------------------+
	| mysql-bin.000002 |      107 | bijionta     |                  |
	+------------------+----------+--------------+------------------+

Keep that on your screen, and then move to your slave server.

SLAVE

/usr/local/mysql/bin/mysql -uroot -p
mysql> slave stop;
mysql> CHANGE MASTER TO MASTER_HOST='master_ip_address',
       MASTER_USER='replication_user_name',
       MASTER_PASSWORD='replication_user_password',
       MASTER_LOG_FILE='mysql-bin.000002',
       MASTER_LOG_POS=107;
mysql> slave start;

you can see more directives for Change Master To at: http://dev.mysql.com/doc/refman/5.0/en/change-master-to.html After that, back to master server. MASTER Unlock all tables that you close and lock when you backup your target database

/usr/local/mysql/bin/mysql -uroot -p
mysql> unlock tables;

That’s it for MySQL replication, anyway if you are using MyISAM engine, then it is recommended for you to add the following settings to your my.cnf

innodb_flush_log_at_trx_commit=1
sync_binlog=1

{ 2 comments }

Previous post:

Next post: