EC2上でMySQL Multi-masterフェイルオーバー

EC2上では、仮想IPアドレスなどのIPレベルの機能が制限されているため、仮想IPアドレスを使用した冗長化は基本的には使用できません。が、DNSを使用することで、VIPほどの精度は高くないもののMySQL Multi-master構成を構築することができました。

今回は、MySQL Multi-masterの切り替え用の支援ツールとして、Multi-Master Replication Manager for MySQLを使用します。このツールでは、MySQLの死活監視と仮想IPアドレスの切り替えを行ってくれます。

もちろん、EC2上では仮想IPアドレスは使えないので、そのままではうまく動作しません。ここで、このツールに含まれるns_agentを使用することで仮想IPアドレスではなく、DNSによる切り替えができるようになり、EC2上でMulti-masterを構築することができます。

今回は、このMulti-Master Replication Manager for MySQLとns_agentを使って、DNSによるMulti-masterのフェイルオーバーを実現してみます。

概要について、AWS UGのLTで使ったスライドを貼っておきます。

準備編

環境設定

Amazon EC2 API Toolsを使えるようにしましょう。ドキュメントを参照するか、ググってください。

次に、インスタンスを2台起動します。個人的な趣味で、RightScaleのCentOS 5.2 32bitのイメージを使用しています。セキュリティグループ(mmm_test)は、内部通信は可能にしておきます。

ec2-run-instances ami-ab2071ee -g mmm_test -k mmm_test
ec2-run-instances ami-ab2071ee -g mmm_test -k mmm_test

立ち上がったインスタンスを、host1とhost2とします。

それぞれのホストで、必要なツールをざくざくインストールします。

rpm -Uhv http://apt.sw.be/redhat/el5/en/i386/rpmforge/RPMS/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
yum -y install perl-Proc-Daemon perl-Algorithm-Diff perl-Log-Log4perl perl-MailTools perl-Log-Dispatch perl-Class-Singleton
yum -y install mysql-server mysql
yum -y install php libnet
MySQLの設定

MySQLのMulti-master replicationを構築します。Multi-master replicationの構築方法は、Googleで検索するか、実践ハイパフォーマンスMySQLの第8章を参照してください。

次にMulti-master replication manager用の権限を設定します。パスワードは適当の変更してください。

GRANT REPLICATION CLIENT ON *.* TO 'mmm_monitor'@'%' IDENTIFIED BY 'monitor_password';
GRANT SUPER, REPLICATION CLIENT, PROCESS ON *.* TO 'mmm_agent'@'%'   IDENTIFIED BY 'agent_password';
GRANT SUPER, REPLICATION CLIENT, PROCESS,REPLICATION SLAVE ON *.* TO 'replication'@'%' IDENTIFIED BY 'replication_password';
bindの設定

適切なサーバ(例えば、host1)にbindの設定を行います。参考にほぼ最小構成のbindの設定を書いておきます。ここでは、db.testdomainという名前を使用しています。DNSでフェイルオーバーしますのでTTLを短め(ここでは10秒)に設定することが肝です。

  • /etc/named.conf
options
{
        directory "/var/named"; // the default
        dump-file               "data/cache_dump.db";
        statistics-file         "data/named_stats.txt";
        memstatistics-file      "data/named_mem_stats.txt";

        recursion no;

};

zone "testdomain" IN {
    type master;
    file "zones/mmm.zone";
};
  • /var/named/zone/mmm.zone
$ORIGIN testdomain.
$TTL 10 ; 10 seconds
@       IN SOA  testdomain. postmaster.testdomain. (
        1002044230 ; serial
        3600       ; refresh (1 hour)
        1200       ; retry (20 min.)
        1209600    ; expire (2 weeks)
        900        ; minimum (15 min.)
        )
@               IN  NS          ns1.testdomain.
ns1             IN  A           10.xx.yy.zz
db IN A 10.xx.yy.zz

また各ホストのresolv.confにhost1のIPアドレスを追加しておきます。一応、timeoutも短めに設定しておきましょう。

  • /etc/resolve-conf
search us-west-1.compute.internal
timeout 3
nameserver 10.xx.yy.zz
nameserver 172.16.0.23

DNSによるMySQL Multi-master フェイルオーバー

Multi-Master Replication Manager for MySQL

Multi-Master Replication Manager for MySQLをチェックアウトして、初期設定を行います。

svn checkout http://mysql-master-master.googlecode.com/svn/trunk/ mysql-master-master-read-only
ln -s mysql-master-master-readonly /usr/local/mmm
ln -s /usr/local/mmm/scripts/init.d/mmm_mon /etc/init.d
ln -s /usr/local/mmm/scripts/init.d/mmm_agent /etc/init.d

ns_agentを使用するために、ifconfig.pmを書換えておきます。

mv /usr/local/mmm/lib/ifconfig.pm /usr/local/mmm/lib/ifconfig.original.pm
ln -s /usr/local/mmm/contrib/ns_agent/ifconfig.pm /usr/local/mmm/lib
MySQL Multi-master Manager + ns_agent

MySQL Multi-master Managerとns_agentによる構成は以下の3種類のデーモンプロセスが動作します。

  • 各データベースに同居させるmmmd_agent
    • データベースへの監視、書込み許可/禁止などの制御を行う
  • 監視・切り替え制御を行うmmmd_mon
    • 各サーバの状態を管理し、mmmd_agentを通じて、各サーバの監視・制御を行う
  • DNSサーバに同居させるns_agent
    • zoneファイルを書換えて、DNSの制御を行う
設定ファイル

ずらずらと設定ファイルを並べておきます。

  • mmm_common.conf
# Cluster interface
cluster_interface eth0

# Debug mode
debug yes

# Paths
bin_path /usr/local/mmm/bin

# Logging setup
log mydebug
    file /usr/local/mmm/var/mmm-debug.log
    level debug

log mytraps
    file /usr/local/mmm/var/mmm-traps.log
    level trap
    email root@localhost

# Email notification settings
email notify
    from_address stanaka@hatena.ne.jp
    from_name MMM Control

# Define roles
active_master_role writer

# MMMD command socket tcp-port
agent_port 9989
monitor_ip 127.0.0.1

# Cluster hosts addresses and access params
host db1
    ip 10.xx.yy.zz
    port 3306
    user replication
    password replication_password
    mode master
    peer db2

host db2
    ip 10.xx.yy.zzz
    port 3306
    user replication
    password replication_password
    mode master
    peer db1

# Mysql Writer role
role writer
    mode exclusive
    servers db1, db2
    ip db.testdomain

# Replication credentials used by slaves to connect to the master
replication_user replication
replication_password replication_password

# Checks parameters

# Ping checker
check ping
    check_period 1
    trap_period 5
    timeout 2

# Mysql checker
# (restarts after 10000 checks to prevent memory leaks)
check mysql
    check_period 1
    trap_period  2
    timeout 2
    restart_after 10000

# Mysql replication backlog checker
# (restarts after 10000 checks to prevent memory leaks)
check rep_backlog
    check_period 5
    trap_period 10
    max_backlog 60
    timeout 2
    restart_after 10000

# Mysql replication threads checker
# (restarts after 10000 checks to prevent memory leaks)
check rep_threads
    check_period 1
    trap_period 5
    timeout 2
    restart_after 10000
  • mmm_agent.conf
include mmm_common.conf

# Paths
pid_path /usr/local/mmm/var/mmmd_agent.pid

# MMMD command socket tcp-port and ip
bind_port 9989

# Define current server id
this db1

# Cluster hosts addresses and access params
host db1
    user mmm_agent
    password agent_password

host db2
    user mmm_agent
    password agent_password

nameserver ns1
    ip 10.160.47.21
    port 9994
  • mmm_mon.conf
include mmm_common.conf

# Paths
pid_path /usr/local/mmm/var/mmmd.pid
status_path /usr/local/mmm/var/mmmd.status

# MMMD command socket tcp-port
bind_port 9988

# Choose the default failover method [manual|wait|auto]
failover_method wait

# How many seconds to wait for both masters to become ONLINE
# before switching from WAIT to AUTO failover method, 0 = wait indefinitely
wait_for_other_master 60

# How many seconds to wait before switching node status from AWAITING_RECOVERY to ONLINE
# 0 = disabled
auto_set_online 10

monitor_user mmm_monitor
monitor_password monitor_password
  • mmm_ns_agent.conf
[nameserver_agent]
port = 9994
zoneFile = /var/named/zones/mmm.zone
起動

あとは各デーモンプロセスを起動するのみです。まず、各データベースサーバで、mmm_agentを起動します。

/etc/init.d/mmm_agent start
Starting MMM Agent daemon: MySQL Multi-Master Replication Manager
Version: 1.2.6
[2010-02-22 22:15:59]: Scanning network interfaces for the existing roles...
[2010-02-22 22:15:59]: Core: Execute_bin('/usr/local/mmm/bin/mysql_deny_write 'mmm_agent.conf'')
[2010-02-22 22:15:59]: Listener: Waiting for connection...
[2010-02-22 22:16:00]: Listener: Connect!
[2010-02-22 22:16:00]: SET_STATUS 0, ONLINE, , db1
...

次に、監視サーバでmmm_monを起動します。

# /etc/init.d/mmm_mon start
Daemon bin: '/usr/local/mmm/sbin/mmmd_mon'
Daemon pid: '/usr/local/mmm/var/mmmd.pid'
Starting MMM Monitor daemon: MySQL Multi-Master Replication Manager
Version: 1.2.6
Reading config file: 'mmm_mon.conf'
[2010-02-22 22:16:24]: Spawning checker 'rep_backlog'...
[2010-02-22 22:16:24]: Spawning checker 'mysql'...
[2010-02-22 22:16:24]: Spawning checker 'ping'...
[2010-02-22 22:16:24]: Spawning checker 'rep_threads'...
[2010-02-22 22:16:25]: Trying initial check 'rep_backlog' on host 'db2'...
[2010-02-22 22:16:25]: rep_backlog('db2') = 'OK'
[2010-02-22 22:16:25]: Trying initial check 'mysql' on host 'db2'...
[2010-02-22 22:16:25]: mysql('db2') = 'OK'
...

最後にDNSサーバでns_agentを起動します。

# php /usr/local/mmm/contrib/ns_agent/ns_agent.php
Daemon started, listening on 9994
動作確認

ここでアクティブ側のdb1(host1)のmysqldを落してみます。

/etc/init.d/mysqld stop

そうするとmmm_monの監視が動いて、ns_agentに情報が伝わり、zoneファイルが書換えられます。

Command Received: ADDIP:db.testdomain:10.160.47.21
Executing: sed -i.last '/db.*/d' /var/named/zones/mmm.zone
Executing: echo 'db IN A 10.xx.yy.zzz' >> /var/named/zones/mmm.zone
Updating /var/named/zones/mmm.zone with new serial:1002044230

その後、zoneファイルが再読み込みされ、DNS情報が更新されフェイルオーバーが実現されます。

フェイルオーバーの様子を、監視していると、以下のように数秒で切り替わったことが確認できます。

# while /bin/true; do date; mysqladmin -ummm_agent -pagent_password -hdb.testdomain variables | grep server_id; sleep 2; done
....
Mon Feb 22 22:20:53 EST 2010
| server_id                       | 1                                                          |
Mon Feb 22 22:20:55 EST 2010
| server_id                       | 1                                                          |
Mon Feb 22 22:20:57 EST 2010
mysqladmin: connect to server at 'db.testdomain' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 111'
Mon Feb 22 22:20:59 EST 2010
mysqladmin: connect to server at 'db.testdomain' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 111'
Mon Feb 22 22:21:01 EST 2010
| server_id                       | 2                                                          |
Mon Feb 22 22:21:03 EST 2010
| server_id                       | 2                                                          |
...

おわり

MySQLのMulti-masterのフェイルオーバーに数秒かかってしまいますが、用途によっては、なんとか我慢できるところもあると思います。EC2のホストも時々は落ちることがある、という話はよく聞きますので、少なくともシングルマスターの場合よりは安心できると思います。

もし、よりよい方法があったら、是非教えてください!!