1. 高可用架构选型背后的思考
1.1 为什么是DRBD而不是共享存储
做过MySQL高可用的朋友都知道,方案大致分两派:共享存储派和复制派。共享存储派里最常见的是SAN或者NAS挂载,但中小团队往往没有独立存储设备,预算和运维成本都扛不住。这时候DRBD就成了一个很务实的选择——它把块设备通过内核模块在网络上做实时镜像,相当于用两台服务器的本地磁盘“拼”出一个逻辑上的共享盘。
我最早接触DRBD是在一个内部业务系统上,当时需求很明确:MySQL不能停超过30秒,预算买不起商业存储,团队只有两个人能维护。DRBD+ Pacemaker + Corosync这套组合就是在这种约束下被选出来的。DRBD负责数据层同步,Pacemaker负责资源调度和决策,Corosync负责集群节点间的通信和心跳。三者各司其职,配合起来能实现MySQL主备加自动切换。
这里要强调一个关键点:DRBD是块设备级别的同步,不是文件级别的。这意味着它对上层应用完全透明,MySQL根本不知道自己的数据目录其实是在一个网络镜像的块设备上。这种透明性带来便利的同时也带来一个隐患——如果DRBD的同步状态出问题,MySQL可能还在正常读写,但数据已经不一致了。所以监控DRBD的磁盘状态是日常运维的重中之重。
1.2 Pacemaker与Corosync的分工逻辑
很多人会把Pacemaker和Corosync混为一谈,其实它们职责完全不同。Corosync管的是“消息层”:节点之间互相发心跳、传集群消息、做成员管理。Pacemaker管的是“决策层”:谁当主、谁当备、资源怎么起停、故障时怎么切换。打个比方,Corosync像是集群里的对讲机系统,Pacemaker像是坐在指挥室里的调度员。
这种分层设计的好处是灵活。Corosync可以换配置、换传输方式,Pacemaker不用动;Pacemaker可以换资源代理、换切换策略,Corosync也不用动。但坏处是排查问题时需要同时看两边的日志,新手容易搞混。我踩过的一个坑就是:Corosync心跳正常,但Pacemaker不切换,最后发现是资源代理脚本里的判断逻辑写错了。
1.3 主备模式与双主模式的取舍
MySQL主备模式下,备库默认不提供写服务,只做只读或者热备。有人会问为什么不搞双主?双主听起来很美,两边都能写,但MySQL的双主在冲突处理上非常麻烦,自增ID、唯一键冲突、数据回环都是坑。而且DRBD本身是单主模式(Primary/Secondary),同一时刻只能有一个节点挂载并读写块设备,这天然就限制了双主的可能性。
所以这套架构的定位很清晰:写操作永远只在一个节点上,备节点处于待命状态。切换发生时,备节点接管VIP、挂载DRBD、启动MySQL。整个过程对应用层来说就是VIP漂移加短暂连接中断,应用重连后继续工作。这种模式牺牲了写扩展性,换来了数据一致性和运维简单性,对大多数中小业务来说是完全划算的。
2. 核心组件细节与实操要点拆解
2.1 DRBD的资源配置与同步模式
DRBD的配置文件通常放在/etc/drbd.d/目录下,每个资源一个.res文件。核心配置项包括resource、device、disk、meta-disk、on节点块、address、port等。下面是一个典型的配置示例:
resource mysql_data { device /dev/drbd0; disk /dev/sdb1; meta-disk internal; on node1 { address 192.168.1.101:7789; node-id 0; } on node2 { address 192.168.1.102:7789; node-id 1; } net { protocol C; cram-hmac-alg sha1; shared-secret "your_secret_here"; } disk { on-io-error detach; fencing resource-only; } }这里有几个参数值得展开说。protocol C是DRBD的同步复制模式,写操作必须等到本地和远端都确认落盘才返回成功。这是数据安全性最高的模式,但延迟也最大。如果对性能要求极高且能容忍少量数据丢失,可以考虑protocol B(半同步)或protocol A(异步)。我个人的经验是:MySQL场景下除非网络延迟特别大,否则一律用C,数据丢了比性能慢更可怕。
on-io-error detach这个参数很关键。当底层磁盘出现IO错误时,DRBD会自动detach该节点,避免坏盘上的数据继续被同步到对端。如果不设这个,坏盘可能把错误数据扩散出去,那就真的回天乏术了。
meta-disk internal表示元数据存在同一个磁盘的末尾区域。这种方式简单,但要求磁盘末尾预留足够空间。另一种方式是meta-disk /dev/sdc1,把元数据单独放一块盘上,性能更好但多占一个设备。
2.2 Pacemaker的资源栈与约束规则
Pacemaker的资源管理核心是“资源代理”(Resource Agent),常见的有ocf:heartbeat:IPaddr2、ocf:heartbeat:Filesystem、ocf:heartbeat:mysql、ocf:linbit:drbd等。每个资源代理都有自己的参数和状态机,Pacemaker通过调用这些代理来启动、停止、监控资源。
配置资源的基本命令是pcs resource create,比如:
pcs resource create drbd_mysql ocf:linbit:drbd \ drbd_resource=mysql_data \ op monitor interval=15s role=Master \ op monitor interval=30s role=Slave pcs resource master ms_drbd_mysql drbd_mysql \ master-max=1 master-node-max=1 \ clone-max=2 clone-node-max=1 \ notify=true这里master(新版Pacemaker叫promotable)资源是关键,它定义了DRBD的主从角色。master-max=1表示同一时刻只能有一个节点是Primary,这是DRBD单主模式的硬性要求。
资源创建完之后,必须定义约束规则,否则Pacemaker不知道资源之间的依赖关系和启动顺序。约束分三类:location(位置约束,决定资源倾向在哪个节点)、order(顺序约束,决定资源启动先后)、colocation(共置约束,决定资源是否必须在同一节点)。典型的约束配置如下:
pcs constraint colocation add mysql_vip with ms_drbd_mysql INFINITY with-rsc-role=Master pcs constraint colocation add mysql_fs with mysql_vip INFINITY pcs constraint colocation add mysql_service with mysql_fs INFINITY pcs constraint order promote ms_drbd_mysql then start mysql_fs pcs constraint order start mysql_fs then start mysql_service pcs constraint order start mysql_vip then start mysql_fs这些约束翻译成人话就是:VIP必须和DRBD主节点在一起,文件系统必须和VIP在一起,MySQL必须和文件系统在一起;启动顺序是先提升DRBD为主,再挂文件系统,再起MySQL,最后配VIP。顺序错了,MySQL可能因为找不到数据目录而启动失败。
2.3 Corosync的通信配置与心跳机制
Corosync的配置文件是/etc/corosync/corosync.conf,核心是totem块。下面是一个双节点配置:
totem { version: 2 cluster_name: mysql_cluster transport: knet crypto_cipher: aes256 crypto_hash: sha256 token: 3000 token_retransmits_before_loss_const: 10 join: 60 consensus: 3600 max_messages: 20 interface { ringnumber: 0 bindnetaddr: 192.168.1.0 mcastport: 5405 ttl: 1 } } quorum { provider: corosync_votequorum expected_votes: 2 two_node: 1 } logging { fileline: off to_stderr: yes to_logfile: yes logfile: /var/log/corosync/corosync.log to_syslog: yes debug: off timestamp: on logger_subsys { subsys: QUORUM debug: off } }token参数决定心跳超时时间,默认3000毫秒。如果网络抖动大,可以适当调大,但调太大意味着故障检测变慢。consensus是共识超时,通常是token的1.2倍左右。two_node: 1是双节点集群的特殊配置,告诉Corosync只有两个节点,避免脑裂时无法形成多数派。
注意:双节点集群没有真正的多数派,所以必须配合fencing设备(如IPMI、PDU)或者
resource-onlyfencing策略,否则脑裂时两边都以为自己是主,数据就乱了。
2.4 MySQL在集群中的启动脚本适配
Pacemaker自带的ocf:heartbeat:mysql代理可以直接用,但有几个参数必须根据实际环境调整:
pcs resource create mysql_service ocf:heartbeat:mysql \ binary=/usr/sbin/mysqld \ config=/etc/my.cnf \ datadir=/var/lib/mysql \ socket=/var/lib/mysql/mysql.sock \ pid=/var/run/mysqld/mysqld.pid \ log=/var/log/mysqld.log \ user=mysql \ test_user=root \ test_passwd=your_password \ test_table=mysql.user \ op monitor interval=20s timeout=30s \ op start timeout=120s interval=0 \ op stop timeout=120s interval=0test_user和test_passwd是监控用的账号密码,Pacemaker会定期用这个账号连MySQL执行SELECT来确认服务健康。test_table指定监控查询的表,一般用mysql.user就行。op monitor的timeout要设得比MySQL正常响应时间长,否则网络稍微慢一点就误判为故障。
还有一个容易忽略的点:MySQL的datadir必须和DRBD挂载点一致。比如DRBD挂载到/data,那datadir就应该是/data/mysql,而不是默认的/var/lib/mysql。这个路径在my.cnf里也要同步改,否则切换后备节点启动MySQL会找不到数据。
3. 完整实操流程与关键环节实现
3.1 环境准备与基础软件安装
假设有两台服务器,主机名分别是node1和node2,操作系统是常见的Linux发行版。每台机器除了系统盘外,额外挂一块数据盘/dev/sdb,用来做DRBD的底层设备。
第一步,配置主机名和hosts解析:
hostnamectl set-hostname node1 echo "192.168.1.101 node1" >> /etc/hosts echo "192.168.1.102 node2" >> /etc/hosts第二步,安装必要软件包。不同发行版包名略有差异,以常见的包管理器为例:
# 安装DRBD、Pacemaker、Corosync及相关工具 yum install -y drbd90-utils kmod-drbd90 pacemaker corosync pcs fence-agents-all或者:
apt install -y drbd-utils pacemaker corosync pcs fence-agents第三步,加载DRBD内核模块并设置开机自启:
modprobe drbd echo "drbd" >> /etc/modules-load.d/drbd.conf第四步,初始化数据盘。两块盘都要做,且分区大小要完全一致:
parted /dev/sdb mklabel gpt parted /dev/sdb mkpart primary 0% 100%3.2 DRBD资源初始化与首次同步
在两台机器上都创建/etc/drbd.d/mysql_data.res,内容如前面2.1节所示。然后执行初始化:
drbdadm create-md mysql_data drbdadm up mysql_data这时候DRBD应该处于Connected但Secondary/Secondary状态。接下来在node1上执行首次同步:
drbdadm primary --force mysql_data这个命令会把node1设为Primary,并开始把本地数据同步到node2。同步时间取决于磁盘大小和网络带宽,100GB的数据在千兆网络下大概需要20到30分钟。可以用drbdadm status或者cat /proc/drbd查看同步进度。
实操心得:首次同步期间不要做任何写操作,也不要在DRBD设备上创建文件系统。等两边都显示
UpToDate之后再继续。如果同步卡住,先检查网络连通性和防火墙规则,DRBD默认用7789端口。
同步完成后,在node1上创建文件系统并挂载:
mkfs.ext4 /dev/drbd0 mkdir /data mount /dev/drbd0 /data然后把MySQL的数据目录迁移到/data/mysql,并修改my.cnf中的datadir。确认MySQL能正常启动后,卸载文件系统,把DRBD降为Secondary:
umount /data drbdadm secondary mysql_data3.3 集群初始化与资源栈配置
在两台机器上启动Corosync和Pacemaker:
systemctl enable --now corosync systemctl enable --now pacemaker然后在任意一台机器上设置集群认证并启动集群:
passwd hacluster pcs cluster auth node1 node2 -u hacluster -p your_password pcs cluster setup --name mysql_cluster node1 node2 pcs cluster start --all pcs cluster enable --all检查集群状态:
pcs status应该能看到两个节点都是Online,并且有Quorum。接下来按顺序创建资源:
# 1. 创建DRBD主从资源 pcs resource create drbd_mysql ocf:linbit:drbd \ drbd_resource=mysql_data \ op monitor interval=15s role=Master \ op monitor interval=30s role=Slave pcs resource promotable drbd_mysql \ master-max=1 master-node-max=1 \ clone-max=2 clone-node-max=1 \ notify=true # 2. 创建文件系统资源 pcs resource create mysql_fs ocf:heartbeat:Filesystem \ device=/dev/drbd0 \ directory=/data \ fstype=ext4 \ op monitor interval=20s timeout=40s # 3. 创建VIP资源 pcs resource create mysql_vip ocf:heartbeat:IPaddr2 \ ip=192.168.1.100 \ cidr_netmask=24 \ op monitor interval=10s # 4. 创建MySQL资源 pcs resource create mysql_service ocf:heartbeat:mysql \ binary=/usr/sbin/mysqld \ config=/etc/my.cnf \ datadir=/data/mysql \ socket=/data/mysql/mysql.sock \ pid=/var/run/mysqld/mysqld.pid \ log=/var/log/mysqld.log \ user=mysql \ test_user=root \ test_passwd=your_password \ test_table=mysql.user \ op monitor interval=20s timeout=30s \ op start timeout=120s interval=0 \ op stop timeout=120s interval=0最后添加约束:
pcs constraint colocation add mysql_vip with master drbd_mysql INFINITY with-rsc-role=Master pcs constraint colocation add mysql_fs with mysql_vip INFINITY pcs constraint colocation add mysql_service with mysql_fs INFINITY pcs constraint order promote drbd_mysql then start mysql_fs pcs constraint order start mysql_fs then start mysql_vip pcs constraint order start mysql_vip then start mysql_service配置完成后,Pacemaker会自动在某个节点上启动所有资源。用pcs status确认所有资源都在同一个节点上运行,且DRBD处于Master状态。
3.4 手动切换与自动切换验证
手动切换是验证集群配置是否正确的最直接方式。执行:
pcs resource move mysql_service node2Pacemaker会按顺序停止MySQL、卸载文件系统、降级DRBD、释放VIP,然后在node2上反向操作。整个过程大概需要30到60秒。切换完成后,用pcs status确认资源都在node2上,并且VIP能ping通,MySQL能连接。
自动切换的验证稍微麻烦一点。最安全的方式是模拟MySQL进程崩溃:
# 在MySQL所在节点上执行 killall -9 mysqldPacemaker的监控会在20秒内发现MySQL不可用,然后触发切换。如果配置正确,资源会自动漂移到另一个节点。这里要注意:killall -9只是杀进程,不模拟节点宕机。如果要模拟节点宕机,可以用echo c > /proc/sysrq-trigger,但这会导致内核崩溃,测试环境可以玩,生产环境千万别试。
避坑技巧:自动切换测试前,先把
pcs property set stonith-enabled=false,否则没有fencing设备时集群会拒绝切换。测试完再根据实际情况决定是否开启STONITH。
4. 常见问题与排查技巧实录
4.1 DRBD状态异常排查速查表
| 现象 | 可能原因 | 排查命令 | 解决方法 |
|---|---|---|---|
StandAlone | 网络不通或端口被防火墙拦截 | drbdadm status、ss -tlnp | grep 7789 | 检查防火墙规则,确保7789端口双向放行 |
WFConnection | 对端节点DRBD未启动 | systemctl status drbd | 在对端执行drbdadm up mysql_data |
SyncTarget长时间不变 | 同步速度慢或磁盘IO瓶颈 | cat /proc/drbd、iostat -x 1 | 检查网络带宽和磁盘负载,必要时限速同步 |
Diskless | 底层磁盘故障或detach | dmesg | grep drbd | 检查磁盘健康状态,修复后重新attach |
两边都是Primary | 脑裂 | drbdadm status | 手动干预,选择数据新的一方强制Primary,另一方丢弃数据重新同步 |
脑裂是DRBD最危险的状态。一旦发生,必须人工介入,不能自动恢复。处理原则是:选一个数据最新的节点作为Primary,另一个节点执行drbdadm connect --discard-my-data mysql_data,然后重新同步。丢数据的那一方如果有重要变更,那就真的丢了,所以fencing设备在这套架构里不是可选项,而是必选项。
4.2 Pacemaker资源启动失败常见原因
MySQL资源启动失败是最常见的问题,八成以上是路径或权限问题。Pacemaker的日志在/var/log/pacemaker/pacemaker.log,资源代理的日志在/var/log/cluster/下面。排查时先看pcs status里资源的FAILED原因,再去对应日志里找详细报错。
一个很隐蔽的坑是SELinux。如果系统开了SELinux,Pacemaker启动MySQL时可能被拒绝访问/data目录。临时用setenforce 0验证,如果问题消失,就需要给/data目录打上正确的SELinux标签,或者干脆把SELinux设为permissive。
另一个坑是mysql.sock路径。Pacemaker的MySQL代理默认用/var/lib/mysql/mysql.sock,但我们的数据目录在/data/mysql,socket路径必须显式指定,否则监控会失败,导致资源被反复重启。
4.3 切换后应用连接不上的处理思路
切换完成后,VIP漂移过去了,MySQL也起来了,但应用还是连不上。这种情况通常有三个原因:第一,应用配置的是固定IP而不是VIP;第二,MySQL的用户权限里没有允许从VIP所在网段连接;第三,防火墙没有放行VIP的3306端口。
我遇到过一次很诡异的情况:VIP能ping通,telnet 3306也通,但应用就是报连接超时。最后发现是MySQL的bind-address配置成了127.0.0.1,只监听本地回环。切换后新节点上的MySQL同样只监听本地,外部连接自然进不来。把bind-address改成0.0.0.0或者VIP地址就好了。
实操心得:每次切换后,除了检查
pcs status,一定要用mysql -h VIP -u user -p从外部客户端实际连一次。Pacemaker说资源健康,不代表应用能正常访问。
4.4 性能调优与日常维护建议
DRBD的同步会占用网络带宽和磁盘IO,如果业务写入量大,建议把DRBD的同步限速打开,避免同步流量把业务流量挤掉。在资源配置里加:
disk { c-plan-ahead 0; c-fill-target 24M; c-min-rate 80M; c-max-rate 720M; }这些参数控制DRBD的同步速率,c-max-rate是上限,c-min-rate是下限。具体数值要根据实际带宽和业务负载调整,没有万能公式。
日常维护方面,建议每天检查一次drbdadm status和pcs status,每周做一次手动切换演练。演练不是走过场,要真的把业务切过去跑一段时间再切回来,这样才能发现配置里隐藏的问题。我见过太多集群配好之后从来没切过,真出故障时才发现切换脚本有bug,那就被动了。
5. 这套架构的适用边界与扩展思路
5.1 什么场景适合这套方案
DRBD+Pacemaker+Corosync这套组合最适合的是:预算有限、团队规模小、对数据一致性要求高、能接受秒级到分钟级切换时间的场景。比如内部管理系统、中小型电商后台、日志分析平台等。它的优势是全部基于开源软件,没有License费用,架构透明可控,出了问题能自己排查。
但它也有明显的边界。第一,它不适合跨机房部署,DRBD的同步对网络延迟非常敏感,同城双活勉强可以,异地灾备基本不可行。第二,它不适合写压力极大的场景,因为单主模式下写能力受限于单台服务器。第三,它不适合需要读写分离和水平扩展的场景,那应该考虑MySQL Group Replication或者基于中间件的分库分表方案。
5.2 从主备到多节点的演进路径
如果业务增长到单台MySQL扛不住写压力,这套架构就需要演进。一个常见的路径是:保留DRBD+Pacemaker做底层高可用,上层用MySQL的主从复制做读写分离。写请求走VIP到主库,读请求通过多个从库分担。从库可以部署在独立的服务器上,不参与DRBD集群。
另一个路径是引入ProxySQL或者类似中间件,把应用和数据库解耦。应用连ProxySQL,ProxySQL根据后端MySQL的健康状态自动路由。这样即使Pacemaker切换时VIP短暂不可用,ProxySQL也能快速感知并切换到新的主库,应用层几乎无感。
5.3 监控告警的关键指标
这套架构要监控的指标比单机MySQL多得多。除了常规的MySQL连接数、QPS、慢查询之外,还必须监控:DRBD的复制状态(Connected还是StandAlone)、DRBD的磁盘状态(UpToDate还是Inconsistent)、Pacemaker的节点状态和资源状态、Corosync的Quorum状态、VIP是否可达。
我一般会在每台节点上部署一个轻量级的监控代理,定期采集这些指标并推送到监控系统。告警规则里,DRBD状态异常和Pacemaker资源失败是最高优先级,必须第一时间处理。VIP不可达次之,因为可能只是切换过程中的短暂现象。
最后分享一个小技巧:在Pacemaker里配置一个
notification资源,当资源状态变化时自动发邮件或者调用Webhook。这样切换发生时你能立刻知道,而不是等用户报障才发现。具体做法是创建一个ocf:heartbeat:MailTo资源,绑定到集群的notify事件上。虽然配置稍微麻烦一点,但关键时刻能省很多事。