在遥远的年代,为了实现路由器的高可用性,cisco开发了hsrp,因为hsrp是私有协议,所以开源社区就自己出了一个vrrp。openbsd向来看不起linux,另辟蹊径推出了一个carp,而freebsd在9版本之前的carp移植自openbsd,可不知道为何到了10,freebsd又重新实现了一遍carp。开源社区真是精力旺盛到无以复加的地步。哦对了,pure-ftpd的作者还开发了一个ucarp,linux下的carp实现,不过已经好久没更新了。

这些协议工作原理都是类似的,都有一个浮动的虚拟IP(简称vip),在master和backup中漂移,然而有不少软件是无法监听一个未知的IP的,譬如openvpn、ipsec、tinc等vpn软件,那么有没有可能当vip漂移到某一台主机的时候,再启动vpn的进程?答案是肯定的,不过bsd和linux是两种不同的实现方式:freebsd和openbsd的carp不具备触发外界程序/脚本的能力,只能依靠第三方的链路状态检测工具来触发,freebsd中是devd,openbsd中是ifstated,而linux要方便一些,keepalived在实现vrrp的同时,还顺带提供了触发外界程序/脚本的能力。

下面举例说明如何利用keepalived来实现tinc的(部分)高可用性

  • keepalived/notify配置

/etc/keepalived/keepalived.conf
...
vrrp_instance VI_eth0{
	...
	notify /path/to/script.sh
	...
}
…
/path/to/script.sh
#!/bin/bash

TYPE=$1
NAME=$2
STATE=$3 (1)

case $STATE in
	"MASTER") /bin/echo "start tinc for ${STATE}" | /usr/bin/logger -p local6.notice -t change_tincState
			  exit 0
			  ;;
	"BACKUP") /bin/echo "stop tinc for ${STATE}" | /usr/bin/logger -p local6.notice -t change_tincState
			  /etc/init.d/tinc stop
			  exit 0
			  ;;
	"FAULT")  /etc/init.d/tinc stop
			  exit 0
			  ;;
	*)        echo "Unknown state ${STATE} for VRRP ${TYPE} ${NAME}"
			  exit 1
			  ;;
esac
1 一共有3种状态:MASTER、BACKUP和FAULT,notify会将状态通过参数$3传给该脚本,实现了tinc在MASTER中start,在BACKUP/FAULT中stop的功能

tinc是一个mesh vpn,可惜跟大多数vpn一样,自身都不支持high availability[1]。不过利用keepalived可以部分实现ha,为什么说是部分实现,因为keepalived也无法同步状态,只是能简单的确保两个tinc进程之间,永远有一个处于在线状态,工作原理是:

  1. 远端网关(ext gw)安装了一个tinc(ext tinc),本端网关(loc gw1)安装了一个tinc(loc tinc1),两个tinc之间构建了一条加密隧道,里面跑着一些关键性应用;

  2. 它们存在单点故障,先不考虑远端,先在本端新增一个网关(loc gw2),并安装一个tinc(loc tinc2),它跟loc tinc1使用相同的配置;

  3. 当loc gw1的vrrp状态是master的时候,keepalived通过notify启动loc tinc1,监听vip。此时,因为loc gw2的vrrp状态为backup,所以loc tinc2处于stop状态(即便之前处于start状态,也会被keepalived的notify给stop掉),keepalived的notify确保了tinc1和tinc2两者中只有一个处于start状态,避免了冲突;

  4. ext tinc和loc tinc{1|2}通过心跳包来检测各自的状态,一旦发现中断后将重新发起连接请求;

  5. 当loc tinc{1|2} stop,loc tinc{2|1} start,由于它们两者之间的状态无法同步,所以ext tinc和loc tinc{1|2}的隧道丢失,跑在隧道里面的stateful connection(对,说的就是你,tcp)也丢失,不过由于ext tinc和loc tinc{1|2}会立刻新建一个新的隧道,所以应用程序尝试重连又可以正常工作。所以说,对于tcp而言,keepalived能实现部分HA;

  6. 对于udp而言,则是另外一个场景:由于udp是stateless connection,因而一般由上层应用程序来维护状态,所以这类程序对tinc隧道的状态切换不敏感,keepalived能为这类程序实现传统意义上的HA;

这就是keepalived/devd/ifstated这类程序的价值所在。也许有一天freebsd/openbsd能在carp协议里面添加触发外界程序/脚本的功能,只是不管是学院派freebsd,还是固执的openbsd,修改一个协议的可能性微乎其微。所以,还是安心的用devd/ifstated吧。


1. vpn实现ha非常困难,详见strongswan ha