Bluhalo IT


Simple MySQL replication cluster with load balancer on the slaves

Posted in linux, mysql by Simon Green on May 11, 2009
Tags: , , , , ,

Scenario

Background

This post is a follow on from my previous post on how to setup a load balanced high availability Apache cluster and uses the same network setup and heartbeat setup. For the purposes of testing and to save on resource I am using the web servers from the previous guide as the MySQL servers in this guide. Obviously in a real world setup this would be doing it wrong ;)

End Goal

You will be able to connect to 1 IP address and port with MySQL client and be routed to 1 of many MySQL servers. The machine doing the routing will be in a high availability setup with another machine.

Server Roles

bhlabs1/6 will load balance mysql queries through mysql-proxy
bhlabs2 will be  the master replication server
bhlabs3-5 will be mysql slaves

I’m separating this guide into 3 sections:

  • MySQL Replication Cluster
  • mysql-proxy Setup
  • Usage with heartbeat

MySQL Replication Cluster

On each of the servers :

# apt-get update
# apt-get install mysql-server

The slave servers will need special access to stream the binary logs from the master server, so that needs to be setup using the following GRANT command as root on the master server:

mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' IDENTIFIED BY 'ImASlave';

Next un-comment server id and binary log entries in my.cnf (add them if they weren’t there in the first place):

server-id   = 1
log_bin     = /var/log/mysql/mysql-bin.log

You must also set the server-id on all slaves, starting from 2 and incrementing as you go. You do not need the log_bin entry on the slave servers.

Your slave servers must begin replication at exactly the same state as the master server. From the master server, export all databases with lock on all tables:

# mysqldump -p --all-databases --lock-all-tables > initialDbDump.db

And then import on to all slave servers:

# mysql -p -hbhlabs3 < ./initialDbDump.db
# mysql -p -hbhlabs4 < ./initialDbDump.db
# mysql -p -hbhlabs5 < ./initialDbDump.db

Get log position off bhlabs2:

mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000002 |      516 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

Set configuration and start slave on bhlabs3-5:

mysql> CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=516, MASTER_HOST='bhlabs2', MASTER_USER='repl', MASTER_PASSWORD='ImASlave';
mysql> START SLAVE;

You can check connectivity from bhlabs2:

mysql> show full processlist;
+----+------+---------------------+------+-------------+------+----------------------------------------------------------------+-----------------------+
| Id | User | Host                | db   | Command     | Time | State                                                          | Info                  |
+----+------+---------------------+------+-------------+------+----------------------------------------------------------------+-----------------------+
| 16 | repl | 192.168.11.36:40812 | NULL | Binlog Dump |  248 | Has sent all binlog to slave; waiting for binlog to be updated | NULL                  |
| 17 | repl | 192.168.11.37:48080 | NULL | Binlog Dump |  206 | Has sent all binlog to slave; waiting for binlog to be updated | NULL                  |
| 18 | repl | 192.168.11.38:54400 | NULL | Binlog Dump |  202 | Has sent all binlog to slave; waiting for binlog to be updated | NULL                  |
| 22 | root | localhost           | NULL | Query       |    0 | NULL                                                           | show full processlist |
+----+------+---------------------+------+-------------+------+----------------------------------------------------------------+-----------------------+
4 rows in set (0.00 sec)

And then create a test database on bhlabs2 and it should appear on the other 3 servers.

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

Drop the test database and it should disappear. If this works you’re set to go.

mysql> drop database test;
Query OK, 0 rows affected (0.07 sec)

Remember from this point all commands that create or change anything in MySQL must now be run on the master server. With this in mind, create a read only user, you don’t want your websites accidentally writing to the slaves, that will break replication!

mysql> grant select on *.* to 'selectuser'@'%' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> grant select on *.* to 'selectuser'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

And then obivously, a write user:

mysql> grant select,insert,update,delete on *.* to 'writeuser'@'%' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> grant select,insert,update,delete on *.* to 'writeuser'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.01 sec)

Proxy Servers

Repeat this whole section on both proxy servers (bhlabs1/6)
Install the proxy:

# apt-get updatep
# apt-get install mysql-proxy

Initial startup:

# mysql-proxy --admin-address=:1337 --proxy-backend-addresses=192.168.11.36:3306 --proxy-backend-addresses=192.168.11.37:3306 --proxy-backend-addresses=192.168.11.38:3306 &

Check the proxy is running via netstat to see if the ports have been bound:

# netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 *:4040                  *:*                     LISTEN
tcp        0      0 *:1337                  *:*                     LISTEN

Port 4040 is the proxy inbound port
Port 1337 is the admin database

Connect to admin database:

# mysql -h 127.0.0.1 -P 1337 -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.1.20-agent MySQL Enterprise Agent

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

And then check your proxy_config tables at it should list your backend servers:

mysql> select * from proxy_config;
+----------------------------+--------------------+
| option                     | value              |
+----------------------------+--------------------+
| admin.address              | :1337              |
| proxy.address              | :4040              |
| proxy.lua_script           | NULL               |
| proxy.backend_addresses[0] | 192.168.11.36:3306 |
| proxy.backend_addresses[1] | 192.168.11.37:3306 |
| proxy.backend_addresses[2] | 192.168.11.38:3306 |
| proxy.fix_bug_25371        | 0                  |
| proxy.profiling            | 1                  |
+----------------------------+--------------------+
8 rows in set (0.00 sec)

Quit out of mysql and then reconnect on the proxy ingress port:

# mysql -h 127.0.0.1 -P 4040 -u selectuser -p

And you should then be able to spot a Sleep connection on one of your slave servers through:

mysql> show full processlist;
+----+------------+---------------------+------+---------+------+-------+-----------------------+
| Id | User       | Host                | db   | Command | Time | State | Info                  |
+----+------------+---------------------+------+---------+------+-------+-----------------------+
| 20 | selectuser | 192.168.11.30:58522 | NULL | Sleep   |  161 |       | NULL                  |
| 21 | selectuser | localhost           | NULL | Query   |    0 | NULL  | show full processlist |
+----+------------+---------------------+------+---------+------+-------+-----------------------+
2 rows in set (0.00 sec)

Success!

You can now test failover by shutting down one of your slaves, running queries, even inserts (on the master!), and then starting the slave again. The slave will know the last log position it was at on the master logs and be able to catch back up once you restart it. The proxy will not route queries to a server thats down.

High Availability (Optional)

This will allow your proxy servers to failover between each other on the same address using heartbeat. For how to setup heartbeat initially see the guide here, we’re going to pick up at the end of that guide and just join our mysql-proxy to that shared IP address.

All you need to do to get it to listen on the shared address is add 1 more switch to the startup command, proxy-address:

# mysql-proxy --proxy-address=192.168.11.40:4040 --admin-address=:1337 --proxy-backend-addresses=192.168.11.36:3306 --proxy-backend-addresses=192.168.11.37:3306 --proxy-backend-addresses=192.168.11.38:3306 &

You can then check it’s listening on the specified IP via netstat again. Remember if heartbeat hasn’t failed over to this machine then you won’t be able to reach this address!

# netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 192.168.11.40:4040      *:*                     LISTEN

Furthur Reading

Automatically routing writes and reads

It is possible to use 1 user for both your reads and your writes through 1 IP/Port by using mysql-proxy’s very extensive LUA support to route queries to different servers based on queries. See here and here for examples.

Other funky LUA hacks

There’s plenty you can do with LUA and mysql-proxy. O’Reilly published a good guide with examples such as rewriting queries on the fly to correct typos, executing remote shell commands through MySQL, etc.  Worth a read if you want to do anything automated with it.

10 Responses to 'Simple MySQL replication cluster with load balancer on the slaves'

Subscribe to comments with RSS or TrackBack to 'Simple MySQL replication cluster with load balancer on the slaves'.

  1. Ferinannnd said,

    Да, есть над чем задуматься. Спасибо!

  2. udens said,

    Can you show how to add servers for storage when i have setup apache servers and mysql servers but i need lets say a storage servers for pictures, video, files and stuff. And like if one is full then redirect to other or something. How to handle this kind on things. Or is the storage data replicating too? And it would be cool if i update my php app on one master server then it replecates on all web servers so i dont need to copy php file a changed on all servers :)

  3. Emeau said,

    Hi,

    I’m setting up a mysql/apache cluster behind a loadbalancer to host some WordPress (MU) “high” traffic sites (7000 unique visitor a day), my problem is that during my test i encountered some mysql replication error, a duplicate entry to be clear.

    This replication error come only when more than one apache is behin the loadbalancer, else it works perfectly with a hyperdb r/w splitting.

    Clearly, i must be missing a mysql cluster tip/concept.

    I would like your advise concerning how you set up your infrastructure.

    Thanks you !

    • Simon Green said,

      Hi,

      Can you clarify what you mean by a duplicate entry? Did the replication go out of sync and stop working because of a write to the slave?

  4. Emeau said,

    Hi,

    First, thanks you for you reply.

    In fact, of what i see it seems that’s when replicating an update query (from master to slave), mysql find that an entry with the same ID is already on the slave.

    This is the weird point because before setting up the mysql replication i dump database from master and inject it on the slave, there must be no differences and i’m pretty sure no index-different entries.

    Again, thanks for your time.

    • Simon Green said,

      Very odd. You definitely stopped any applications from talking to the master between doing the dump and getting your log position for starting replication? Can you post your ’show master status’ and ’show slave status’ from your 2 servers?

  5. Emeau said,

    Hello !

    Well, it’s a one way replication with an hyperdb setup.

    Both wordpress are configured to write on the master and read from the slave,
    the replication is working great in this way (before we were in a master/master replication where i encountered the same duplicate entry error).

    Actually my replication seems to be stoped by a ddos attack… (one server come down, the slave one) so i can’t give you the master/slave status rigth now.

    The weird thing is that the duplicate entry occurs only when both web servers are behind the loadbalancer. When just one of them is there, there’s no replication problem.

    Thanks you.


  6. [...] Simple MySQL replication cluster with load balancer on the slaves « Bluhalo IT. Cancel [...]

  7. mysteron said,

    Hi.

    Great tutorial, but I cannot connect on port 1337. All I get is an error message saying that I’ve got the wrong password.

    Is there a lua script that has to be run before the proxy can work?

    The servers I’m running are Ubuntu 9.10 and the mysql-proxy that shippes with karmic koala.

    Regards,
    /mysteron

  8. John said,

    This setup sounds good but it gives rise to a question, or two.

    If you’ve got say 3 web servers that act as a cluster, would it be reasonable to make each of the web servers also a mysql proxy. Would there be any issue with having multiple proxies talking to the masters/slaves?

    Assuming you wanted to go for a keep everything local approach and had 1 sql master and 3 servers acting as web, proxy and mysql slaves is there a way of forcing the proxy to make all reads from the local slave server? The end result would be a cluster of web servers where every sql read is a local one and every write/update goes correctly to the master.


Leave a Reply