群集技术介绍
保证业务可用性是一切工作的基础
丧失可用性的安全毫无意义
消灭单点故障隐患是提高可用性的主要手段
从单机、多机再到网络所有节点的冗余
多机实现容错通常被称为群集(Cluster)
负载均衡群集 / HA (高可用)/ 服务器群集(关键在于是否同步数据),服务器群集能达到数据和会话的同步,而负载均衡群集第一台服务器宕机了,第二台服务器你就要重新和它建立连接。
服务器群集
共享存储(存储设备价格比较贵)
基于块设备复制(趋势、廉价)
原来构建群集服务器的时候有两台服务器,A、B服务器同时连上一个很贵的存储设备,必须要有三个硬件盒子,把它们连接起来配置好,才能构成以前的群集的。因为中间的存储盒子是很贵的,现在为了节省成本,把中间的存储设备砍掉,砍掉之后变成了A、B两台服务器,那怎么实现数据的同步、数据的移植呢?以前所有的数据所有的群集数据资源都是放在共享的磁盘存储柜里面的,数据就存在那,你只是前端换了一个应用来读取刚才的数据、会话。所以之前的群集这么构建,原因就在于数据永远存在后端的存储设备里面,而存储设备从硬件打造上面可用性非常的强,它出现故障的可能性非常低,磁盘柜里面又有大量的磁盘做冗余和备份,所以基于它自身存储设备的可用性很高,拿它就不当单点故障隐患了,再配合前端几个跑应用的服务器,这样共同来搭建群集。之前之所以这么做,就是因为数据永远存在那一个地方,数据在那个地方是永远不会变的,通过一个集中的数据存储点来保证数据的一致性。
现在把中间的存储设备砍掉,你访问A服务器的时候数据和会话都存储在A服务器里的硬盘里面,如果A服务器出故障了,所有的访问都切换到B服务器上去,应用你是可以正常工作的,但是A里面的数据怎么才能变到B服务器里面呢?技术界里面的研发人员就想到基于块设备,所谓块设备,就像是我们计算机里面的硬盘就是一种块设备。 不是拷贝一个文件到另一个机器的硬盘上,它是把A服务器上整个硬盘存储数据的每一个扇区、每一个存储设备的块,完全照搬映射到同样大小的存储设备上。它把你整个硬盘,硬盘上每一块存储扇区,每一个存储数据的位都完完整整的复制到另一个设备上,这种方式我们就成为块设备的复制。如果A服务器和B服务器上面各有一块硬盘,做成了块设备的复制关系,这个时候,你在A服务器的硬盘上删掉一个为文件,(我们都知道,删掉一个文件操作系统并不会把这个文件对应存储上面的存储空间清零,操作系统只会把这个文件的索引位的第一个字符把它标记成删除了,但其实所有的数据都还存在硬盘里面。)B服务器上硬盘里面也有一块区域被标记删除了。所以只用服务器本地硬盘通过块复制实现彼此数据的保持。来实现群集,那么这个成本会便宜很多。
完整的集群并不只是解决了磁盘的数据同步这一件事情,集群其实是由多种资源组合起来构成的一个集合体。磁盘的数据同步问题可能是一个最重要的问题。
群集资源 群集中所有服务器共同享有的一组资源
如果说现在A服务器作为主服务器向外提供服务访问,那B服务器就可以把自己对群集资源使用权放弃,B先不干活就在这呆着。这就好比两个人干一份工作,第一个人先干着,等干累了或者生病了,第二个人接着干。
所以这个服务器集群,如果就拿最简单的双节点主备集群这种最经典的、最简单的就去哪构建方式,两台服务器一台作为主节点,一台作为备用节点,平时工作只有主节点一台服务器,虽然两台机器都开机,该装的软件都装了,该配置的都配置了,但是平时只有一台服务器在干活。所以在构建群集的时候,有一半的资源是浪费的,只要发生故障了切换应用的一瞬间群集的价值才体现出来。
如果说构成群集的A服务器是主节点的话,所有的群集资源都由A占有。如果A出现故障了,主板烧掉了,这个时候由B节点来接管A的资源。那么B怎么知道A节点出故障了呢?构成集群的A、B两台服务器是由心跳信息来实时的互相监控的。B服务器虽然在这呆着不干活,但一直盯着A服务器,B是发送心跳数据包来探测A是否还在、应用是否正常,突然发现A不行了,B马上把A的资源抢夺过来,放在自己身上,继续向外提供服务。
主机之间基于设备复制
保持主机间的数据同步是一切的基础
DRBD介绍 实现主机之间块设备复制的技术:
DRBD(Distributed Replicated Block Device),免费开源
分布式设备的复制技术
由linbit开源
由Linux内核模块 +用户空间管理工具组成
实现服务器间设备的同步镜像(相当于跨主机的RAID 1)
通常采用主 / 备模式部署(Primary / Slave)
DRBD对下,来管理物理硬盘、物理存储;对上来提供应用的访问接口;对左对右,通过网线来实现数据在不同主机之间的同步。DRBD作为一个中间节点,对上对下对左对右都是它来集中管理。另外它工作在内核层面,速度比较快。
在一主一备构成的群集,A服务器是主节点,对外提供服务,客户端发来的请求都给A服务器,这不是单机情况下只要把数据保存到本地那么简单了。A服务器首先要把数据写到本机的硬盘里面保存下来,通过网络将A服务器上的数据拷贝到B服务器上缓存里面,再从缓存里面写到B服务器上的硬盘里面。这要比单节点存储数据的时间要长。
为了同时满足,既能响应速度快一点,又能保证数据的一致性这一目标,DRBD设计了三种协议方法:
A:异步复制,本地写成功后即确认,发送buffer中的数据可能丢失。由客户端发来数据给我,我就本地写,本地硬盘数据写成功了,就给客户端返回确认,数据已经接收到了。客户端就发起数据的下一次请求。但是A节点向客户端确认的时候,还没有向B节点同步数据,只是在A本地硬盘上写入了,这时候我要把它发给B节点的数据放到A节点的buffer(写缓存)中,但是写缓存没有发给B节点。如果A节点出现故障,A节点中的buffer数据就会丢失,B节点的数据可能会和A节点的数据不一致。 这个方法的响应速度是最快的,相当于单节点的速度,但是丢失数据的可能性也是最大的。
B:内存同步,本地写入并发送成功即确认,如掉电则数据丢失。由客户端发来数据写到本地硬盘里面,不但要写到本地硬盘,我还要把数据通过网络从A节点发送到B节点,B节点这时候接收到了,可能还没有来得及往硬盘里面去写入数据,这种情况也会存在一个短暂的时间片段A节点的数据和B节点的数据不一致。如果刚卡在那一个时间点上,A节点突然宕机,B节点也突然宕机,这时候B节点刚把数据同步到自己的内存里面,就断电了。这时候也会造成数据丢失。B方法比A方法好的一点在于,接收到的数据不但在自己确认硬盘写入了,也同时成功的将数据发给了B节点,但是B节点可能还没成功的将数据写入到硬盘里面。使用B协议的时候,响应速度可能就会比A协议慢了,因为多一步发给B节点数据的操作。
C:同步复制,节点全部写入成功后确认。当有客户端发来数据给第一个节点,第一个节点要把数据从内存里、缓存里写入到本地硬盘里面,然后再把数据通过网络发给第二个节点,第二个节点接收到数据再从内存里写缓存,把数据写入到硬盘里面再到确认,两个节点都确认写入后,再向客户端返回一个确认。这种做法是最能保证数据的一致性。如果说A节点B节点都宕机了,那么数据丢失AB节点数据都会丢,不会出现数据不一样的情况。要丢都丢,要写都写。这种做法响应速度是三种方法是最慢的,但能保证数据的一致性。
C可最高确保数据的同步,但写入时间长,性能出于劣势。
作为要构成的群集的两个服务器,要各放置一个硬盘,这两个硬盘的大小无关紧要,但是要保证来两个一致,比如说都是20G硬盘。
要做块设备的映像,块设备的镜像是从一个服务器的硬盘上的每一个扇区、每一个block去一块一块的完全相同的给它映射复制到另一个服务器的硬盘上。因为要完整的同步,中间要有数据同步的过程。尤其当你硬盘比较大时,数据同步就比较耗时,还可能将硬盘里面原有的已经删除的数据给同步到另一台服务器的硬盘上。为了避免这些问题,会对两块硬盘做如下的操作:
1 sudo dd if =/dev/zero of=/dev/sdb1
这个操作是为了避免构建磁盘做数据同步的时候出现的异常情况,或者读写硬盘的混乱。
硬盘只能一次给一个服务器集群的节点使用,不能两个服务器同时对它读写。同时读写就会造成数据的混乱,数据的混乱对于构建群集是致命的伤害。 出现数据混乱:当硬盘挂载到A服务器的时候,你会看到一个文件,当你将硬盘挂载到B节点的时候,这个文件就没有了。原本两边的数据是应该是镜像同步一致的,但是出现读写硬盘混乱,就会导致不正常。
DRBD安装准备 准备两台节点:
Node 1:usrv01
Node 2:usrv02
两个节点各增加一块硬盘,用于镜像(大小必须一致),物理磁盘、分区、RAID、LVM都可作为镜像块设备
修改主机名 注意:因为两台节点是virtualbox克隆过来的,主机的ip地址一样,需要修改,主机名也要修改成不一样的
1 2 3 4 5 6 sudo vi /etc/machine-id sudo vi /etc/cloud/cloud.cfg sudo vi /etc/hostname
配置域名解析 我们现实的网络里面是有DNS服务器的,那么就可以给两台主机定义好名称,创建主机名和ip地址的对应关系,大家都通过DNS去域名解析。自己用虚拟机试验,可以用hosts主机文件来做域名解析
1 2 3 4 sudo vi /etc/hosts 192.168.1.110 usrv01 192.168.1.111 usrv02
磁盘复制镜像同步 安装drbd 安装之前先安装ntp,因为群集工作过程也要对两台服务器的时间做同步检查,保证群集里面两台服务器的系统时间一致,也是非常重要。
1 2 3 sudo apt install ntp sudo apt install drbd8-utils
修改主配置文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 sudo vi /etc/drbd.conf global { usage-count no; } common { syncer { rate 100M; } } resource r0 { protocol C; startup { wfc-timeout 15; degr-wfc-timeout 60; } net { cram-hmac-alg sha1; shared-secret "secret" ; } on usrv01 { device /dev/drbd0; disk /dev/sdb1; address 192.168.1.110:7788; meta-disk internal; } on usrv02 { device /dev/drbd0; disk /dev/sdb1; address 192.168.1.111:7788; meta-disk internal; } }
先对硬盘sdb先分区
初始化元数据存储(两节点分别执行) 1 sudo drbdadm create-md r0
启动服务 1 2 3 4 5 6 7 8 sudo systemctl start drbd.service sudo systemctl enable drbd.service sudo drbdadm up r0 sudo drbd-overview
指定主节点 1 sudo drbdadm -- --overwrite-data-of-peer primary all
观察同步进程(在另一节点上执行) 1 watch -n1 cat /proc/drbd
测试主备 查看每个节点的状态 1 2 3 lsblk sudo drbd-overview sudo cat /proc/drbd
格式化并挂载 1 2 3 sudo mkfs.ext4 /dev/drbd0 sudo mount /dev/drbd0 /srv/
测试(主节点) 1 2 3 4 5 6 sudo vi /srv/index.html sudo umount /srv/ sudo drbdadm secondary r0
测试(备节点) 1 2 sudo drbdadm primary r0 sudo mount /dev/drbd0 /srv/
节点主备不会自动切换
不可同时做主节点并挂载
需要群集管理器来实现资源的在不同节点间自动切换
Pacemaker介绍 DRBD构建的磁盘镜像的群集只是实现了数据的同步,从一台服务器可以实时的写入到另一台服务器的硬盘里面。通过DRBD只是实现了数据的同步,但它并没有实现当一边的web服务应用宕机了,另外一遍的web服务应用会自动的起来。在终端用户看来,这时候是无法做到数据的无缝衔接的,这时候服务就会当掉,管理员必须手动参与。这不是我们想要的。
我们想要的是完整的应用级别上的群集。我们对外提供一个服务,后端可以通过DRBD实现不同服务器上数据的同步,保证一致性。同时我们需要有一个软件或一个进程,它能够实现不同节点运行状态的判断。当第一个节点挂掉的时候,服务应用挂掉,对磁盘使用占用权已经失效的时候,工作在第二台服务器的程序或进程,它应该能及时发现这件事,这个时候,它应该主动地把这些资源的使用权以及应用服务的资源在原本备用节点的机器上抢占过来,把服务启动、硬盘挂载。
DRBD只是实现磁盘同步数据一致的基础,有了它,我们具备了构建应用层面群集的基础,但是只有DRBD还不够,还需要群集资源管理的程序。
构建一个应用层面的群集需要很多的资源,首先是数据,不同节点之间的数据要保持一致,这个已经通过DRBD得到解决了,可以实现数据在多台服务器上镜像同步。还有应用的服务,比如说web服务、数据库服务,那这些服务也需要作为资源加入到群集里面,由群集对它进行管理。群集资源由群集资源管理器来判断,如果第一个节点工作正常,那群集资源管理器就继续监视;但是一旦发现第一个节点工作状态出现了问题,不能对外提供服务了,群集资源管理器就会立刻在备用节点上将资源从第一台节点抢占过来,硬盘挂载、启动服务,让数据还能一致的对外提供。这是群集资源管理器该做的事情。
而DRBD构建了磁盘镜像,它把磁盘作为一种群集资源,在多个服务器之间实现同步镜像。还需要应用服务的资源,一旦发现主节点数据库服务挂了,备用节点赶紧把服务起起来。还有一个问题就是IP地址。假如说我们构建了一个数据库服务的群集,对外提供一个数据库的服务,但是在数据库最终用户、最终使用者那边看来,他使用的其实是一个唯一的数据库服务,他连接的是一个数据库服务的ip地址。如果一台服务器的数据库服务挂了,那这个时候对外提供数据库服务的ip地址是不能发生变化的。ip地址一旦发生变化,数据库服务的使用者应用程序那边来调用你数据库服务的时候,数据库服务的ip都变了,使用者就得想办法更改ip去连接了。但是我们做群集的目的就是想让应用、使用者这一端不知道后端隐藏着多少个复杂的群集结构。他只知道去访问一个不变的ip地址。这是我们想要的达到的效果。我们想让,对用户来讲是高可用的,但是对用户的使用体验上却保持和之前完全一样,不希望当群集那边资源切换到另一边,IP地址发生了变化。
所以这边群集资源里还包含另外一个基础的、底层的群集资源——ip地址资源,它和磁盘资源的重要性是一致的。不但磁盘数据要保持一致,还要群集对外提供服务的时候要有唯一的一个ip。当群集里一台服务器宕机,备用节点就要把那台服务器的ip地址资源抢占过来。
群集资源统一管理:磁盘、ip地址、应用服务资源
群集资源管理器一方面要监视群集中每个节点的运行状态,并且由它来判断在某一时刻里面交由哪个节点去使用,同时要不停地监控群集中节点的所有状态。群集资源管理器起到一个仲裁的作用。一旦这个节点出现故障,这个资源接下来以多快的速度、什么方式把群集资源抢占到另一台服务器上,重新启用应用服务。
在开源世界里面,在Linux上使用最多的群集资源管理器就是pacemaker。它可以构建各种大小类型的,也可以构建主主群集、主备群集等。群集资源管理器在每一个群集节点的服务器上都运行,它们之间互相通信。一旦第一台服务器出故障了,运行在第二个节点的群集资源管理器就要不断地探测第一个节点,探测了几个周期之后,如果第一个节点都没有没有回应,就判断第一个节点完蛋了,那第二个节点就马上抢占资源。如果第一个节点故障处理好了,这时候工作在第一个节点上的群集资源管理器也会和其他节点的群集资源管理器通信,知道第二个节点资源成了主节点。
群集资源管理(CRM—— Cluster Resource Manager)
开源,适合各种大小(类型)群集管理(主 / 主、主 / 备)
基于资源级别的监测和恢复保证应用最大可用性
基础组件(Corosync、Heartbeat)实现群集各成员间通信和关系管理
Corosync:消息层组件(心跳传输),管理成员间关系、消息和仲裁
可使用共享存储或基于块设备的复制(DRBD)
pacemaker安装(两台节点) pacemaker成为了其他群集资源的控制程序,默认情况下,希望drbd在所有的服务器上都关上,谁启用、谁占有资源由pacemaker决定。不同节点的pacemaker彼此通信,先由第一个节点工作,由第一个节点的pacemaker启动下面drbd的服务,配置群集的ip地址,启用群集相应的应用资源。
1 2 3 4 5 sudo systemctl stop drbd sudo umount /srv/ sudo drbdadm down r0 sudo apt install -y pacemaker
使用corosync信息传递组件 所谓的底层的信息传递组件,就是在不同节点之间互相地发送心跳信息,互相确认。在构建成群集多个节点之间,会有大量的心跳信息周期性的、不间断的发送。这些心跳信息是用来监控彼此的状态的。只有通过网络实时监视,不停的探测彼此的状态,基于这种机制,其中有一主节点挂掉之后,其他节点就会发现,发现之后才会选取一个新的节点作为主节点。
pacemaker配置(两台节点) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 sudo vi /etc/corosync/corosync.conf totem{ version: 2 cluster_name: ubuntu secauth: off transport: udpu interface { ringnumber: 0 bindnetaddr: 172.30.91.3 broadcast: yes mcastport: 5405 } } nodelist { node { ring0_addr: 172.30.91.102 name: usrv01 nodeid: 1 } node { ring0_addr: 172.30.91.103 name: usrv02 nodeid: 2 } } quorum { provider: corosync_votequorum two_node: 1 wait_for_all: 1 last_man_standing: 1 auto_tie_breaker: 0 }
启动服务(两台节点) 1 2 sudo systemctl restart corosync sudo systemctl restart pacemaker
群集管理器的框架已经搭建好了,需要把之前的ip资源、磁盘资源等纳入到群集管理器中。
查看群集状态 1 2 3 4 sudo apt install crmsh sudo crm status
配置和创建群集资源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 sudo crm configure property stonish-enabled=false property no-quorum-policy=ignore primitive drbd_res ocf:linbit:drbd params drbd_resource=r0 op monitor interval=29s role=Master op monitor interval=31s role=Slave ms drbd_master_slave drbd_res meta master-max=1 master-node-max=1 clone-max=2 clone-node-max=1 notify=true primitive fs_res ocf:heartbeat:Filesystem params device=/dev/drbd0 directory=/var/www/html fstype=ext4 colocation fs_drbd_colo INFINITY: fs_res drbd_master_slave:Master order fs_after_drbd mandatory: drbd_master_slave:promote fs_res:start commit show quit
重启主节点测试自动切换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sudo systemctl restart corosync.service sudo systemctl restart pacemaker.service sudo systemctl restart corosync.service sudo systemctl restart pacemaker.service mount | grep drbd sudo drbd-overview
基于heartbeat信息传递组件创建MySQL群集 主机添加第二块网卡,用作DRBD数据复制
当有一个数据写入到第一个服务器上,按照DRBD使用协议的不同,如果使用的是C协议的话,C协议就要求数据必须在第一个节点完成写入,并且同时在第二个节点写入,两个节点都完成写入后才认为数据已经写入到硬盘,给应用返回响应。如果群集对外提供的服务比较繁忙,写入数据的操作非常的频繁,时时刻刻有大量的数据写到群集里。群集数据首先是写入到活动的第一个节点,这个节点的服务器会通过DRBD的磁盘复制机制,通过网络复制到第二个节点的磁盘上。当数据量比较少的时候,这一块网卡既要对外提供服务,又要完成数据的同步,数据量少的时候是完全可以接受的,性能上也不会有太多的影响。如果创建的是数据量访问大、像数据库应用服务的话,建议构建服务器群集时至少使用两块网卡,一块网卡对外提供应用服务,另一块网卡用作DRBD数据的同步。
实验环境: 两台Ubuntu server的节点:db1、db2
db1:添加1G存储硬盘,网络两个网卡,一个桥接模式,一个是内部网络
db2和db1一样
首先修改主机名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 sudo vi /etc/cloud/cloud.cfg preserve_hostname: true sudo vi /etc/hostname sudo vi /etc/hosts 172.16.0.1 db1 172.16.0.2 db2 sudo vi /etc/netplan/50-cloud-init.yaml network: ethernets: enp0s3: dhcp4: true enp0s8: addresses: [172.16.0.1/24] version: 2 sudo netplan apply
在db1和db2上安装服务 1 2 3 4 5 6 7 8 9 10 11 sudo echo -e 'n\np\n1\n\n\nw' | sudo fdisk /dev/sdb sudo apt install drbd8-utils heartbeat -y sudo systemctl start drbd sudo systemctl start heartbeat sudo systemctl enable drbd sudo systemctl enable heartbeat
群集配置文件,db1和db2都配置相同 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 sudo vi /etc/drbd.conf global { usage-count no; } common { syncer { rate 100M; } } resource r0 { protocol C; startup { wfc-timeout 15; degr-wfc-timeout 60; } net { cram-hmac-alg sha1; shared-secret "secret" ; } on db1 { device /dev/drbd0; disk /dev/sdb1; address 172.16.0.1:7788; meta-disk internal; } on db2 { device /dev/drbd0; disk /dev/sdb1; address 172.16.0.2:7788; meta-disk internal; } }
1 2 3 4 5 6 7 8 9 10 sudo vi /etc/ha.d/ha.cf keepalive 1 deadtime 10 initdead 60 auto_failback off bcast enp0s3 node db1 node db2
群集资源 1 2 3 4 5 6 sudo vi /etc/ha.d/haresources db1 192.168.1.200/24 drbddisk::r0 Filesystem::/dev/drbd0::/var/lib/mysql::ext4::noatime
身份认证 1 2 3 4 5 6 7 sudo vi /etc/ha.d/authkeys auth1 1 sha1 40bd001563085fc35165329ea1ff5c5ecbdbbeef echo -n 123 | sha1sum
设置身份认证的权限 1 sudo chmod 600 /etc/ha.d/authkeys
创建drbd磁盘资源 节点1
1 2 3 4 5 6 7 8 sudo drbdadm create-md r0 sudo systemctl restart drbd sudo drbdadm -- --overwrite-data-of-peer primary all sudo drbdadm primary r0 sudo mkfs.ext4 /dev/drbd0 sudo chmod 600 /etc/ha.d/authkeys sudo mkdir /var/lib/mysql
在节点2
1 2 3 4 5 6 7 8 9 sudo drbdadm create-md r0 sudo systemctl restart drbd sudo chmod 600 /etc/ha.d/authkeys sudo mkdir /var/lib/mysql
启动服务(BOTH) 1 sudo systemctl restart heartbeat
验证IP资源、硬盘资源 1 2 3 ip a sudo mount | grep drbd
安装MariaDB 1 2 3 sudo apt install mariadb-server sudo mysql_secure_installation
禁用MySQL服务自动启动
1 2 3 sudo systemctl disable mysql
停止备用节点服务
1 2 sudo systemctl stop mysql sudo rm -rf /var/lib/mysql/*
在第一个节点进行MySQL配置
1 2 3 4 5 sudo mysql create user 'root' @'192.168.1.%' identified by 'password' ; grant all privileges on *.* to 'root' @'192.168.1.%' with grant option; flush privileges; quit;
将节点修改侦听端口,两个都要修改
1 sudo sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mysql/mariadb.conf.d/*.cnf
将数据库服务资源加入到群集,两个节点都要做
1 2 3 sudo vi /etc/ha.d/haresources db1 192.168.1.200/24 drbddisk::r0 Filesystem::/dev/drbd0::/var/lib/mysql::ext4::noatime mysql
双节点重启服务(先db1后db2)
1 2 sudo systemctl restart heartbeat
测试 1 2 3 4 5 6 7 8 9 10 mysql -h 192.168.1.200 -u root -p create database test ; sudo reboot