4. CARP 协议
CARP (通用地址冗余协议) 是一个实现系统冗余的协议, 它的原理是让处于同一网段的一组主机(冗余组)共享一个IP地址, 这样在其中某台主机出现故障时,冗余组内的另一台主机可以马上接替它的工作。CARP还允许在系统间有一定程度的
负载共享 。尽管CARP最广泛的用处之一是创建冗余的防火墙, 但是CARP并非是仅能应用在防火墙上的协议。它还可以用来确保服务的连续性和/或网络服务的负载均衡。
起初, OpenBSD团队想推出一款免费的IETF标准协议, VRRP (Virtual Router Redundancy Protocol), 定义在 [
RFC3768], 和 HSRP (Hot Standby Router Protocol), 定义在 [
RFC2281]; 但是Cisco宣称对它们拥有专利权, 并且严正地通知OpenBSD社区Cisco将捍卫自己的VRRP专利 (参 [
CARP] 以获取更多的细节), 因此这就强迫OpenBSD开发者开发出了一个完全不同的、全新的、与VRRP竞争的协议。CARP是一个组播协议, 把几台物理计算机组合在一起,使用一个或几个虚拟地址。当然一个系统充当主力机并负责处理所有前往该(冗余)组的数据包; 其它系统(备份) 仅处于备用状态, 等着主力机一旦出现任何问题就取代其位置 (就像工作搭档一样...)。
在可配置的间隔周期内, 主力机在IP protocol number 112上广告它自己正常工作。 如果主力机出故障了, 冗余组内的其他主机将会开始广告。广告最频繁的主机就会成为主力机。当原主力机的故障解除后, 尽管可以尝试通过配置让它回来后仍旧是主力机,但是默认情况下它回来后会变成备用机 。
如你所见, CARP只创建和管理虚拟网络接口; 系统管理员可以选择用
pfsync(4) (我们将在后面讨论),
rsync 或者不管什么协议同步组内的数据。
4.1 配置参数
CARP配置可以通过
sysctl(8) 和
ifconfig(8) 命令完成。有三个
sysctl(3) 相关的变量:
net.inet.carp.allow
[INDENT]定义了该主机是否处理CARP数据包。默认处理;
[/INDENT]net.inet.carp.log
[INDENT]定义了是否记录CARP错误。有效范围是0~7, 与
syslog(3) 的优先权一致, 默认值是2 (也就是只记录CARP状态变化);
[/INDENT]net.inet.carp.preempt
[INDENT]如果设置为0 (默认), 当接收到其它主力机的广告后,这台主机不会尝试成为主力机。否则, 如果它的广告频率高于目前的主力机,它会尝试成为主力机。启用这个选项后,则会在一个接口出现故障时由其它接口接替其工作。事实上, 如果一个启用了CARP的物理接口出现故障, CARP会将所有该接口所属组的demotion counter加上1 (看下面) , 这样会允许在所有子网段选出新的主力机。
[/INDENT]用
ifconfig(8) 配置CARP的语法是:
代码: 全选
ifconfig carpN create
ifconfig carpN [advbase n] [advskew n] [balancing mode] \
[carpnodes vhid:advskew,vhid:advskew,...] [carpdev iface] \
[[-]carppeer peer_address] [pass passphrase] [state state] [vhid host-id]
carpN
[INDENT]
carp(4) 虚拟接口的名称。
[/INDENT]advbase, advskew
[INDENT]这些值决定两个CARP广告之间的时间间隔。间隔(以秒计)由公式(advbase + (advskew / 255)) 计算出来; 加大advbase (默认是1秒) 会减少网络通信,但是会推迟选举出新的主力机。更小的advskew值使主机广告的更频繁, 增加了其成为主力机的可能性。advbase的值必须在1到255之间, advskew值在0(默认值)和254之间;
[/INDENT]balancing
[INDENT]设置负载均衡模式 (我们将在后面讨论); 有效的模式是 arp, ip, ip-stealth 和 ip-unicast;
[/INDENT]carpnodes
[INDENT]用逗号分隔的一份vhid名单:advskew对实际上定义了在配置的CARP节点上如何均衡负载(参看
below 以获取更多的信息);
[/INDENT]carpdev
[INDENT]指定属于此冗余组的物理接口。默认情况下, CARP使用处于同一子网的物理接口作为虚拟接口;
[/INDENT]carppeer
[INDENT]允许你指定其它的CARP对的IP地址, 而不是使用默认的组播组;
[/INDENT]pass
[INDENT]与冗余组内其它启用了CARP的主机通讯时使用的密码。在所有成员的设置里这个密码必须是一致的;
[/INDENT]state
[INDENT]强制一个
carp(4) 接口进入一个指定的状态 (init, backup or master);
[/INDENT]vhid
[INDENT]虚拟主机ID。这是一个唯一的数值 (范围是1到255) ,用来分辨冗余组和网络上的其它节点。
[/INDENT]
4.1.1 The demotion counter
除了基本的配置以外, 以还可以利用
ifconfig(8) 命令来调整CARP的demotion counter, 它用来衡量冗余组内的某台主机是否更胜任主力机的工作 (数值越高则说明准备越不足).我们看一起细节。
CARP接口以组划分 (默认所有的
carp(4) 接口都属于 "carp" 接口组的成员) 且每个组分配到一个demotion counter, 这个值可以通过下面的命令查到:
代码: 全选
$ ifconfig -g carp
carp: carp demote count 0
demotion counter可提供方便地提供以下功能:
- 你想临时防止一台主机成为主力机: 例如, 系统重新启动后在启用网络以前, rc(8) 脚本会将 demotion counter数值在原来的基础上加上128,而在所有接口初始化完毕和启动后,再将这个数值在目前的基础上减掉 (demotion counter 不能被设置为一个绝对值, 而只能通过增加或减少其数值来控制):
- 文件/etc/rc
代码: 全选
ifconfig -g carp carpdemote 128
[ ... ]
ifconfig -g carp -carpdemote 128
- 你只想在一台主机的几个 carp(4) 接口(不是所有的, 因为这发生在一个接口出现故障而且已经启用了preempt)完成平滑的故障切换。下例中, 我们将故障切换设置到carp1和carp2接口,而对其它接口不做修改:
代码: 全选
# ifconfig carp1 group morituri
# ifconfig carp2 group morituri
# ifconfig morituri
carp1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
carp: MASTER carpdev sis0 vhid 1 advbase 1 advskew 100
groups: carp morituri
inet 1.2.3.4 netmask 0xffffff00 broadcast 1.2.3.255
carp2: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
carp: MASTER carpdev sis1 vhid 2 advbase 1 advskew 100
groups: carp morituri
inet 2.3.4.5 netmask 0xffffff00 broadcast 2.3.4.255
# ifconfig -g morituri
morituri: carp demote count 0
# ifconfig -g morituri carpdemote 50
# ifconfig -g morituri
morituri: carp demote count 50
有关CARP demotion counter的更多信息,请参阅For [
PFFAQ].
4.1.2 负载均衡
CARP用两种不同的方法完成CARP的主机间进站通讯的负载均衡: ARP balancing 和 IP balancing.
这两种方法都需要你先建立一个负载均衡组, 方法是对均衡组内的每台主机上的
carp(4) 接口进行设置; 每个VHID上的 advskew 都需要配置以便每台主机都可能成为一个独立的WHID的主力机 (请参阅下面的实例)。
ARP balancing的工作原理是将一个哈希函数应用到发送ARP请求的源MAC地址来来验证哪个VHID应该处理这个请求。这个ARP请求将由该VHID里
carp(4) 接口是主力的那台主机来回应。要启用ARP负载均衡可以通过使用
ifconfig(8) 命令在所有的主机上将balancing 选项设置为 "arp" 来实现; 例如:
代码: 全选
# ifconfig carp0 balancing arp carpnodes 1:0,2:100
IP负载均衡的工作原理和ARP负载均衡很类似, 但是,它是使用IP数据包的源地址和目标地址的哈希来验证哪个 VHID (和因而哪台主机) 应该接受这个数据包。
IP均衡需要到达CARP的地址的这个通讯被所有CARP主机接收。要启用IP负载均衡可以通过使用
ifconfig(8)命令将balancing选项设置为 "ip" 来实现; 这会导致CARP使用一个组播MAC地址, 强制交换机把进站通讯发送到冗余组内的所有节点上。例如:
代码: 全选
# ifconfig carp0 balancing ip carpnodes 1:0,2:100
或者, 你也可以将balancing选项设置为 "ip-stealth" (stealth模式), 这样可以防止主机以它们的虚拟MAC地址作为源来发送数据包; 这会防止交换机知道这个虚拟的MAC地址, 强迫其发送这个数据包到其所有的端口。最后, 如果你使用一个提供某些监视模式的集线器(hub)或者交换机(switch), 你可以将balancing设置为"ip-unicast"。
两种负载均衡方式的选择和取舍取决于系统网络环境: ARP均衡只能为本地网络的客户端工作,并不能跨越路由器实现通讯均衡, 因为路由的通讯总是将路由器的MAC地址作为其源地址。因此, 如果客户端在远程网络上, IP均衡是唯一的选择; IP均衡的唯一缺陷是发送到负载均衡的IP地址的那些通讯会被所有启用CARP的主机接收, 这回导致很大的网络负载。
4.2 参数配置
现在是在防火墙上配置CARP的时候了。为了试验两个略有不同的CARP配置, 我们将两个内部的防火墙 (Mickey和Minnie, 在LAN和DMZ之间) 设置为 active/stand-by 模式, 这种模式下只有一个系统过滤整个网络的通讯,另一个处于热备份状态; 两台外部的防火墙 (Donald和Daisy, 分开DMZ和internet)的工作模式则为 active/active 模式, 均衡负载。
我们瞄一眼防火墙地址, 就像我们在
network diagram 中已经看过的那样:
[table] |Mickey|Minnie|Virtual address
LAN|172.16.0.200|172.16.0.201|172.16.0.202
DMZ|172.16.240.200|172.16.240.201|172.16.240.202
pfsync|192.168.2.200|192.168.2.201|[/table]
[table] |Donald|Daisy|Virtual address
DMZ|172.16.240.100|172.16.240.101|172.16.240.102
Internet|172.16.250.100|172.16.250.101|172.16.250.102
pfsync |192.168.1.100|192.168.1.101|[/table]
4.2.1 Active/standby 配置
我们先从Mickey和Minnie开始: 首先, 我们需要创建 carp* 设备,然后用
ifconfig(8) 配置它们:
代码: 全选
mickey# ifconfig carp0 172.16.0.202/24 vhid 1 pass password1 advbase 1 advskew 0mickey
# ifconfig carp1 172.16.240.202/24 vhid 2 pass password2 advbase 1 advskew 0
代码: 全选
minnie# ifconfig carp0 172.16.0.202/24 vhid 1 pass password1 advbase 1 advskew 100minnie# ifconfig carp1 172.16.240.202/24 vhid 2 pass password2 advbase 1 advskew 100
我们创建了接口、分配了一个IP地址、一个虚拟主机ID (LAN是1, DMZ是2) 以及一个用于认证的密码 (也许这个密码不太安全...) 。 我们决定, 只要可能,无论何时Mickey都充当主力机; 这可以通过给Minnie设置一个高一些的 advskew 值(100)来实现,这样Minnie的广告间隔 (1 + 100 / 255) 高于Mickey的广告间隔 (1 + 0 / 255)。结果正像我们
前面 看到的, 广告最频繁的那台主机可以成为主力机。
而且, 通过在Mickey上将 net.inet.carp.preempt 设置为 "1" , 我们确保了Mickey会总是尝试成为主力机:
代码: 全选
mickey# sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 0 -> 1
要使上面的设置重新启动系统后永久有效, 我们只需要编辑Mickey上的 /etc/hostname.carp* 和 /etc/sysctl.conf 文件:
文件 /etc/hostname.carp0
代码: 全选
inet 172.16.0.202 255.255.255.0 172.16.0.255 vhid 1 pass password1 advbase 1 advskew 0
文件 /etc/hostname.carp1
代码: 全选
inet 172.16.240.202 255.255.255.0 172.16.240.255 vhid 2 pass password2 advbase 1 advskew 0
文件 /etc/sysctl.conf
代码: 全选
[...]
net.inet.carp.preempt=1
而在Minnie:
文件 /etc/hostname.carp0
代码: 全选
inet 172.16.0.202 255.255.255.0 172.16.0.255 vhid 1 pass password1 advbase 1 advskew 100
文件 /etc/hostname.carp1
代码: 全选
inet 172.16.240.202 255.255.255.0 172.16.240.255 vhid 2 pass password2 advbase 1 advskew 100
注意: 为了使CARP可以简单地应用到已经存在的网络上, CARP允许使用一台主机的物理地址作为整个冗余组的虚拟地址。
4.2.2 Active/active 配置
现在我们设置Donald和Daisy,先从其所在的DMZ接口开始。和从前一样, 我们将在每台机器上创建carp0设备, 不过这次, 为了确保负载均衡, 我们将使用 carpnodes 选项来指定两个不同的虚拟主机ID给接口 (VHIDs 3 和 4).
在VHID 3, 我们分别将Donald和Daisy的 advskew 设置为0和100: 这将确保 Donald成为i这个VHID的主力机; 而在VHID 4, 正好相反, 我们分别将Donald和Daisy的 advskew 设置为100和0, 这样是为了强制Daisy成为VHID4的主力机:
代码: 全选
donald# ifconfig carp0 172.16.240.102/24 balancing ip carpnodes 3:0,4:100 \
> pass password3
donald# sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 0 -> 1
代码: 全选
daisy# ifconfig carp0 172.16.240.102/24 balancing ip carpnodes 3:100,4:0 \
> pass password3
daisy# sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 0 -> 1
我们现在有两个带有同一IP地址的冗余组, 但是每个冗余组的主力机不同:
代码: 全选
donald# ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 01:00:5e:00:01:01
carp: carpdev rl1 advbase 1 balancing ip
state MASTER vhid 3 advskew 0
state BACKUP vhid 4 advskew 100
groups: carp
inet 172.16.240.102 netmask 0xffffff00 broadcast 172.16.240.255
inet6 fe80::2c0:a8ff:fe8e:b112%carp0 prefixlen 64 scopeid 0x5
代码: 全选
daisy# ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 01:00:5e:00:01:01
carp: carpdev rl1 advbase 1 balancing ip
state BACKUP vhid 3 advskew 100
state MASTER vhid 4 advskew 0
groups: carp
inet 172.16.240.102 netmask 0xffffff00 broadcast 172.16.240.255
inet6 fe80::219:d2ff:fe02:6469%carp0 prefixlen 64 scopeid 0x5
要确保重新启动后设置仍有效, 我们需要编辑Donald的启动文件:
文件 /etc/hostname.carp0
代码: 全选
inet 172.16.240.102 255.255.255.0 172.16.240.255 balancing ip carpnodes 3:0,4:100 pass password3
文件 /etc/sysctl.conf
代码: 全选
[...]
net.inet.carp.preempt=1
and Daisy:
文件 /etc/hostname.carp0
代码: 全选
inet 172.16.240.102 255.255.255.0 172.16.240.255 balancing ip carpnodes 3:100,4:0 pass password3
文件 /etc/sysctl.conf
代码: 全选
[...]
net.inet.carp.preempt=1
现在我们将在外部的网络接口上做同样的工作,也就是针对另两个Virtual Host IDs (VHIDs 5 and 6):
代码: 全选
donald# ifconfig carp1 172.16.250.102/24 balancing ip carpnodes 5:0,6:100 \
> pass password5
代码: 全选
daisy# ifconfig carp1 172.16.250.102/24 balancing ip carpnodes 5:100,6:0 \
> pass password5
然后编辑Donald上的启动文件:
文件/etc/hostname.carp1
代码: 全选
inet 172.16.250.102 255.255.255.0 172.16.250.255 balancing ip carpnodes 5:0,6:100 pass password5
然后是Daisy:
文件 /etc/hostname.carp1
代码: 全选
inet 172.16.250.102 255.255.255.0 172.16.250.255 balancing ip carpnodes 5:100,6:0 pass password5
尽管上面的配置仅包含了几台机器, 不过它可以轻易地扩展到32台主机。
最后的注意事项: 负载均衡不可能在两台主机间实现理想的 50/50 负载均摊, 因为CARP使用一个源和目标IP地址的哈希来决定哪个系统接收此数据包, 而不是根据系统的实际负载。