| 系统:CentOS 5.4  IP分配: 
                          HA1 eth0:192.168.0.66 eth1:192.168.10.1 
                          HA2 eth0:192.168.0.69 eth1:192.168.10.2 
                          VIP 192.168.0.120 DRBD(Distributed Replicated Block Device),DRBD 
                          号称是 “网络 RAID”,开源软件,由LINBIT 公司开发。DRBD实际上是一种块设备的实现,主要被用于Linux平台下的高可用(HA)方案之中。他有内核模块和相关程序而组成,通过网络通信来同步镜像整个设备,有点类似于一个网络RAID-1的功能。也就是说当你将数据写入本地的DRBD设备上的文件系统时,数据会同时被发送到网络中的另外一台主机之上,并以完全相同的形式记录在文件系统中。本地节点与远程节点的数据可以保证实时的同步,并保证IO的一致性。所以当本地节点的主机出现故障时,远程节点的主机上还会保留有一份完全相同的数据,可以继续使用,以达到高可用的目的。 一、安装DRBD 在HA1和HA2上安装DRBD。 wget http://oss.linbit.com/drbd/8.3/drbd-8.3.5.tar.gz [root@HA1 ~]# tar xzvf drbd-8.3.5.tar.gz [root@HA1 ~]# cd drbd-8.3.5 [root@HA1 drbd-8.3.5]# make clean all [root@HA1 drbd-8.3.5]# make install [root@HA1 drbd-8.3.5]# cd [root@HA1 ~]# vi /etc/drbd.conf 
                          global {           
                          usage-count yes; # 是否参加使用者统计,yes为参加           
                          } 
                          common { 
                          syncer { rate 100M; } # 设置网络同步速率,建议改为实际网络速率 
                                 } # 一个DRBD设备(即:/dev/drbdX),叫做一个"资源"。 
                          resource "r0" { 
                          protocol C; # 数据同步协议,C表示收到远程主机的写入确认后才认为写入完成 
                          startup { 
                                } 
                          disk { 
                                  on-io-error detach; 
                               } 
                          handlers { 
                                  split-brain "/usr/lib/drbd/notify-split-brain.sh 
                          root"; # 自动修复脑裂问题 
                                } 
                          net { 
                                # 设置主备机之间通信使用的信息算法. 
                                cram-hmac-alg sha1; 
                                shared-secret "FooFunFactory"; 
                                # 自动修复脑裂问题 
                                after-sb-0pri discard-zero-changes; 
                                after-sb-1pri discard-secondary; 
                                after-sb-2pri disconnect; 
                              } 
                          syncer { 
                              } 
                          # 每个主机的说明以"on"开头,后面是主机名 
                          on HA1 { 
                                device /dev/drbd0; 
                                disk /dev/sdb; 
                                # 设置DRBD的监听端口,用于与另一台主机通信 
                                address 192.168.0.66:7789; 
                                # metadata的存放位置 
                                # internal表示将metadata存放到drbd挂在的磁盘分区的最后的位置上 
                                meta-disk internal; 
                              } on HA2 { 
                                device /dev/drbd0; 
                                disk /dev/sdb; 
                                address 192.168.0.69:7789; 
                                meta-disk internal; 
                              } 
                          } 
                          DRBD将数据的各种信息块保存在一个专用的区域里,这些metadata包括了 a,DRBD设备的大小 b,产生的标识 c,活动日志 d,快速同步的位图 metadata的存储方式有内部和外部两种方式,使用哪种配置都是在资源配置中定义的 内部metadata:内部metadata存放在同一块硬盘或分区的最后的位置上 
                          优点:metadata和数据是紧密联系在一起的,如果硬盘损坏,metadata同样就没有了,同样在恢复的时候,metadata也会一起被恢复回来 
                          缺点:metadata和数据在同一块硬盘上,对于写操作的吞吐量会带来负面的影响,因为应用程序的写请求会触发metadata的更新,这样写操作就会造成两次额外的磁头读写移动。 外部metadata:外部的metadata存放在和数据磁盘分开的独立的块设备上 
                          优点:对于一些写操作可以对一些潜在的行为提供一些改进 
                          缺点:metadata和数据不是联系在一起的,所以如果数据盘出现故障,在更换新盘的时候就需要人为的干预操作来进行现有node对心硬盘的同步了 [root@HA1 ~]# scp /etc/drbd.conf HA2:/etc/ 初始化并启动两个系统上的 DRBD 服务: [root@HA1 ~]# drbdadm create-md r0 
                          [root@HA1 ~]# service drbd start 
                          Starting DRBD resources: [ d(r0) s(r0) n(r0) ]. 将 HA1 配置为主节点: 
                          [root@HA1 ~]# drbdadm --overwrite-data-of-peer primary 
                          r0 两个设备开始同步数据: 
                          [root@HA2 ~]# service drbd status 
                          drbd driver loaded OK; device status: 
                          version: 8.3.5 (api:88/proto:86-91) 
                          GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb build 
                          by root@HA2, 2009-11-13 01:58:29 
                          m:res cs ro ds p mounted fstype 
                          … sync’ed: 0.6% (6108/6140)M 
                          0:r0 SyncTarget Secondary/Primary Inconsistent/UpToDate 
                          C 
                          ……… 
                          [root@HA2 ~]# service drbd status 
                          drbd driver loaded OK; device status: 
                          version: 8.3.5 (api:88/proto:86-91) GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb 
                          build by root@HA2, 2009-11-13 01:58:29 
                          m:res cs ro ds p mounted fstype 
                          … sync’ed: 45.6% (3344/6140)M 
                          0:r0 SyncTarget Secondary/Primary Inconsistent/UpToDate 
                          C 
                          ……… 同步数据完成: [root@HA2 ~]# service drbd status 
                          drbd driver loaded OK; device status: 
                          version: 8.3.5 (api:88/proto:86-91) 
                          GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb build 
                          by root@HA2, 2009-11-13 01:58:29 
                          m:res cs ro ds p mounted fstype 
                          … sync’ed:100.0% (4/6140)M 
                          0:r0 SyncTarget Secondary/Primary Inconsistent/UpToDate 
                          C [root@HA2 ~]# service drbd status 
                          drbd driver loaded OK; device status: 
                          version: 8.3.5 (api:88/proto:86-91) 
                          GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb build 
                          by root@HA2, 2009-11-13 01:58:29 m:res cs ro ds p mounted fstype 0:r0 Connected Secondary/Primary UpToDate/UpToDate 
                          C [root@HA1 ~]# cat /proc/drbd version: 8.3.5 (api:88/proto:86-91) GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb 
                          build by root@HA1, 2009-11-13 01:53:51 0: cs:Connected ro:Primary/Secondary 
                          ds:UpToDate/UpToDate C r—- ns:6291228 nr:0 dw:0 dr:6291228 al:0 
                          bm:384 lo:0 pe:0 ua:0 ap:0 ep:1 wo:b oos:0 两个节点上的块设备都完全同步之后,使用诸如ext3的文件系统格式化主节点上的 
                          DRBD 设备。 [root@HA1 ~]# mkfs.ext3 /dev/drbd0 测试DRBD服务: 手动挂载DRBD设备,并测试写入文件。 [root@HA1 ~]# mount -o rw /dev/drbd0 
                          /data/ [root@HA1 ~]# echo “This is a test line.” 
                          > /data/test.txt 卸载DRBD设备并将HA1设置为从设备。 [root@HA1 ~]# umount /data/ [root@HA1 ~]# drbdadm secondary r0 将HA2设置为主设备。 [root@HA2 ~]# drbdadm primary r0 [root@HA2 ~]# service drbd status drbd driver loaded OK; device status: version: 8.3.5 (api:88/proto:86-91) GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb 
                          build by root@HA2, 2009-11-13 01:58:29 m:res cs ro ds p mounted fstype 0:r0 Connected Primary/Secondary UpToDate/UpToDate 
                          C 挂载DRBD设备并验证能够读出在HA1上写入的文件。 [root@HA2 ~]# mount -o rw /dev/drbd0 
                          /data/ [root@HA2 ~]# cat /data/test.txt This is a test line. 卸载DRBD设备并将HA2设置为从设备。 [root@HA2 ~]# umount /data/ [root@HA2 ~]# drbdadm secondary r0 将HA1设置为主设备。 [root@HA1 ~]# drbdadm primary r0 查看HA2的DRDB状态: [root@HA2 ~]# service drbd status drbd driver loaded OK; device status: version: 8.3.5 (api:88/proto:86-91) GIT-hash: ded8cdf09b0efa1460e8ce7a72327c60ff2210fb 
                          build by root@HA2, 2009-11-13 01:58:29 m:res cs ro ds p mounted fstype 0:r0 Connected Secondary/Primary UpToDate/UpToDate 
                          C 在HA1和HA2上配置hosts [root@HA1 ~]# cat /etc/hosts 
                          # Do not remove the following line, or various programs 
                          # that require network functionality will fail. 
                          127.0.0.1 vpc localhost.localdomain localhost 
                          ::1 localhost6.localdomain6 localhost6 
                          192.168.10.1 HA1 
                          192.168.10.2 HA2 
                          在HA1和HA2上配置时间同步: [root@HA1 ~]# crontab -e 
                          */5 * * * * /usr/sbin/ntpdate ntp.api.bz  二、在HA1和HA2安装MySQL和Nginx并将数据迁移到/data目录 [root@HA1 ~]# yum install -y mysql-server [root@HA1 ~]# cat /etc/my.cnf 
                          [mysqld] 
                          datadir=/data/mysql 
                          socket=/data/mysql/mysql.sock 
                          user=mysql 
                          bind-address=192.168.0.120 
                          [root@HA1 ~]# cp -r /var/lib/mysql/ /data/ [root@HA1 ~]# cd /data/ [root@HA1 data]# chown -R mysql.mysql 
                          mysql/ [root@HA1 data]# service mysqld start Starting MySQL: [ OK ] [root@HA1 data]# service mysqld stop Stopping MySQL: [ OK ] 注意:数据迁移只需在HA1上做。 安装Nginx略,具体见Nginx 0.7.x + PHP 5.2.8(FastCGI)搭建胜过Apache十倍的Web服务器(http://blog.s135.com/post/366/) [root@HA1 ~]# chkconfig --level 2345 
                          mysqld off [root@HA2 ~]# chkconfig --level 2345 
                          mysqld off 注意:不要在外部启动HA使用的资源,一切让HA去控制。 编写nginx lsb资源代理脚本(注意nginx安装路径): [root@HA1 ~]# cat /etc/init.d/nginxd 
                          #!/bin/sh # source function library 
                          . /etc/rc.d/init.d/functions # Source networking configuration. 
                          . /etc/sysconfig/network # Check that networking is up. 
                          [ ${NETWORKING} = "no" ] && exit 0 RETVAL=0 
                          prog="nginx" nginxDir=/usr/local/nginx 
                          nginxd=$nginxDir/sbin/nginx 
                          nginxConf=$nginxDir/conf/nginx.conf 
                          nginxPid=$nginxDir/nginx.pid nginx_check() 
                          { 
                                if [[ -e $nginxPid ]]; then 
                                ps aux |grep -v grep |grep -q nginx 
                                if (( $? == 0 )); then 
                                echo "$prog already running..." 
                                exit 1 
                                else 
                                rm -rf $nginxPid &> /dev/null 
                                fi 
                                fi 
                           } start() 
                          { 
                                nginx_check 
                                if (( $? != 0 )); then 
                                 true 
                                 else 
                                 echo -n $"Starting $prog:" 
                                 daemon $nginxd -c $nginxConf 
                                 RETVAL=$? 
                                 echo 
                                 [ $RETVAL = 0 ] && touch /var/lock/subsys/nginx 
                                 return $RETVAL 
                                 fi 
                          } stop() 
                          { 
                                 echo -n $"Stopping $prog:" 
                                 killproc $nginxd 
                                 RETVAL=$? 
                                 echo 
                                 [ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx 
                                 $nginxPid 
                          } reload() 
                          { 
                                 echo -n $"Reloading $prog:" 
                                 killproc $nginxd -HUP 
                                 RETVAL=$? 
                                 echo 
                          } case "$1" in 
                          start) 
                          start 
                          ;; 
                          stop) 
                          stop 
                          ;; 
                          restart) 
                          stop 
                          start 
                          ;; 
                          reload) 
                          reload 
                          ;; 
                          status) 
                          status $prog 
                          RETVAL=$? 
                          ;; 
                          *) 
                          echo $"Usage: $0 {start|stop|restart|reload|status}" 
                          RETVAL=1 
                          esac 
                          exit $RETVAL 
                          [root@HA1 ~]# chmod +x /etc/init.d/nginxd [root@HA1 ~]# scp /etc/init.d/nginxd 
                          HA2: /etc/init.d/nginxd 三、安装配置corosync和pacemaker corosync是基于OpenAIS构建的集群引擎,可替代heartbeat进行心跳检测。 The Corosync Cluster Engine is an open 
                          source project Licensed under the BSD License derived 
                          from the OpenAIS project. OpenAIS uses a UDP multicast 
                          based communication protocol to periodically check for 
                          node availability. [root@HA1 ~]# wget -O /etc/yum.repos.d/pacemaker.repo 
                          http://clusterlabs.org/rpm/epel-5/clusterlabs.repo [root@HA1 ~]# wget ftp://ftp.pbone.net/mirror/centos.karan.org/el5/extras/testing/i386/RPMS/libesmtp-1.0.4-6.el5.kb.i386.rpm [root@HA1 ~]# rpm -ivh libesmtp-1.0.4-6.el5.kb.i386.rpm [root@HA1 ~]# yum install -y pacemaker 
                          corosync [root@HA1 ~]# corosync-keygen Corosync Cluster Engine Authentication 
                          key generator. Gathering 1024 bits for key from /dev/random. Press keys on your keyboard to generate 
                          entropy. Writing corosync key to /etc/corosync/authkey. [root@HA1 ~]# scp /etc/corosync/authkey 
                          HA2:/etc/corosync/ [root@HA1 ~]# cp /etc/corosync/corosync.conf.example 
                          /etc/corosync/corosync.conf [root@HA1 ~]# vi !$ 
                          # Please read the corosync.conf.5 manual page 
                          compatibility: whitetank totem { 
                                 version: 2 
                                 secauth: off 
                                 threads: 0 
                                 interface { 
                                        ringnumber: 0          
                                bindnetaddr: 192.168.10.0             
                             mcastaddr: 226.94.1.1             
                             mcastport: 5405 
                                        } 
                          } logging { 
                                 fileline: off 
                                 to_stderr: yes 
                                 to_logfile: yes 
                                 to_syslog: yes 
                                 logfile: /var/log/corosync.log 
                                 debug: off 
                                 timestamp: on 
                                 logger_subsys {         
                                        
                          subsys: AMF 
                                               debug: off 
                                        } 
                          } amf { 
                                 mode: disabled 
                          } service { 
                                 # Load the Pacemaker Cluster Resource Manager 
                                 ver: 0 
                                 name: pacemaker 
                                 use_mgmtd: yes 
                                 } 
                          [root@HA1 ~]# scp /etc/corosync/corosync.conf HA2:/etc/corosync/corosync.conf [root@HA1 ~]# service corosync start Starting Corosync Cluster Engine (corosync): 
                          [ OK ] [root@HA1 ~]# chkconfig --level 2345 
                          corosync on 在HA2上执行: [root@HA2 ~]# chown root:root /etc/corosync/authkey [root@HA2 ~]# chmod 400 /etc/corosync/authkey [root@HA2 ~]# service corosync start Starting Corosync Cluster Engine (corosync): 
                          [ OK ] [root@HA2 ~]# chkconfig --level 2345 
                          corosync on 四、配置CRM资源 
                          [root@HA1 ~]# crm 
                          crm(live)# configure 
                          crm(live)configure# node HA1 
                          crm(live)configure# node HA2 
                          # 配置drbd原始资源 
                          crm(live)configure# primitive drbd ocf:linbit:drbd \ 
                          params drbd_resource="r0" \ 
                          meta migration-threshold="10" 
                          # 配置drbd资源监控 
                          crm(live)configure# monitor drbd 30s:20s 
                          # 配置文件系统原始资源 
                          crm(live)configure# primitive fs ocf:heartbeat:Filesystem 
                          \ 
                          params device="/dev/drbd0" directory="/data" 
                          fstype="ext3" 
                          # 配置mysql原始资源,使用lsb代理 
                          crm(live)configure# primitive mysqld lsb:mysqld 
                          # 配置nginx原始资源,使用lsb代理 
                          crm(live)configure# primitive nginxd lsb:nginxd 
                          # 配置共享IP原始资源 
                          crm(live)configure# primitive vip ocf:heartbeat:IPaddr2 
                          \ 
                          params ip="192.168.0.120" nic="eth0:0" 
                          # 创建资源组保障资源在某一节点上按顺序启动和停止 
                          crm(live)configure# group mysql-group fs vip mysqld 
                          nginxd 
                          # 配置drbd主资源约束 
                          crm(live)configure# ms ms-drbd-mysql drbd \ 
                          meta master-max="1" master-node-max="1" 
                          clone-max="2" clone-node-max="1" 
                          notify="true" 
                          # 配置资源位置约束,保证mysql-group资源组启动在drbd主资源上 
                          crm(live)configure# colocation mysql-on-drbd inf: mysql-group 
                          ms-drbd-mysql:Master 
                          # 配置资源启动顺序约束,保证drbd启动后启动mysql-group资源组 
                          crm(live)configure# order mysql-after-drbd inf: ms-drbd-mysql:promote 
                          mysql-group:start 
                          crm(live)configure# property $id="cib-bootstrap-options" 
                          \ 
                          expected-quorum-votes="2" \ 
                          stonith-enabled="false" \ 
                          no-quorum-policy="ignore" \ 
                          start-failure-is-fatal="false" 
                          crm(live)configure# commit 
                          crm(live)configure# end 
                          crm(live)#  五、测试 [root@HA1 ~]# crm status ============ Last updated: Fri Nov 20 22:47:51 2009 Stack: openais Current DC: HA2 -- partition with quorum Version: 1.0.6-f709c638237cdff7556cb6ab615f32826c0f8c06 2 Nodes configured, 2 expected votes 2 Resources configured. ============ Online: [ HA1 HA2 ] Master/Slave Set: ms-drbd-mysql Masters: [ HA1 ] Slaves: [ HA2 ] Resource Group: mysql-group fs (ocf::heartbeat:Filesystem): Started 
                          HA1 vip (ocf::heartbeat:IPaddr2): Started 
                          HA1 mysqld (lsb:mysqld): Started HA1 nginxd (lsb:nginxd): Started HA1 关闭HA1,在HA2上查看HA状态: [root@HA2 ~]# crm_mon -i1 ============ Last updated: Sat Nov 21 01:31:13 2009 Stack: openais Current DC: HA2 - partition WITHOUT 
                          quorum Version: 1.0.6-f709c638237cdff7556cb6ab615f32826c0f8c06 2 Nodes configured, 2 expected votes 2 Resources configured. ============ Online: [ HA2 ] OFFLINE: [ HA1 ] Master/Slave Set: ms-drbd-mysql Masters: [ HA2 ] Stopped: [ drbd:1 ] Resource Group: mysql-group fs (ocf::heartbeat:Filesystem): Started 
                          HA2 vip (ocf::heartbeat:IPaddr2): Started 
                          HA2 mysqld (lsb:mysqld): Started HA2 nginxd (lsb:nginxd): Started HA2 启动HA1,资源自动迁移到HA1: [root@HA1 ~]# crm_mon -i1 ============ 
                          Last updated: Mon Nov 23 15:42:52 2009 
                          Stack: openais 
                          Current DC: HA2 - partition with quorum 
                          Version: 1.0.6-f709c638237cdff7556cb6ab615f32826c0f8c06 
                          2 Nodes configured, 2 expected votes 
                          2 Resources configured. ============ 
                          Online: [ HA1 HA2 ] 
                          Master/Slave Set: ms-drbd-mysql 
                          Masters: [ HA1 ] 
                          Slaves: [ HA2 ] 
                          Resource Group: mysql-group 
                          fs (ocf::heartbeat:Filesystem): Started HA1 
                          vip (ocf::heartbeat:IPaddr2): Started HA1 
                          mysqld (lsb:mysqld): Started HA1 
                          nginxd (lsb:nginxd): Started HA1 手动迁移资源到HA2 
                          [root@HA1 ~]# crm resource migrate mysql-group HA2 
                          [root@HA2 ~]# crm_mon -i1 ============ 
                          Last updated: Mon Nov 23 15:43:42 2009 
                          Stack: openais 
                          Current DC: HA2 - partition with quorum 
                          Version: 1.0.6-f709c638237cdff7556cb6ab615f32826c0f8c06 
                          2 Nodes configured, 2 expected votes 
                          2 Resources configured. 
                          ============ 
                          Online: [ HA1 HA2 ] 
                          Master/Slave Set: ms-drbd-mysql 
                          Masters: [ HA2 ] 
                          Slaves: [ HA1 ] 
                          Resource Group: mysql-group 
                          fs (ocf::heartbeat:Filesystem): Started HA2 
                          vip (ocf::heartbeat:IPaddr2): Started HA2 
                          mysqld (lsb:mysqld): Started HA2 
                          nginxd (lsb:nginxd): Started HA2 六、解决脑裂(split brain)问题: 在“双机热备”高可用(HA)系统中,当联系2个节点的“心跳线”断开时,本来为一整体、动作协调的HA系统,就分裂成为2个独立的个体。由于相互失去了联系,都以为是对方出了故障,2个节点上的HA软件像“裂脑人”一样,“本能”地争抢“共享资源”、争起“应用服务”,就会发生严重后果:或者共享资源被瓜分、2边“服务”都起不来了;或者2边“服务”都起来了,但同时读写“共享存储”,导致数据损坏(常见如数据库轮询着的联机日志出错)。 对付HA系统“裂脑”的对策大概有以下几条: 
                          1)添加冗余的心跳线,例如双线跳线。尽量减少“裂脑”发生机会。 
                          2)启用磁盘锁。正在服务一方锁住共享磁盘,“裂脑”发生时,让对方完全“抢不走”共享磁盘资源。但使用锁磁盘也会有一个不小的问题,如果占用共享盘的一方不主动“解锁”,另一方就永远得不到共享磁盘。现实中假如服务节点突然死机或崩溃,就不可能执行解锁命令。后备节点也就接管不了共享资源和应用服务。于是有人在HA中设计了“智能”锁。即,正在服务的一方只在发现心跳线全部断开(察觉不到对端)时才启用磁盘锁。平时就不上锁了。 
                          3)设置仲裁机制。例如设置参考IP(如网关IP),当心跳线完全断开时,2个节点都各自ping一下 参考IP,不通则表明断点就出在本端,不仅“心跳”、还兼对外“服务”的本端网络链路断了,即使启动(或继续)应用服务也没有用了,那就主动放弃竞争,让能够ping通参考IP的一端去起服务。更保险一些,ping不通参考IP的一方干脆就自我重启,以彻底释放有可能还占用着的那些共享资源。 手动解决DRBD脑裂问题: [root@HA2 ~]# drbdadm down all  [root@HA2 ~]# drbdadm create-md all |