
本文转载自CSDN博主wzy0623
原文链接:https://blog.csdn.net/wzy0623/article/details/97918038

01

A:Async,异步:本地写成功后立即返回,数据放在发送buffer中,可能丢失,但传输性能好。 B:Semi sync,半同步:对方接收到数据后,但还没有落盘前返回。 C:Sync,同步:本地和对方写成功落盘确认后返回,数据可靠性高,生产系统一般都采用这种方式。
资源名称:可以是除了空白字符外的任意ACSII码字符。
DRBD设备:在双方节点上,此DRBD设备的设备文件,一般为/dev/drbdN,其主设备号147。
磁盘:在双方节点上,各自提供的存储设备。
网络配置:双方数据同步时所使用的网络属性。
4. DRBD 配置工具
DRBD主要有以下三个配置工具:
drbdadm:高级管理工具,管理/etc/drbd.conf,向drbdsetup和drbdmeta发送指令。
drbdsetup:配置装载进kernel的DRBD模块,平时很少直接用。
drbdmeta:管理META数据结构,平时很少直接用。
5. DRBD与RAID1区别
RAID1也是实现不同存储设备间的数据镜像备份,不同的是RAID1各存储设备是连接一个RAID控制器接入到一台主机上的,而DRBD是通过网络实现不同节点主机存储设备数据的镜像备份。
DRBD安装配置
02
IP与主机名:
172.16.1.125 node1
软件环境:
CentOS Linux release 7.2.1511 (Core)
DRBD 9.0.20
DRBD:
磁盘分区:/dev/sdb 5G
DRBD设备:/dev/drbd0
DRBD资源:mysql
[root@node1~]#cat /etc/sysconfig/network-scripts/ifcfg-ens32
# Generated by dracut initrd
NAME="ens32"
DEVICE="ens32"
ONBOOT=yes
NETBOOT=yes
UUID="adb62466-2361-405e-ada9-b48fe7c09546"
IPV6INIT=yes
BOOTPROTO=static
TYPE=Ethernet
IPADDR=172.16.1.125
NETMASK=255.255.255.0
GATEWAY=172.16.1.254
DNS1=172.16.1.10
重启网络服务:
service network restart
(2)配置域名解析
编辑/etc/hosts文件配置域名解析,如node1的内容如下:
[root@node1~]#cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.1.125 node1
172.16.1.126 node2
(3)禁用SELINUX与防火墙
systemctl stop firewalld
systemctl disable firewalld
(4)设置双向ssh免密
node1上执行:
ssh-keygen -t rsa
ssh-copy-id 172.16.1.126
node2上执行:
ssh-keygen -t rsa
ssh-copy-id 172.16.1.125
yum install ntp
systemctl enable ntpd.service
service ntpd start
# 导入GPG-KEY,用于验证签名
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
# 安装yum源
yum install https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
# 安装drbd和内核相关软件包
yum install -y drbd90-utils kmod-drbd90 kernel*
# 因为升级了内核,需要重启系统
reboot
重启向内核加载drbd模块并确认:
[root@node2~]#modprobe drbd
[root@node2~]#lsmod | grep drbd
drbd_transport_tcp 22144 1
drbd 568697 3 drbd_transport_tcp
libcrc32c 12644 4 xfs,drbd,ip_vs,nf_conntrack
[root@node2~]#
4. 配置DRBD
[root@node1~]#cat /etc/drbd.conf
# You can find an example in /usr/share/doc/drbd.../drbd.conf.example
include "drbd.d/global_common.conf";
include "drbd.d/*.res";
[root@node1~]#
/etc/drbd.conf主配置文件里面配置了包含关系,不需要做任何修改,直接使用即可。
(2)配置global_common.conf文件内容如下。
[root@node1~]#cat /etc/drbd.d/global_common.conf
global {
usage-count no; # 不让官方统计drbd的使用情况
}
common {
protocol C; # 使用DRBD的同步协议,数据可靠性高
disk {
on-io-error detach; # 配置I/O错误处理策略为分离
}
}
on-io-error策略可能为以下选项之一:
detach:这是默认和推荐的选项,如果在节点上发生底层的硬盘I/O错误,它会将设备运行在Diskless无盘模式下。
pass_on:DRBD会将I/O错误报告到上层,在主节点上,它会将其报告给挂载的文件系统。
local-in-error:调用本地磁盘I/O处理程序定义的命令。这需要有相应的local-io-error调用的资源处理程序,给管理员有足够自由的权力调用local-io-error处理I/O错误。
/etc/drbd.d/global_common.conf是全局配置文件,这里只设置了使用统计量、同步协议、I/O错误处理策略三项。该文件中还有一些其它选项,每个选项的含义在安装后的样例文件中都有相应说明。
[root@node1 ~]# cat /etc/drbd.d/mysql.res
resource mysql { # 资源名称
disk /dev/sdb; # 磁盘分区
device /dev/drbd0; # DRBD设备
meta-disk internal; # 元数据存储方式
on node1 {
device /dev/drbd0;
disk /dev/sdb;
address 172.16.1.125:7789; # 节点1地址
}
on node2 {
device /dev/drbd0;
disk /dev/sdb;
address 172.16.1.126:7789; # 节点2地址
}
}
metadata有两种存储方式internal和external,存储方式是在每个resource配置段中指定的。配置成使用internal意味着DRBD把它的metadata和实际生产数据存储于相同的底层物理设备中。该存储方式是在设备的最后位置留出一个区域来存储metadata。
scp -rp /etc/drbd.d/* node2:/etc/drbd.d/
(5)初始化设备元数据
drbdadm create-md mysql
(6)启动DRBD资源
drbdadm up mysql
(7)查看节点角色
[root@node1~]#drbdadm role mysql
Secondary
(8)将node1设置primary
[root@node1~]#drbdadm --force primary mysql
[root@node1~]#drbdadm role mysql
Primary
Parimary表示资源目前为主,并且可能正在被读取或写入,如果不是双主只会出现在两个节点中的其中一个节点上。Secondary表示资源目前为从,正常接收对等节点的更新。
[root@node1~]#mkfs.xfs /dev/drbd0
meta-data=/dev/drbd0 isize=512 agcount=4, agsize=327668 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=0, sparse=0
data = bsize=4096 blocks=1310671, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal log bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
因为文件系统的挂载只能在Primary节点进行,所以也只有在设置了主节点后才能对DRBD设备进行格式化,这样就会连格式化后的全部信息都镜像到另一个节点。
mount /dev/drbd0 /mnt
(11)查看状态
[root@node1/mnt]#drbdadm dstate mysql
UpToDate/UpToDate
[root@node1/mnt]#drbdadm cstate mysql
Connected
节点的硬盘有可能为下列状态之一:
Diskless:本地没有块设备分配给DRBD使用,这表示没有可用的设备,或者使用drbdadm命令手工分离或是底层的I/O错误导致自动分离。
Attaching:读取无数据时候的瞬间状态。
Failed:本地块设备报告I/O错误的状态,其下一个状态为Diskless无盘。
Negotiating:在已经连接的DRBD设置进行Attach读取无数据前的瞬间状态。
Inconsistent:数据是不一致的,在两个节点上(初始的完全同步前)这种状态出现后立即创建一个新的资源。此外,在同步期间在同步目标节点上出现这种状态。
Outdated:数据资源是一致的,但是已经过时。
DUnknown:当对等节点网络连接不可用时出现这种状态。
Consistent:一个没有连接的节点数据一致。当建立连接时,它决定数据是UpToDate或是Outdated。
UpToDate:一致的最新的数据状态,这个状态为正常状态。
一个资源可能有以下连接状态中的一种:
StandAlone:网络配置不可用,资源还没有被连接或是被管理断开(使用 drbdadm disconnect 命令),或是由于出现认证失败或是脑裂的情况。
Disconnecting:断开只是临时状态,下一个状态是StandAlone独立的。
Unconnected:是尝试连接前的临时状态,可能下一个状态为WFconnection和WFReportParams。
Timeout:与对等节点连接超时,也是临时状态,下一个状态为Unconected。
BrokerPipe:与对等节点连接丢失,也是临时状态,下一个状态为Unconected。
NetworkFailure:与对等节点推动连接后的临时状态,下一个状态为Unconected。
ProtocolError:与对等节点推动连接后的临时状态,下一个状态为Unconected。
TearDown:临时状态,对等节点关闭,下一个状态为Unconected。
WFConnection:等待和对等节点建立网络连接。
WFReportParams:已经建立TCP连接,本节点等待从对等节点传来的第一个网络包。
Connected:DRBD已经建立连接,数据镜像现在可用,节点处于正常状态。
StartingSyncS:完全同步,有管理员发起的刚刚开始同步,未来可能的状态为SyncSource或PausedSyncS。
StartingSyncT:完全同步,有管理员发起的刚刚开始同步,下一状态为WFSyncUUID。
WFBitMapS:部分同步刚刚开始,下一步可能的状态为SyncSource或PausedSyncS。
WFBitMapT:部分同步刚刚开始,下一步可能的状态为WFSyncUUID。
WFSyncUUID:同步即将开始,下一步可能的状态为SyncTarget或PausedSyncT。
SyncSource:以本节点为同步源的同步正在进行。
SyncTarget:以本节点为同步目标的同步正在进行。
PausedSyncS:以本地节点是一个持续同步的源,但是目前同步已经暂停,可能是因为另外一个同步正在进行或是使用命令(drbdadm pause-sync)暂停了同步。
PausedSyncT:以本地节点为持续同步的目标,但是目前同步已经暂停,这可以是因为另外一个同步正在进行或是使用命令(drbdadm pause-sync)暂停了同步。
VerifyS:以本地节点为验证源的线上设备验证正在执行。
VerifyT:以本地节点为验证目标的线上设备验证正在执行。
03
1. 将MySQL数据目录设置为DRBD挂载点
datadir = /mnt/
2. 启动mysql服务
# 停止mysql服务
service mysql stop
# 将数据目录拷贝到挂载点
cp -r /data/* /mnt/
# 将数据目录的属主改为MySQL
chown -R mysql:mysql /mnt/
# 启动mysql服务
service mysql start
3. 建立数据库表
create database db1;
use db1;
create table t1 (a int);
insert into t1 select 1;
commit;
4. 手工执行主从切换,验证数据同步
service mysql stop
umount /mnt
drbdadm secondary mysql
(2)将node2升为主,并启动mysql服务
drbdadm primary mysql
mount /dev/drbd0 /mnt
chown -R mysql:mysql /mnt
service mysql start
(3)验证数据同步
mysql> select * from db1.t1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
可以看到数据已经同步,在node2上已经有了新建的库表数据。
heartbeat+DRBD+mysql高可用方案
04
前面的DRBD环境充其量只是一个容灾配置,还算不上高可用方案。设想当主节点的MySQL数据库出现问题时,需要人为发现故障,并手工执行主从切换,这个过程不可避免的长时间停止服务。要保证主服务器不间断服务,需要实现自动故障转移,与DRBD一起使用比较多的是Heartbeat。顾名思义,Heartbeat像是在主从两个DRBD之间接了一条心跳线,当从节点接收不到主节点的心跳时接管服务。Heartbeat双机热备软件来保证数据库服务的稳定性和连续性,数据一致性由DRBD来保证。默认情况下只有一台MySQL实例在工作,当主MySQL服务器出现问题后,系统将自动切换到备机上继续提供服务。这个方案的优点显而易见:安全性、稳定性、可用性高,出现故障自动切换;但缺点也彰明较著:只有一台服务器提供服务,成本相对较高,不方便扩展,可能会发生脑裂。

前面已经完成了DRBD+mysql的安装部署,下面配置heartbeat。heartbeat的安装参见:
[root@node1 ~]# cat /usr/local/heartbeat/etc/ha.d/ha.cf
debugfile /var/log/ha-debug
logfile /var/log/ha-log
logfacility local0
keepalive 1
deadtime 30
warntime 10
initdead 120
udpport 694
bcast ens32
ucast ens32 172.16.1.126
auto_failback off
node node1
node node2
ping 172.16.1.254
respawn hacluster /usr/local/heartbeat/libexec/heartbeat/ipfail
apiauth ipfail gid=haclient uid=hacluster
[root@node1 ~]#
节点2上的/usr/local/heartbeat/etc/ha.d/ha.cf文件只有一个配置项与节点1不同。
ucast ens32 172.16.1.125
(2)编辑认证文件authkeys
节点1与节点2上的authkeys文件相同,内容如下:
[root@node1 ~]# cat /usr/local/heartbeat/etc/ha.d/authkeys
auth 1
1 crc
[root@node1 ~]# cat /usr/local/heartbeat/etc/ha.d/haresources
node1 IPaddr::172.16.1.101/24/ens32 drbddisk::mysql Filesystem::/dev/drbd0::/mnt::xfs mysql
node1:主节点的主机名。
IPaddr::172.16.1.101/24/ens32:用IPaddr脚本配置对外服务的浮动虚拟IP。
drbddisk::mysql:用drbddisk脚本实现DRBD主从节点资源组的挂载和卸载。该脚本文件为heartbeat自带,缺省安装在/etc/ha.d/resource.d/drbddisk。
Filesystem::/dev/drbd0::/mnt::xfs:用Filesystem脚本实现磁盘挂载和卸载。
mysql:MySQL启动脚本。
(4)建立插件软连接
在两个节点执行以下命令:
cd /usr/local/heartbeat/etc/ha.d
chmod 600 authkeys
ln -svf /usr/local/heartbeat/lib64/heartbeat/plugins/RAExec/* /usr/local/heartbeat/lib/heartbeat/plugins/RAExec/
ln -svf /usr/local/heartbeat/lib64/heartbeat/plugins/* /usr/local/heartbeat/lib/heartbeat/plugins/
创建drbddisk文件:
cp /etc/ha.d/resource.d/drbddisk /usr/local/heartbeat/etc/ha.d/resource.d/
创建mysql文件,内容如下:
[root@node1 ~]# more /usr/local/heartbeat/etc/ha.d/resource.d/mysql
chown -R mysql:mysql /mnt/
service mysql start
创建MySQL服务检测脚本文件,内容如下:
[root@node1 ~]# more /home/mysql/mysql_check.sh
#!/bin/bash
. /home/mysql/.bashrc
count=1
while true
do
mysql -uroot -p123456 -S /data/mysql.sock -e "show status;" > /dev/null 2>&1
i=$?
ps aux | grep mysqld | grep -v grep > /dev/null 2>&1
j=$?
if [ $i = 0 ] && [ $j = 0 ]
then
sleep 3
else
if [ $i = 1 ] && [ $j = 0 ]
then
sleep 3
else
if [ $count -gt 5 ]
then
break
fi
let count++
continue
fi
fi
done
/etc/init.d/heartbeat stop
systemctl start heartbeat
systemctl enable heartbeat
systemctl status heartbeat
如下所示,此时VIP绑定在node1上:

客户端通过VIP可以正常访问MySQL:
C:\Users\wxy>mysql -h172.16.1.101 -uroot -p123456 -e "select * from db1.t1"
mysql: [Warning] Using a password on the command line interface can be insecure.
+------+
| a |
+------+
| 1 |
+------+
当启动heartbeat时同时启动ipfail来检测心跳,hacluster为启动heartbeat时的用户 ,也就是说ipfail和heartbeat都是用hacluster这个用户来运行。heartbeat和ipfail进程如下所示:

2. 测试
(1)停掉node1上的mysqld,查看服务是否切换到node2
# 首先启动mysql检测脚本,因为heartheat不检查服务的可用性,需要通过自定义脚本实现
nohup /home/mysql/mysql_check.sh &
# 停止mysqld
service mysql stop
node1的heartbeat进程停止,VIP和DRBD都切换到node2,客户端可以正常访问MySQL,自动切换成功。
# 先启动node1的heartheat
systemctl start heartbeat
# 停止node2的heartheat,也可以使用 iptables -I INPUT -p icmp -j DROP 禁用 ping
systemctl stop heartbeat
node2的heartbeat进程停止,VIP和DRBD都切换回node1,客户端可以正常访问MySQL,自动切换成功。
# 先启动node2的heartheat
systemctl start heartbeat
# node1重启
reboot
VIP和DRBD都切换到node2,客户端可以正常访问MySQL,自动切换成功。
# 停止node1的heartbeat服务
systemctl stop heartbeat
# 停止node2的heartbeat服务
systemctl stop heartbeat
# 在node1上添加防火墙策略,拒绝node2的广播包
iptables -A INPUT -i ens32 -p udp -s 172.16.1.126 --dport 694 -j DROP
# 在node2上添加防火墙策略,拒绝node1的广播包
iptables -A INPUT -i ens32 -p udp -s 172.16.1.125 --dport 694 -j DROP
# 启动node1的heartbeat服务
systemctl start heartbeat
# 启动node2的heartbeat服务
systemctl start heartbeat
在超过initdead定义的时间后,两个节点都绑定了同一个VIP,都挂载了DRDB盘,都启动了MySQL服务,都可以进行MySQL的读写。它们之间各自为政,不知道对方的存在,出现的脑裂。
由于两节点之间在指定的时间内,无法互相检测到对方心跳而各自启动故障转移功能,取得了资源以及服务的所有权,此时的两节点都还活着并作正常运行,这样会导致资源冲突,最严重的就是两台主机同时占用一个VIP的地址,当用户写入数据的时候可能会分别写入到两端,这样可能会导致两节点数据不一致或造成数据丢失,这种情况被称为裂脑。
一般来说,裂脑的发生,主要是由以下的几个原因导致的:
节点对之间心跳线路故障,导致无法正常的通信。
节点对上开启了防火墙阻挡了心跳消息的传输。
节点对上的心跳网卡地址等信息配置的不正确,导致发送心跳失败。
其它服务配置不当等原因,如心跳的方式不同,心跳广播冲突,软件出现了BUG等。
实际的生产环境中,我们可以从以下几个方面来防止裂脑的发生:
同时使用串行电缆和以太网电缆连接,同时用两条心跳线路,这样一条线路坏了,另一个线路还是好的,依然能传送消息,这是最简单的一个方案,也是推荐的防脑裂方法。
检测到裂脑的时候强行的关闭一个心跳节点(需要特殊的节点支持,如stonith,fence),相当于程序上备节点发现心跳线故障,发送关机命令到主节点。
做好对裂脑的监控报警,如邮件以及手机短信等,在问题发生的时候能够人为的介入到仲裁,降低损失。当然,在实施高可用方案的时候,要根据业务的实际需求确定是否能够容忍这样的损失。对于一般的网站业务,这个损失是可控的。
启用磁盘锁。正在服务一方锁住共享磁盘,脑裂发生的时候,让对方完全抢不走共享的磁盘资源。但使用锁磁盘也会有一个不小的问题,如果占用共享盘的乙方不主动解锁,另一方就永远得不到共享磁盘。现实中介入服务节点突然死机或者崩溃,另一方就永远不可能执行解锁命令。后备节点也就截关不了共享的资和应用服务。于是有人在HA中设计了“智能”锁,正在服务的一方只在发现心跳线全部断开时才启用磁盘锁,平时就不上锁。
报警报在服务器接管之前,给人员处理留足够的时间就是1分钟内报警了,但是服务器不接管,而是5分钟之后接管,接管的时间较长。数据不会丢失,但就是会导致用户无法写数据。
报警后,不直接自动服务器接管,而是由人员接管。
增加仲裁的机制,确定谁该获得资源,这里面有几个参考的思路:增加一个仲裁机制。例如设置参考的IP,当心跳完全断开的时候,2个节点各自都ping一下参考的IP,不同则表明断点就出现在本段,这样就主动放弃竞争,让能够ping通参考IP的一端去接管服务。或者通过第三方软件仲裁谁该获得资源。
相关链接
MySQL行级别并行复制能并行应用多少个binlog group?
MySQL高可用工具Orchestrator系列四:拓扑恢复
MySQL高可用工具Orchestrator系列三:探测机制
MySQL高可用工具Orchestrator系列二:复制拓扑的发现
MySQL高可用工具Orchestrator系列一:单节点模式安装
Oracle RAC Cache Fusion系列十八:Oracle RAC Statisticsand Wait Events

更多干货,欢迎来撩~




