引:
近期工作上安排我做一个双链路热备系统,沿着这个轨迹,从vrrp开始,我设计了一个LVrrp的协议,它完全适用于非交叉非全互联的多链路热备和负载均衡的情况,所谓的非交叉和非全互联指的是链路两端是固定的,并且任意两条链路之间在两端不存在交叉情况。
事后得知,有时候不是我们不想做某件事,而是因为我们做不好!事后还得知,其实RSVP的设计过程不过如此。还有一个收获,诸如ospf协议并不能解决所有的问题,有的时候,在特定的环境,自己设计一个协议解决问题要比使用标准协议好得多,关键是, 如果我们能坚持把设计做下去并且广泛应用,这个协议也能成为标准协议。
一.初始需求:
拓扑:
B1----------L1---------F1
switch--| |----switch
B2----------L2---------F2
L1和L2之间互相热备,F1和B2不连通,F2和B1不连通,要求L1和L2之间热备份
二.方案评析
方案一.
1. F1和F2之间开启vrrp,B1 和B2之间开启vrrp;
2. F1和B1之间互相检测,F2和B2之间互相检测,规则如下:
2.1. 如果F成为了backup/fault,那么通知B也成为backup,反之亦然;
2.2. 如果F检测到B为backup/fault,那么如果自己是master,则切换到backup,反之亦然;
评价:
1. 四台机器完全对称,配置简单,但是容易死锁,也就是两个F和两个B交叉切换,解决办法就是不要以确定的时间段定时触发F和B之间的检测,而是在一个时间段范围内以随机的时间段为间隔触发F和B之间的检测。或者使用方案二,不对称的配置方式。
2. 不易扩展。不容易扩展到链路加权最优化选择,也不容易扩展到N(N>2)条链路之间的热备或者加权最优化选择。
方案二.
0.拓扑:
B1----------L1---------F1
switch--| |----switch
B2----------L2---------F2
1. 将F1和B1划为一组RG1,F2和F2划为一组RG2,这种组的性质是要么全工作,要么全不工作。
2. 将F1和F2划为一组VG1,B1和B2划为一组VG2,这种组的性质是只有一个工作。
3. VG中的两个主机之间开启vrrp,RG的两个主机之间开启互相检测,检测规则如下:
3.1. RG中的成员为串联关系,只要有一个不工作,该链路就完蛋了,因此手工选择一个为主控即可,这就是不对称的原因之所在,没有必要在RG组中协商,一个down掉整路down。因此选择B为主控;
3.2. 只要L通畅,则开启两端的vrrp,L不通畅,则停止两端的vrrp。此原则的原因是如果L都不通,开启两端的vrrp只起到干扰作用;
3.3. 主控设备检测被控设备:如果被控设备不是master且主控设备是master,则主控设备放弃master;
3.4. 被控设备检测主控设备:如果主控设备是fault且被控设备是master,则被控设备放弃master;
3.5. 其实3.4是不合理的,被控设备按道理是不应该检测主控设备的,正确的方式应该是主控设备发送“放弃master”的命令给被控设备,被控设备收到后判断如果自己是master则放弃master;
评价:
1. 将所有控制集中在主控设备端,被控设备不再参与vrrp状态的检测和控制,被控设备仅仅参与链路L的连通性检测从而控制是否开启vrrp,为何不采取主控设备发出启动vrrp的命令给被控设备,被控设备方启动vrrp呢,因为这样过于复杂,被控设备需要设置接收命令的回调和超时时间,如果超时时间到了没有收到命令或者保活包,则停止vrrp,这实在没有必要。
2. 收敛快 。由于只靠主控设备一端来控制链路的选择,而控制的依据完全是根据成熟的vrrp协议,不会造成交叉切换这种死锁。链路选择的完全等同于一个vrrp虚拟组中成员的master选择,链路本身成了vrrp虚拟组成员的“挂件”。
3. 可扩展性超强。
3.1. 扩展到N(>2)条链路的情况。
拓扑:
|---B1----------L1---------F1----| vidn
|---B2----------L2---------F2----| vidn
switch--|……………………………………… … … … |--switch
|---Bn----------Ln---------Fn-----| vidn
图中:n均为正整数,标示vrrp虚拟组号
主控设备端的vrrp虚拟组将完全依照被控设备端的vrrp组设备的状态进行切换:
3.1.1. 由于被控设备端的vrrp组中最多只有一个master;
3.1.2. 主控设备的vrrp组中的master成员根据原则3.3将放弃master,最终根据下列新增原则快速收敛到一条稳定链路;
3.1.2.1. 主控设备检测被控设备时,只要被控设备的状态不为master且主控设备状态为master,则重启vrrp时优先级递减,为防止放弃master的成员在收敛到稳定状态前再次当选master;
3.1.2.2. 主控设备检测被控设备时,只要被控设备的状态为master且主控设备状态不为master,则将主控设备vrrp优先级递增,然后重启vrrp。
3.1.3 .由此可见,被控设备虽然叫做“被控”,实则主导着整个切换过程,不管是双链路热备还是多链路热备都成立,状态检测不对称的集中在一端,有利于避免死锁和交叉切换。
3.2. 扩展到加权最优选路
拓扑:
|---B1----------L1---------F1----| vidm-vidn
|---B2----------L2---------F2----| vidx-vidy
switch--|……………………………………… … … |--switch
|---Bn----------Ln---------Fn----|…
图中:m,n,x,y均为正整数,标示vrrp虚拟组号
这种扩展类似于基于最短路径的路由协议(比如OSPF),然而却比其更轻量,多条链路此时更多的是一种负载均衡的关系而不是热备。思路如下:
3.2.1. 在主控设备和被控设备两端每台设备各配置最多N个vrrp组,其中N最好等于链路个数(也可以大于)。我们期望的是每一条链路跑一种专有的应用.
3.2.2. 基于每一个vrrp组进行主控设备到被控设备的状态探测和被控设备到主控设备的fault状态探测,其中主控设备到被控设备的探测包中包含了加权信息(比如当前带宽,当前缓冲区大小,速率,已被选为master链路的vid数组等);
3.2.3. 主控设备根据加权值发送命令给被控设备,使被控设备调整vrrp优先级(调整的优先级是基于权值的单调函数);
3.2.4. 按照3.2.1.1和3.2.1.2的原则收敛每一个vrrp组的master链路。
3.3. 除了3.2.所述的之外,还要有N个用户进程运行在每一个主控设备端,它们使用基于vrrp组的相同的配置文件,管理每一个应用的主链路,备选链路,二级备选链路等,配置文件格式如下:
App=源ip 目的ip ip层tos 源端口 目的端口 传输层协议/协议 [首选vid 次选vid …] #vid为vrrp的vid号
#
如此一来,可以实现为特定的应用分配最佳的链路,由于每一个vrrp组的虚拟ip是不同的,因此可以根据应用首选vid对应的虚拟ip为该应用的数据包设置路由和netfilter规则,当首选vid全部down掉之后,则根据备选vip对应的虚拟ip设置路由和netfilter规则。
4.优点
所有的配置完全根据vrrp动态调整,确保负载根据配置的不同被均衡到不同的链路Ln
5.再扩展
前文所述的Bn和Fn中间只有一根网线,由于主控设备到被控设备的探测完全是基于ip+protocol+port的,因此L实则一条网路层通路,L中间可以存在一个云!
三.基于链路的vrrp
vrrp是提供主机之间的冗余备份的,本文将其扩展到了链路上,最终可以实现了一个可灵活配置的负载均衡方案。vrrp协议运作原理是在vrrp虚拟组之间通过组播进行协商,扩展到链路上的vrrp保持此优秀原理不变,将这些vrrp虚拟组成员作为被控设备存在,与每个组成员相连的“对端”设备作为主控设备,主控设备通过检测被控设备的状态被动的调整自己的vrrp状态,最终收敛于与被控设备一致的vrrp状态。
1.LVrrp的组件
被控组: 运行标准vrrp,成员间完全按照vrrp协议运作,稳定后同时只有一个master
主控组: 运行标准vrrp,每个成员通过LVrrp协议(见下)查询被控组状态,根据规则(见下)调整自己的vrrp状态,稳定后同时只有一个master
并联组: 主控组和被控组
串联组: 一条链路L上两端的主控设备和被控设备组成的组,稳定后同时生效或者同时失效
规则: 同一主控组的所有主控设备查询与之对应串联组的被控设备状态:
0) 如果状态检测超时未返回,则停止本端的vrrp
1) 如果被控设备不为master且主控设备为master,主控设备放弃master,且优先级递减
2) 如果被控设备为master且主控设备不为master,主控设备优先级递增,重启vrrp
3) 如果主控设备为fault,则通知被控设备,被控设备需要判断如果自己为master,则重启vrrp(也可以由被控设主控设备的fault状态)
上述规则1)和2)在只有两个串联组的情况下可优化为以下一条规则:
1)如果被控设备不为master且主控设备为master,主控设备重启vrrp
2.LVrrp协议
1)状态查询协议
查询对端的vrrp状态
2)状态通知协议
通知对端本端的vrrp状态
3)链路权值通知协议
通知对端该链路的权值
4)优先级调整协议
通知对端更改自己的vrrp优先级
5)链路权值计算函数
将感兴趣的链路情况作为输入,输出一个权值,这个函数可以根据不同的偏好和实施侧重点动态设置
6)链路权值与被控组vrrp优先级映射
将链路情况映射到vrrp优先级,从而通知被控端调整vrrp优先级,以便被控端重新选举,之后主控端根据被控端的状态进行被动调整
7)检测间隔计算函数
为了防止意外发生,比如交叉切换(虽然理论上不会出现,但是必须有容错手段),检测间隔最好不要步调一致(当然你可以设置成步调一致,不过引起共振就麻烦了,类似士兵过桥,不过步调一致在正常情况下效率更高),最好每次计算下一次间隔时都在m~n之间随机选择一个间隔,随机化等待时间是一种自适应解锁机制,有些死锁情况可以自行解开。并且如果链路失效或者负载变化的可能性不大,这个间隔可以设置的长一些,总之它是一个现场情况的函数。
8)错误处理
如果获取对端或者本端的vrrp状态出错(比如没有获取到),要进行处理,比较推荐的方式是切换次数最小化,比如在获取本端状态出错时,是将本端vrrp状态设为master,还是别的,或者返回一个none?本着切换次数最小化的原则,不能设置为master,因为本端为master的话,根据规则1)切换的可能性很大…此时不切换,等待下一轮probe是最好的办法,说不定下一轮检测时就好了。
四.后记
近期工作上安排我做一个双链路热备系统,沿着这个轨迹,从vrrp开始,我设计了一个LVrrp的协议,它完全适用于非交叉非全互联的多链路热备和负载均衡的情况,所谓的非交叉和非全互联指的是链路两端是固定的,并且任意两条链路之间在两端不存在交叉情况。
事后得知,有时候不是我们不想做某件事,而是因为我们做不好!事后还得知,其实RSVP的设计过程不过如此。还有一个收获,诸如ospf协议并不能解决所有的问题,有的时候,在特定的环境,自己设计一个协议解决问题要比使用标准协议好得多,关键是, 如果我们能坚持把设计做下去并且广泛应用,这个协议也能成为标准协议。
一.初始需求:
拓扑:
B1----------L1---------F1
switch--| |----switch
B2----------L2---------F2
L1和L2之间互相热备,F1和B2不连通,F2和B1不连通,要求L1和L2之间热备份
二.方案评析
方案一.
1. F1和F2之间开启vrrp,B1 和B2之间开启vrrp;
2. F1和B1之间互相检测,F2和B2之间互相检测,规则如下:
2.1. 如果F成为了backup/fault,那么通知B也成为backup,反之亦然;
2.2. 如果F检测到B为backup/fault,那么如果自己是master,则切换到backup,反之亦然;
评价:
1. 四台机器完全对称,配置简单,但是容易死锁,也就是两个F和两个B交叉切换,解决办法就是不要以确定的时间段定时触发F和B之间的检测,而是在一个时间段范围内以随机的时间段为间隔触发F和B之间的检测。或者使用方案二,不对称的配置方式。
2. 不易扩展。不容易扩展到链路加权最优化选择,也不容易扩展到N(N>2)条链路之间的热备或者加权最优化选择。
方案二.
0.拓扑:
B1----------L1---------F1
switch--| |----switch
B2----------L2---------F2
1. 将F1和B1划为一组RG1,F2和F2划为一组RG2,这种组的性质是要么全工作,要么全不工作。
2. 将F1和F2划为一组VG1,B1和B2划为一组VG2,这种组的性质是只有一个工作。
3. VG中的两个主机之间开启vrrp,RG的两个主机之间开启互相检测,检测规则如下:
3.1. RG中的成员为串联关系,只要有一个不工作,该链路就完蛋了,因此手工选择一个为主控即可,这就是不对称的原因之所在,没有必要在RG组中协商,一个down掉整路down。因此选择B为主控;
3.2. 只要L通畅,则开启两端的vrrp,L不通畅,则停止两端的vrrp。此原则的原因是如果L都不通,开启两端的vrrp只起到干扰作用;
3.3. 主控设备检测被控设备:如果被控设备不是master且主控设备是master,则主控设备放弃master;
3.4. 被控设备检测主控设备:如果主控设备是fault且被控设备是master,则被控设备放弃master;
3.5. 其实3.4是不合理的,被控设备按道理是不应该检测主控设备的,正确的方式应该是主控设备发送“放弃master”的命令给被控设备,被控设备收到后判断如果自己是master则放弃master;
评价:
1. 将所有控制集中在主控设备端,被控设备不再参与vrrp状态的检测和控制,被控设备仅仅参与链路L的连通性检测从而控制是否开启vrrp,为何不采取主控设备发出启动vrrp的命令给被控设备,被控设备方启动vrrp呢,因为这样过于复杂,被控设备需要设置接收命令的回调和超时时间,如果超时时间到了没有收到命令或者保活包,则停止vrrp,这实在没有必要。
2. 收敛快 。由于只靠主控设备一端来控制链路的选择,而控制的依据完全是根据成熟的vrrp协议,不会造成交叉切换这种死锁。链路选择的完全等同于一个vrrp虚拟组中成员的master选择,链路本身成了vrrp虚拟组成员的“挂件”。
3. 可扩展性超强。
3.1. 扩展到N(>2)条链路的情况。
拓扑:
|---B1----------L1---------F1----| vidn
|---B2----------L2---------F2----| vidn
switch--|……………………………………… … … … |--switch
|---Bn----------Ln---------Fn-----| vidn
图中:n均为正整数,标示vrrp虚拟组号
主控设备端的vrrp虚拟组将完全依照被控设备端的vrrp组设备的状态进行切换:
3.1.1. 由于被控设备端的vrrp组中最多只有一个master;
3.1.2. 主控设备的vrrp组中的master成员根据原则3.3将放弃master,最终根据下列新增原则快速收敛到一条稳定链路;
3.1.2.1. 主控设备检测被控设备时,只要被控设备的状态不为master且主控设备状态为master,则重启vrrp时优先级递减,为防止放弃master的成员在收敛到稳定状态前再次当选master;
3.1.2.2. 主控设备检测被控设备时,只要被控设备的状态为master且主控设备状态不为master,则将主控设备vrrp优先级递增,然后重启vrrp。
3.1.3 .由此可见,被控设备虽然叫做“被控”,实则主导着整个切换过程,不管是双链路热备还是多链路热备都成立,状态检测不对称的集中在一端,有利于避免死锁和交叉切换。
3.2. 扩展到加权最优选路
拓扑:
|---B1----------L1---------F1----| vidm-vidn
|---B2----------L2---------F2----| vidx-vidy
switch--|……………………………………… … … |--switch
|---Bn----------Ln---------Fn----|…
图中:m,n,x,y均为正整数,标示vrrp虚拟组号
这种扩展类似于基于最短路径的路由协议(比如OSPF),然而却比其更轻量,多条链路此时更多的是一种负载均衡的关系而不是热备。思路如下:
3.2.1. 在主控设备和被控设备两端每台设备各配置最多N个vrrp组,其中N最好等于链路个数(也可以大于)。我们期望的是每一条链路跑一种专有的应用.
3.2.2. 基于每一个vrrp组进行主控设备到被控设备的状态探测和被控设备到主控设备的fault状态探测,其中主控设备到被控设备的探测包中包含了加权信息(比如当前带宽,当前缓冲区大小,速率,已被选为master链路的vid数组等);
3.2.3. 主控设备根据加权值发送命令给被控设备,使被控设备调整vrrp优先级(调整的优先级是基于权值的单调函数);
3.2.4. 按照3.2.1.1和3.2.1.2的原则收敛每一个vrrp组的master链路。
3.3. 除了3.2.所述的之外,还要有N个用户进程运行在每一个主控设备端,它们使用基于vrrp组的相同的配置文件,管理每一个应用的主链路,备选链路,二级备选链路等,配置文件格式如下:
App=源ip 目的ip ip层tos 源端口 目的端口 传输层协议/协议 [首选vid 次选vid …] #vid为vrrp的vid号
#
如此一来,可以实现为特定的应用分配最佳的链路,由于每一个vrrp组的虚拟ip是不同的,因此可以根据应用首选vid对应的虚拟ip为该应用的数据包设置路由和netfilter规则,当首选vid全部down掉之后,则根据备选vip对应的虚拟ip设置路由和netfilter规则。
4.优点
所有的配置完全根据vrrp动态调整,确保负载根据配置的不同被均衡到不同的链路Ln
5.再扩展
前文所述的Bn和Fn中间只有一根网线,由于主控设备到被控设备的探测完全是基于ip+protocol+port的,因此L实则一条网路层通路,L中间可以存在一个云!
三.基于链路的vrrp
vrrp是提供主机之间的冗余备份的,本文将其扩展到了链路上,最终可以实现了一个可灵活配置的负载均衡方案。vrrp协议运作原理是在vrrp虚拟组之间通过组播进行协商,扩展到链路上的vrrp保持此优秀原理不变,将这些vrrp虚拟组成员作为被控设备存在,与每个组成员相连的“对端”设备作为主控设备,主控设备通过检测被控设备的状态被动的调整自己的vrrp状态,最终收敛于与被控设备一致的vrrp状态。
1.LVrrp的组件
被控组: 运行标准vrrp,成员间完全按照vrrp协议运作,稳定后同时只有一个master
主控组: 运行标准vrrp,每个成员通过LVrrp协议(见下)查询被控组状态,根据规则(见下)调整自己的vrrp状态,稳定后同时只有一个master
并联组: 主控组和被控组
串联组: 一条链路L上两端的主控设备和被控设备组成的组,稳定后同时生效或者同时失效
规则: 同一主控组的所有主控设备查询与之对应串联组的被控设备状态:
0) 如果状态检测超时未返回,则停止本端的vrrp
1) 如果被控设备不为master且主控设备为master,主控设备放弃master,且优先级递减
2) 如果被控设备为master且主控设备不为master,主控设备优先级递增,重启vrrp
3) 如果主控设备为fault,则通知被控设备,被控设备需要判断如果自己为master,则重启vrrp(也可以由被控设主控设备的fault状态)
上述规则1)和2)在只有两个串联组的情况下可优化为以下一条规则:
1)如果被控设备不为master且主控设备为master,主控设备重启vrrp
2.LVrrp协议
1)状态查询协议
查询对端的vrrp状态
2)状态通知协议
通知对端本端的vrrp状态
3)链路权值通知协议
通知对端该链路的权值
4)优先级调整协议
通知对端更改自己的vrrp优先级
5)链路权值计算函数
将感兴趣的链路情况作为输入,输出一个权值,这个函数可以根据不同的偏好和实施侧重点动态设置
6)链路权值与被控组vrrp优先级映射
将链路情况映射到vrrp优先级,从而通知被控端调整vrrp优先级,以便被控端重新选举,之后主控端根据被控端的状态进行被动调整
7)检测间隔计算函数
为了防止意外发生,比如交叉切换(虽然理论上不会出现,但是必须有容错手段),检测间隔最好不要步调一致(当然你可以设置成步调一致,不过引起共振就麻烦了,类似士兵过桥,不过步调一致在正常情况下效率更高),最好每次计算下一次间隔时都在m~n之间随机选择一个间隔,随机化等待时间是一种自适应解锁机制,有些死锁情况可以自行解开。并且如果链路失效或者负载变化的可能性不大,这个间隔可以设置的长一些,总之它是一个现场情况的函数。
8)错误处理
如果获取对端或者本端的vrrp状态出错(比如没有获取到),要进行处理,比较推荐的方式是切换次数最小化,比如在获取本端状态出错时,是将本端vrrp状态设为master,还是别的,或者返回一个none?本着切换次数最小化的原则,不能设置为master,因为本端为master的话,根据规则1)切换的可能性很大…此时不切换,等待下一轮probe是最好的办法,说不定下一轮检测时就好了。
四.后记
为什么不使用分布式的算法,为什么放弃了方案一?实际上如果能用分布式算法的话,当然更好,方案一明显就是一种没有处理异常情况的分布式算法。有的时候,我们不使用某种方式并不是因为那种方式不好,而是因为大多数情况我们用不好它,它太复杂了。计算机上有 n个处理器核,将一个计算分布在n个核上并且不处理异常是很容易的事,然而任何人都知道,处理同步,死锁等问题的代码量将会是总代码量的80%以上,这又是一个80/20原则!
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271044