2009-01-21

HeartBeat+DRBD+MySQL on Debian Etch 建置/測試筆記

HeartBeat(2.1.3-6)+DRBD(2:8.3.0-1)+MySQL on Debian Etch 建置/測試筆記


一般在處裡網路服務的系統架構時,如何做到高可用性及即時備援機制一直都是一個重要課題,一般常見的架構是USER-WEB-DB,目前面向USER的WEB SERVER要做到LoadBalance/Failover已經有很多可運用且方便的手段,但後端的資料庫如果要做到Failover就稍微麻煩了些,以下就對MySQL資料庫搭配HeartBeat/DRBD的即時備援機制來作一個簡單的建置/測試紀錄.
其中相關設定是經過實測所得,若有不夠完備須修正調整之處還請各位不吝提供意見.

DRBD (Distributed Replicated Block Device) http://www.drbd.org/
是透過網路即時同步的Block Device,可視同為透過網路進行即時同步的Raid-1,架構示意圖如下(來源為DRBD網站)


DRBD搭配HeartBeat可建構高可用性(HA-high availability)的服務
如將MySQL的DATA FILE放到DRBD上,配合HeartBeat控管可作到MySQL高可用性的即時同步與Failover備援切換.
(其實還是會有down time,不過如果跟MySQL replication的Master/Slave模式在一般情況下需人工介入切換相比,可算是方便省事多了)

架構設定說明:



建置/測試環境:
OS: Debian Etch 4.0
/dev/sdb1 4GB for drbd use

NODE-A (Primary)
eth0 192.168.1.11 Real_IP(R_IP)
eth0:0 192.168.1.10 Service_IP(S_IP) 由HeartBeat控管作為主要提供服務的IP
eth1 192.168.100.11 Private_IP (P_IP) 對接供DRBD傳輸用

NODE-B (Secondary)
eth0 192.168.1.12 Real_IP(R_IP)
eth0:0 192.168.1.10 Service_IP(S_IP) 由HeartBeat控管作為主要提供服務的IP
eth1 192.168.100.12 Private_IP (P_IP) 對接供DRBD傳輸用

DRBD編譯/安裝

因為Debian etch內的drbd7太舊了(0.7.21-4), etch-backports上的是drbd8_8.0.14-2,乾脆直接從sid那抓最新(drbd8_8.3.0-1)的source package http://packages.debian.org/source/sid/drbd8回來重編kernel-module.
(懶得自己重編也可以直接到http://www.linbit.com/support/drbd-8.3.0/debian-etch/ 去找出搭配當前kernel版本的deb檔回來直接裝)

建立drbd8 source package重包的工作目錄.
mkdir /usr/src/drbd8_8.3.0-1
cd /usr/src/drbd8_8.3.0-1

把drbd8_8.3.0-1 的source package抓回來
wget http://ftp.de.debian.org/debian/pool/main/d/drbd8/drbd8_8.3.0-1.dsc
wget http://ftp.de.debian.org/debian/pool/main/d/drbd8/drbd8_8.3.0.orig.tar.gz
wget http://ftp.de.debian.org/debian/pool/main/d/drbd8/drbd8_8.3.0-1.diff.gz
安裝重包drbd8所需必要套件
apt-get install debhelper dh-make dpkg-dev debconf-utils gcc sp bison flex dpatch bzip2 libc6-dev docbook-utils module-assistant linux-headers-2.6-686
解開 source package
dpkg-source -x drbd8_8.3.0-1.dsc
進入解開目錄
cd /usr/src/drbd8_8.3.0-1/drbd8-8.3.0/
重新編譯包
dpkg-buildpackage
包完會產生 drbd8-source_8.3.0-1_all.deb & drbd8-utils_8.3.0-1_i386.deb
把這兩個deb裝起來
dpkg -i /usr/src/drbd8_8.3.0-1/drbd8-utils_8.3.0-1_i386.deb /usr/src/drbd8_8.3.0-1/drbd8-source_8.3.0-1_all.deb
產生drbd8 kernel module
module-assistant auto-build drbd8
可以簡化為
m-a a-b drbd8

依照目前kernel版本重編好的drbd8 kernel module 位置 /usr/src/drbd8-2.6.18-6-686_8.3.0-1+2.6.18.dfsg.1-23etch1_i386.deb
把剛編好的drbd8 module裝上
dpkg -i /usr/src/drbd8-2.6.18-6-686_8.3.0-1+2.6.18.dfsg.1-23etch1_i386.deb
(可以在其中一台作重包的動作,做完後再把 drbd8-utils_8.3.0-1_i386.deb & drbd8-2.6.18-6-686_8.3.0-1+2.6.18.dfsg.1-23etch1_i386.deb 丟到另一台安裝即可)

HeartBeat 編譯/安裝

Debian etch內的heartbeat-2 (2.0.7-2) 沒有支援dopd (DRBD outdate-peer daemon),所以也去http://packages.debian.org/source/sid/heartbeat 抓新版的2.1.4-3source package回來重包

建立heartbeat-2 source package重包的工作目錄.
mkdir /usr/src/heartbeat-2.1.3-6
cd /usr/src/heartbeat-2.1.3-6

把heartbeat_2.1.3-6 的source package抓回來
wget http://ftp.de.debian.org/debian/pool/main/h/heartbeat/heartbeat_2.1.3-6.dsc
wget http://ftp.de.debian.org/debian/pool/main/h/heartbeat/heartbeat_2.1.3.orig.tar.gz
wget http://ftp.de.debian.org/debian/pool/main/h/heartbeat/heartbeat_2.1.3-6.diff.gz

安裝重包heartbeat_2所需必要套件
apt-get install libsnmp-dev libglib2.0-dev psmisc libnet1-dev iproute libtool libcurl3-openssl-dev libxml2-dev uuid-dev lynx libbz2-dev zlib1g-dev uuid-dev libsensors-dev libltdl3-dev swig libgnutls-dev python-dev libpam0g-dev libncurses5-dev psmisc libopenhpi-dev python-central gawk libxml2-utils
解開 source package
dpkg-source -x heartbeat_2.1.3-6.dsc
進入解開目錄
cd /usr/src/heartbeat-2.1.3-6/heartbeat-2.1.3
重新編譯打包
dpkg-buildpackage
(視需求可以去改 heartbeat-2.1.3/debian/control ,像我只需要 heartbeat就只留下Package: heartbeat那段,可以省點時間)
包完後只要裝heartbeat_2.1.3-6_i386.deb即可
dpkg -i /usr/src/heartbeat-2.1.3-6/heartbeat_2.1.3-6_i386.deb
(可以在其中一台作重包的動作,做完後再把 heartbeat_2.1.3-6_i386.deb 丟到另一台安裝即可)

MySQL Server安裝

如果沒有特殊需求就直接用 apt-get install mysql-server 安裝
裝完後把開機啟動的MySQL 相關服務取消,以便之後交由HeartBeat控管 (可以裝sysv-rc-conf來處裡比較省事)
mv /etc/rc2.d/S17mysql-ndb-mgm /etc/rc2.d/K23mysql-ndb-mgm
mv /etc/rc2.d/S18mysql-ndb /etc/rc2.d/K22mysql-ndb
mv /etc/rc2.d/S19mysql /etc/rc2.d/K21mysql

DRBD設定

編輯NODE-A & NODE-B上的
/etc/drbd.conf

common {
syncer {
rate 100M;
}
}
resource r0 {
protocol C;
handlers {
pri-on-incon-degr "echo o > /proc/sysrq-trigger ; halt -f";
pri-lost-after-sb "echo o > /proc/sysrq-trigger ; halt -f";
local-io-error "echo o > /proc/sysrq-trigger ; halt -f";
outdate-peer "/usr/lib/heartbeat/drbd-peer-outdater -t 6";
pri-lost "echo pri-lost. Have a look at the log files. | mail -s 'DRBD Alert' root";
split-brain "/usr/lib/drbd/notify-split-brain.sh root";
out-of-sync "/usr/lib/drbd/notify-out-of-sync.sh root";
}
syncer {
verify-alg md5;
}
disk {
on-io-error detach;
fencing resource-only;
}
startup {
wfc-timeout 120;
degr-wfc-timeout 120;
}
on NODE-A {
device /dev/drbd0;
disk /dev/sdb1;
address 192.168.100.11:7788;
meta-disk internal;
}
on NODE-B {
device /dev/drbd0;
disk /dev/sdb1;
address 192.168.100.12:7788;
meta-disk internal;
}
}
因為裡面用到了heartbeat的dopd(DRBD outdate-peer daemon),
所以需要針對幾個drbd管理程式作權限調整
chgrp haclient /sbin/drbdsetup
chmod o-x /sbin/drbdsetup
chmod u+s /sbin/drbdsetup
chgrp haclient /sbin/drbdmeta
chmod o-x /sbin/drbdmeta
chmod u+s /sbin/drbdmeta

初始化 resource r0
分別在NODE-A & NODE-B執行
drbdadm create-md r0


啟動DRBD service
分別在NODE-A & NODE-B執行
/etc/init.d/drbd start


檢視DRBD 狀態



=====由/proc/drbd 看到的狀態,因為尚未初始同步所以是Inconsistent/Inconsistent=====

初始化同步r0,將NODE-A設為primary
NODE-A上以它的資料為基準開始同步
drbdadm -- --overwrite-data-of-peer primary r0
初始同步進行時 NODE-A的狀態


初始同步過程中 NODE-B的 /proc/drbd 資訊


初始同步過程中 NODE-B的eth1資料傳輸統計


NODE-A的syslog中可以看到初始同步完之後的資訊

可以看到4G的資料初始化同步花了97秒,平均同步速率為 43056 K/sec

MySQL設定

分別在NODE-A & NODE-B執行 mkdir /DB 建立 /DB 作為/dev/drbd0的掛載點
此時在Primary NODE-A 上先對 /dev/drbd0 建立filesystem(XFS),並掛載於/DB
NODE-A:
mkfs.xfs /dev/drbd0

mount /dev/drbd0 /DB



NODE-A:
先將MySQL停掉(如果有在執行的話),再把MySQL的資料由 /var/lib/mysql 搬移到 /DB/mysql 上
/etc/init.d/mysql stop
mv /var/lib/mysql /DB
修改 /etc/mysql/my.cnf 中的 datadir= /DB/mysql


Debian 的MySQL-Server 有個比較特別的地方是安裝時會建立一個debian-sys-maint的DB Account並產生隨機密碼以供MySQL啟動檢查/維護用.相關資訊及密碼會記錄於 /etc/mysql/debian.cnf 內,因為配合DRBD有更改預設的datadir,
所以在 /etc/mysql/debian.cnf 內的[mysql_upgrade]設定區段中也必須加上 datadir= /DB/mysql ,

否則在啟動MySQL時在syslog中會出現下面的錯誤
/etc/mysql/debian-start[_PID_]: Can't find data directory. Please restart with --datadir=path-to-writable-data-dir

因為NODE-A & NODE-B的MySQL資料檔案是放在DRBD上面,也就是兩個NODE使用的是同一組資料,包含啟動檢查所需的debian-sys-maint帳號/密碼,
所以必須將NODE-A上面的 /etc/mysql/my.cnf & /etc/mysql/debian.cnf 複製到NODE-B上,
這樣當發生failover切換時NODE-B才能正常啟動MySQL.

HeartBeat設定

這邊使用設定上比較單純的Heartbeat R1 compatible clusters

NODE-A 上產生/etc/ha.d/authkeys
( echo -ne "auth 1\n1 sha1 "; dd if=/dev/urandom bs=512 count=1 | openssl md5 ) > /etc/ha.d/authkeys
chmod 0600 /etc/ha.d/authkeys


NODE-A 剛產出的 /etc/ha.d/authkeys 複製到HOST-B

HOST-A heartbeat主設定檔
/etc/ha.d/ha.cf
autojoin none
ucast eth0 192.168.1.12 #點對點偵測 HOST-B 的IP
ucast eth1 192.168.100.12 #點對點偵測 HOST-B 的IP
ping 192.168.1.254 #其他IP連線偵測,給個夠穩定的IP供偵測網路狀態用
respawn hacluster /usr/lib/heartbeat/ipfail
respawn hacluster /usr/lib/heartbeat/dopd
apiauth dopd gid=haclient uid=hacluster
udpport 694
warntime 5 #偵測到節點異常後的等待警告時間
deadtime 15 #偵測到節點異常後的等待切換時間
initdead 60 #初始啟動等待60秒
keepalive 2 #每兩秒偵測一次
node NODE-A
node NODE-B
auto_failback off #當Default Primary(NODE-A)掛掉再恢復之後不自動取回Primary的身分,以避免頻繁切換影響服務

HOST-B
heartbeat主設定檔
/etc/ha.d/ha.cf
autojoin none
ucast eth0 192.168.1.11 #點對點偵測 HOST-A 的IP
ucast eth1 192.168.100.11 #點對點偵測 HOST-A 的IP
ping 192.168.1.254 #其他IP連線偵測,給個夠穩定的IP供偵測網路狀態用
respawn hacluster /usr/lib/heartbeat/ipfail
respawn hacluster /usr/lib/heartbeat/dopd
apiauth dopd gid=haclient uid=hacluster
udpport 694
warntime 5
deadtime 15
initdead 60
keepalive 2
node NODE-A
node NODE-B
auto_failback off

NODE-A
& NODE-B heartbeat資源設定檔
/etc/ha.d/haresources

NODE-A  \
192.168.1.10/24 \
drbddisk::r0 \
Filesystem::/dev/drbd0::/DB::xfs::noatime \
mysql
裡面指定NODE-A為Default Primary,綁定IP為192.168.1.10,使用DRBD resource r0 , 將/dev/drbd0 掛載於 /DB ,指定為xfs,使用noatime參數,最後啟動mysql

HeartBeat failover測試

正常情況

NODE-A為Primary ,DRBD resource r0在NODE-A上為Primary, /dev/drbd0 掛載於 /DB ,MySQL在NODE-A上運作,eth0:0 綁定 192.168.1.10 以提供服務 ,
此時WEB或是其他APP對 192.168.1.10 進行MySQL的資料連線,由NODE-A進行處裡.
NODE-A & NODE-B的heartbeat相互偵測,同時也對額外的PingNode 192.168.1.254進行偵測, NODE-B待命, DRBD resource r0在NODE-B上是屬於Secondary,NODE-A的 /dev/drbd0資料異動會及時寫到NODE-B上

網路發生異常時

NODE-A的網路出現異常(網線中斷或網卡故障)時, NODE-A 在偵測到此狀況後會先將MySQL停止,卸載 /DB 目錄,將DRBD resource r0 /dev/drbd0設為 Secondary.
而後NODE-B 接手將DRBD resource r0 設為Primary, /dev/drbd0 掛載於 /DB , 再將MySQL 啟動, eth0:0 綁定 192.168.1.10 以提供服務 ,
FailOver過程中WEB或是其他APP對DB IP:192.168.1.10的連線會中斷,但當FailOver動作完成之後重連便由NODE-B進行處裡.

主節點死掉時

NODE-B偵測到主節點(NODE-A)死掉的時候,會主動將DRBD resource r0 設為Primary, /dev/drbd0 掛載於 /DB , 再將MySQL 啟動, eth0:0 綁定 192.168.1.10 以提供服務

MySQL with DRBD效能測試

使用MySQL附帶的 sql-bench (/usr/share/mysql/sql-bench) 中的 test-create & test-insert來分別針對 MySQL DATAFILE在一般磁碟分區與DRBD上進行測試比較.以下擷取幾個比較明顯的測試數據,主要差距發生寫入動作上,select讀取並沒有太大差異 .
N代表MySQL DATAFILE放在一般硬碟分區上,D代表MySQL DATAFILE放在DRBD上,檔案系統使用XFS,DB格式為MyISAM
test-create
N Time for create_MANY_tables (10000): 27 wallclock secs ( 0.11 usr 0.09 sys + 0.00 cusr 0.00 csys = 0.20 CPU)
D Time for create_MANY_tables (10000): 67 wallclock secs ( 0.31 usr 0.18 sys + 0.00 cusr 0.00 csys = 0.49 CPU)
N Time for drop_table_when_MANY_tables (10000): 7 wallclock secs ( 0.13 usr 0.48 sys + 0.00 cusr 0.00 csys = 0.61 CPU)
D Time for drop_table_when_MANY_tables (10000): 35 wallclock secs ( 0.19 usr 0.75 sys + 0.00 cusr 0.00 csys = 0.94 CPU)
N Time for create+drop (10000): 22 wallclock secs ( 0.20 usr 0.23 sys + 0.00 cusr 0.00 csys = 0.43 CPU)
D Time for create+drop (10000): 57 wallclock secs ( 0.83 usr 1.29 sys + 0.00 cusr 0.00 csys = 2.12 CPU)
N Time for create_key+drop (10000): 25 wallclock secs ( 0.26 usr 1.05 sys + 0.00 cusr 0.00 csys = 1.31 CPU)
D Time for create_key+drop (10000): 48 wallclock secs ( 0.43 usr 0.55 sys + 0.00 cusr 0.00 csys = 0.98 CPU)
N Total time: 84 wallclock secs ( 0.84 usr 2.11 sys + 0.00 cusr 0.00 csys = 2.95 CPU)
D Total time: 210 wallclock secs ( 1.86 usr 3.10 sys + 0.00 cusr 0.00 csys = 4.96 CPU)
test-insert
N Time for insert (300000): 34 wallclock secs ( 1.35 usr 9.09 sys + 0.00 cusr 0.00 csys = 10.44 CPU)
D Time for insert (300000): 39 wallclock secs ( 2.46 usr 26.74 sys + 0.00 cusr 0.00 csys = 29.20 CPU)
N Time for insert_duplicates (100000): 9 wallclock secs ( 0.42 usr 3.08 sys + 0.00 cusr 0.00 csys = 3.50 CPU)
D Time for insert_duplicates (100000): 12 wallclock secs ( 1.29 usr 2.22 sys + 0.00 cusr 0.00 csys = 3.51 CPU)
N Total time: 610 wallclock secs (85.82 usr 184.06 sys + 0.00 cusr 0.00 csys = 269.88 CPU)
D Total time: 623 wallclock secs (111.64 usr 116.01 sys + 0.00 cusr 0.00 csys = 227.65 CPU)

由上面比較可以看出在進行 test-create 做大量的create table動作時,因為MyISAM每個table都有三個實體檔案(.frm .MYI .MYD)的特性,所造成大量的建檔動作對DRBD的負擔較重,效能下降的幅度高達 250%.
而在進行test-insert時只是單一table進行資料寫入,效能下降幅度較輕微(寫入動作效能約降1/4~1/5).
一般情況下MySQL並不會有大量的create table動作,所以搭配DRBD原則上是在資料異動寫入時耗損約1/4~1/5的效能,所以進行評估時就看在即時備援的考量之下是否可接受這個效能降低的代價了.

參考資料
The DRBD User's Guide : Chapter 8. Integrating DRBD with Heartbeat clusters: http://www.drbd.org/users-guide/ch-heartbeat.html
MySQL 5.0 Reference Manual :: 14.2 Using Linux HA Heartbeat: http://dev.mysql.com/doc/refman/5.0/en/ha-heartbeat.html

相關資源
MySQL: http://www.mysql.com
Linux-HA: http://linux-ha.org/
DRBD: http://www.drbd.org/
Heartbeat: http://linux-ha.org/Heartbeat