返回 登录
0

理解 OpenStack 高可用(2):虚拟路由冗余协议(下)

阅读2194

在上一篇文章中我们介绍了VRRP的基础概念。本文将详细介绍VRRP的详细实现。

2. Neutron Juno 版本中 VRRP 的实现

OpenStack HA (1)概述 一文中谈到,Neutron L3 Agent 在 Juno 版本中添加了两种 HA 机制的实现:VRRP 机制(blueprint, Spec,Wiki)和 DVR 机制。VRRP 机制就是借助实现 VRRP 协议的软件,来保证 Neutron L3 Agent 的高可用性。这需要多方面的实现:

创建 HA Router 时,创建多个 router 实例部署到不同物理服务器上的 L3 Agent 中。这个实现需要修改 Neutron schedulers。

借助实现 VRRP 的软件,保证多个 L3 Agent 的 HA,即其中一个是 Master,其他是 Backup。由 Master 向虚机提供路由服务。在 Master 故障时,某个 Standby 被选举为新的 Master,接替之前的 Master。目前的 Neutron VRRP 实现使用的是 Keepalived。

路由器故障切换时保持已建立的连接。Neutron 会使用 conntrack 来实现该功能。

2.1 创建 HA Router

neutron server 上:

(1)类似普通的 router,首先执行 DB 操作在 DB 中创建一个 router。

(2)检查 router 所在的 tenant 中是否存在 HA Network,如果不存在的话,则创建名称为 “HA network tenant”+ tenant_id 的 network,以及该 network 中的名字为 “HA subnet tenant” + tenant_id, cidr 为 cfg.CONF.l3_ha_net_cidr 的 subnet。其余的参数皆为默认或者null。

s1@controller:~$ neutron net-show bc5501ed-56ee-4d32-974b-02279fb35c32
+---------------------------+----------------------------------------------------+
| Field                     | Value                                              |
+---------------------------+----------------------------------------------------+
| admin_state_up            | True                                               |
| id                        | bc5501ed-56ee-4d32-974b-02279fb35c32               |
| name                      | HA network tenant 74c8ada23a3449f888d9e19b76d13aab |
| provider:network_type     | gre                                                |
| provider:physical_network |                                                    |
| provider:segmentation_id  | 1                                                  |
| router:external           | False                                              |
| shared                    | False                                              |
| status                    | ACTIVE                                             |
| subnets                   | 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b               |
| tenant_id                 |                                                    |
+---------------------------+----------------------------------------------------+

s1@controller:~$ neutron subnet-show 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b
+-------------------+------------------------------------------------------+
| Field             | Value                                                |
+-------------------+------------------------------------------------------+
| allocation_pools  | {"start": "169.254.192.1", "end": "169.254.255.254"} |
| cidr              | 169.254.192.0/18                                     |
| dns_nameservers   |                                                      |
| enable_dhcp       | False                                                |
| gateway_ip        |                                                      |
| host_routes       |                                                      |
| id                | 2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b                 |
| ip_version        | 4                                                    |
| ipv6_address_mode |                                                      |
| ipv6_ra_mode      |                                                      |
| name              | HA subnet tenant 74c8ada23a3449f888d9e19b76d13aab    |
| network_id        | bc5501ed-56ee-4d32-974b-02279fb35c32                 |
| tenant_id         |                                                      |
+-------------------+------------------------------------------------------+

注意,当 tenant 最后一个 HA Router 被删除的时候,该 HA network/subnet 不会被自动删除,这里有个 bug。看起来还没有被 fixed,目前只能手动删除。

(3)分配一个可用的 VRRP ID,该 ID 为 1 - 255 之间。

(4)创建与部署的 router 实例相同数量的 port 作为 HA Interfaces,每个 L3 Agent 上使用一个。

{'tenant_id': '',
             'network_id': network_id,
             'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
             'mac_address': attributes.ATTR_NOT_SPECIFIED,
             'admin_state_up': True,
             'device_id': router_id,
             'device_owner': constants.DEVICE_OWNER_ROUTER_HA_INTF,
             'name': constants.HA_PORT_NAME % tenant_id}})

比如需要部署的 router 的实例数目为2 的情况下,会分配 2 个port,分别用于各 router network namespace 中 keepalived 之间的通信:

s1@controller:~$ neutron port-list | grep 169.254.192
| 20894a79-b668-44d9-bf42-ef6bc2981960 | HA port tenant 74c8ada23a3449f888d9e19b76d13aab | fa:16:3e:09:29:d4 | {“subnet_id”: “2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b”, “ip_address”: “169.254.192.2”} |
| a8a3a49f-c175-4ea6-9342-240ef646e99c | HA port tenant 74c8ada23a3449f888d9e19b76d13aab | fa:16:3e:84:24:7f | {“subnet_id”: “2ce1ff9d-6889-41cb-8d6b-18e7ccd67a7b”, “ip_address”: “169.254.192.1”} |

(5)schedule router。具体参见 2.1.2 章节.

(6)向待部署 router 实例的每个 L3 Agent 发送 RPC Cast “routers_updated” 消息

图片描述

每个 L3 Agent 上:

(1)循环运行的 routers_updated 方法将待处理的每个 router 加入到内部 queue 中,等待被 L3 Agent 启动的循环线程处理

(2)处理过程可以参考我 这篇文章 的 3.1 部分

(3)在完成常规的 router 添加操作后,调用 process_ha_router_added 执行 HA 配置部分

(3.1)将 HA Port plug 到 OVS 的 br-int 上,并设置 interface 的三层属性,比如 IP, gateway, route 等。

Bridge br-int
  Port "ha-20894a79-b6"
            tag: 7
            Interface "ha-20894a79-b6"
                type: internal

(3.2)准备 keepalived

(3.2.1)生成一个 keepalived 实例。该实例的初始状态为 Backup,非抢占式,VRRP 通告间隔2秒,优先级为 50。

instance = keepalived.KeepalivedInstance('BACKUP', interface_name, ri.ha_vr_id, ha_port_cidr, nopreempt=True, advert_int=self.conf.ha_vrrp_advert_int, priority=ri.ha_priority)

(3.2.2)通过上面第三步分配的 VRRP ID,定位到 VRRP Group;然后将 3.2.1 中的 keepalived instance 加入到 该 group 。

(3.2.3)将 group 和 instance 保存到 keepalived config 中。

(4)调用 _add_keepalived_notifiers 方法来添加 neutron 需要的 keepalived notifiers。具体见 2.1.3 章节。

(5)调用 process_router 方法继续处理该 route。对于HA Router,会首次启动新的或者重启已有的 keepalived 进程。

(5.1)生成 keepalived 配置。配置文件的位置可以由配置项 vrrp_confs 指定,其默认值为 “$state_path/vrrp”。还可以使用配置项 ha_vrrp_auth_type (默认 ‘PASS’) 和 ha_vrrp_auth_password (默认 none)指定 VRRP 组播的认证方法,以及 使用配置项 ha_vrrp_advert_int (默认值 2) 指定 VRRP 广播发送的时间间隔。

vrrp_sync_group VG_1 { # VRRP ID 为 1
    group {
        VR_1
    }
    notify_master "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh"
    notify_backup "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh"
    notify_fault "/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_fault.sh"
}
vrrp_instance VR_1 {
    state BACKUP
    interface ha-20894a79-b6 # 实例绑定的网卡,因为在配置虚拟 IP 的时候必须是在已有的网卡上添加的
    virtual_router_id 1      # VRRP ID,每个 tenant HA Router 一个 ID
    priority 50              # 该 HA Router instance 的 优先级,根据这个选举 Master
    nopreempt                # 使用非抢占模式
    advert_int 2             # VRRP 广播发送间隔,单位秒
    track_interface {        # 跟踪接口,设置额外的监控
        ha-20894a79-b6
    }
    virtual_ipaddress {      # 该 VRRP 的 VIP,格式为 HA Network CIDR 最后一位被 VID 替换,该例子中,CIDR 为 “169.254.0.1/24”, VID 为 1
        169.254.0.1/24 dev ha-20894a79-b6
    }
}

keepalived 各种配置说明:

配置 说明 neutron 用法
notify master/backup/fault 表示当该 VRRP 节点切换到 master/backup/fault 状态时,要执行的脚本 见本文 2.1.3 章节
state state 指定 instance(Initial) 的初始状态,就是说在配置好后,这台服务器的初始状态就是这里指定的,但这里指定的不算,还是得要通过竞选通过优先级来确定,里如果这里设置为master,但如若他的优先级不及另外一台,那么这台在发送通告时,会发送自己的优先级,另外一台发现优先级不如自己的高,那么他会就回抢占为master neutron 将所有 keeplived 实例的初始状态都设置为 BACKUP,而且都使用默认优先级 50。这样实际上,一般来说,哪个 L3 Agent 先起来,它的 keepalived 实例就是  Master 了。
interface VRRP 实例绑定的网卡,在此网卡上添加 VIP 作为 secondary IP HA Interface
dont track primary 忽略 VRRP 的 interface 错误 不使用
track interface 跟踪接口,设置额外的监控,里面任意一块网卡出现问题,都会进入故障(FAULT)状态,例如,用nginx做均衡器的时候,内网必须正常工作,如果内网出问题了,这个均衡器也就无法运作了,所以必须对内外网同时做健康检查 neutron 只跟踪 HA Interface,其实它还是可以跟踪 network namespace 的其他 interface,比如 qr,qg 等,是吧?
mcast src ip 发送多播数据包时的源IP地址,这里注意了,这里实际上就是在那个地址上发送VRRP通告,这个非常重要,一定要选择稳定的网卡端口来发送,这里相当于heartbeat的心跳端口,如果没有设置那么就用默认的绑定的网卡的IP,也就是interface指定的IP地址 不设置,默认使用 HA Interface IP
garp master delay 在切换到master状态后,延迟进行免费的ARP(gratuitous ARP)请求 未指定
virtual router id 这里设置VRID,这里非常重要,相同的 VRID 为一个组,他将决定多播的MAC地址 创建 HA Router时从 neutron DB 分配,从 1 开始
priority 100 设置本节点的优先级,优先级高的为master 默认为 50
advert int 检查间隔,默认为1秒 配置项 ha_vrrp_advert_int (默认值 2)
virtual ipaddress 这里设置的就是VIP,也就是虚拟 IP 地址,他随着 state 的变化而增加或者删除,当 state 为master的时候就添加,当 state 为 backup 的时候删除,这里主要是有优先级来决定的,和 state 设置的值没有多大关系,这里可以设置多个 IP 地址。这篇文章 谈到其数目限制为 20,超过该数目的 IP 可以放到 virtual_ipaddress_excluded。
VIP。格式为 HA Network CIDR 最后一位被 VID 替换,
该例子中,CIDR 为 “169.254.0.1/24”, VID 为 1。
因此,每个 HA Router 的 VIP 是固定的。
virtual routes 原理和 virtual ipaddress 一样,只不过这里是增加和删除路由 不使用

authentication

auth type

auth pass

认证方式,类型,密码 配置项 ha_vrrp_auth_type (默认 ‘PASS’)  和 ha_vrrp_auth_password (默认 none)
nopreempt 设置不抢占,这里只能设置在state为backup的节点上,而且这个节点的优先级必须别另外的高 设置,使用非抢占模式。这样,只要 Master 没坏,即使 Backup 的优先级更高,也不会触发选举
preempt delay 抢占延迟 不使用
virtual_ipaddress_excluded 需要 keepalived 维护但是不会放在VRRP包中传输的IP。也就是说当某个router被选举为master的时候其会的将qg口设置对应的IP。而在变为 backup 时,会将相应的 IP 删除。 VIP。Neutron 将 浮动 IP,internal/external port IP 等保存在这里。这样,当一个 node 变为 master 的时候,这些 IP 地址会生效;当一个 node 变为 backup 的时候,这些 IP 地址会被删除。
virtual_routes 在某个router设置为 master 的时候其会的设置对应的 route。相反的,如果一个router变成了backup,那么上面这些操作会反过来做一遍。 每个 external port 对应一条路由,在 VRRP 状态变化时增加或者删除

(5.2)在 route network namespace 运行命令 ‘keepalived -P -f config_path -p pid_file -r pid_file-vrrp’ 启动 keepalived 进程。

root     31510     1  0 10:42 ?        00:00:00 keepalived -P -f /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf -p /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid -r /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid-vrrp
root     31512 31510  0 10:42 ?        00:00:00 keepalived -P -f /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf -p /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid -r /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid-vrrp

根据上面基础知识部分的介绍,第一个进程(进程号 31510)是 Watchdog 进程,第二个进程(进程号 31512)是 VRRP Stack 进程。
(6) Neutron 中,keepalived 进程启动后,初始状态都是 BACKUP。在它指定时间没收到 Master 的广播后,它自己切换至 Master 状态,然后通过 VRRP 通告报文的交互获知虚拟路由器组中其他成员的优先级,进行 Master 的选举:

如果 VRRP 报文中 Master 路由器的优先级高于或等于自己的优先级,则 Backup 交换机保持 Backup 状态。显然,Neutron 中的 keepalived 都是同样的优先级,它们会各自保持自己的 BACKUP 状态。
如果 VRRP 报文中 Master 路由器的优先级低于自己的优先级,因为 neutron 中 keepalived 采用非抢占模式,Backup交换机仍保持 Backup 状态。
可见,neutron 中,哪个 L3 Agent namespace 中的 keepalived 进程先启动并先发出 VRRP 通告,则它会成为 Master,直到它 down 掉才会由别的节点接替。在状态变化时,会调用指定的脚本。详细见 2.1.3 部分。状态变化完成后,会达到下面这种效果(Juno 中还没有实现 conntrack):

图片描述

(7)启动后状态转换过程

Aug  1 18:42:32 network Keepalived[31509]: Starting Keepalived v1.2.7 (08/14,2013)    #进程启动
Aug  1 18:42:32 network Keepalived[31510]: Starting VRRP child process, pid=31512     # VRRP Stack 进程启动
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Interface queue is empty
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Registering Kernel netlink reflector
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Registering Kernel netlink command channel
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Registering gratuitous ARP shared channel
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Initializing ipvs 2.6
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Opening file '/var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/keepalived.conf'. #导入配置文件
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Configuration is using : 64796 Bytes
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Using LinkWatch kernel netlink reflector...
Aug  1 18:42:32 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Entering BACKUP STATE #根据初始配置,首先进入 BACKUP 状态
Aug  1 18:42:32 network Keepalived_vrrp[31512]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh #脚本调用
Aug  1 18:42:39 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Transition to MASTER STATE #第一次选举后进入 Master 状态
Aug  1 18:42:39 network Keepalived_vrrp[31512]: VRRP_Group(VG_1) Syncing instances to MASTER state
Aug  1 18:42:39 network Keepalived_vrrp[31512]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh #脚本调用
Aug  1 18:42:41 network Keepalived_vrrp[31512]: VRRP_Instance(VR_1) Entering MASTER STATE

(8)VRRP 心跳

下面的 log 能看出来,Master 每隔两秒钟,向 组播地址 224.0.0.18/01:00:5e:00:00:12 发出长度为 54 bytes 的一个心跳:

ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb tcpdump -envi ha-20894a79-b6 -vvv

11:18:10.969378 fa:16:3e:09:29:d4 > 01:00:5e:00:00:12, ethertype IPv4 (0x0800), length 54: (tos 0xc0, ttl 255, id 1066, offset 0, flags [none], proto VRRP (112), length 40)
    169.254.192.2 > 224.0.0.18: vrrp 169.254.192.2 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20, addrs: 169.254.0.1
11:18:12.971101 fa:16:3e:09:29:d4 > 01:00:5e:00:00:12, ethertype IPv4 (0x0800), length 54: (tos 0xc0, ttl 255, id 1067, offset 0, flags [none], proto VRRP (112), length 40)
    169.254.192.2 > 224.0.0.18: vrrp 169.254.192.2 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20, addrs: 169.254.0.1

2.2 HA Router 调度(scheduling)

Neutron L3 Agent 的调度包括三个层面:

  • 普通(Legacy)L3 Agent 调度:在存在多个 L3 Agent 的时候,通过随机或者最小 router 数算法找到一个 L3 Agent,将该 Router 部署在上面。
  • 分布式(DVR)L3 Agent 调度:将 Router 分布到所有计算节点和网络节点。
  • 高可用(HA) L3 Agent 调度:见下文

关于 HA L3 Agent 的调度,这部分的实现代码在 这里。新的代码修改了已有的 scheduler 算法,使得它们可以支持 HA Router,但是目前不能同时支持 DVR 和 HA Router。主要代码在 /n eutron/scheduler/l3_agent_scheduler.py。该实现在 Neutron server 配置文件中添加了如下的配置项:

neutron.conf:
l3_ha = True #由管理做的全局配置,创建的所有 router 均为 HA Router
max_l3_agents_per_router = 4 #每个 HA router 最多被部署到若干个 L3 Agent 上
min_l3_agents_per_router = 2 #每个 HA Router 最少被部署到若干个 L3 Agent 上

该文件:

(1)检查可用的 L3 Agent 数目(candidates)是否超过 conf.min_l3_agents_per_router 。

(2)确定部署的 router 数目,取 min(candidates,conf.max_l3_agents_per_router )。

(3)实现两个 scheduler 类:ChanceScheduler - 随机地在可用的 L3 Agent 上部署 Router;LeastRoutersScheduler - 将 router 部署到所管理的 router 数目最少的 L3 Agent 上。它们根据不同的算法选出待部署 router 实例的 L3 Agent。

(4)bind_router 函数会执行 db 操作,将 router 和 scheduler 选出的 L3 Agent 绑定在一起。比如:

2015-08-01 10:42:21.488 19430 DEBUG neutron.scheduler.l3_agent_scheduler [req-827c863f-cb01-4650-a26d-0f176ef84026 None] HA Router 04f6e792-3b79-4b8f-a577-2ad38d33a2bb is scheduled to L3 agent 04c360d0-3066-4f04-9af2-d4ef8586ad2b) bind_ha_router_to_agents /usr/lib/python2.7/dist-packages/neutron/scheduler/l3_agent_scheduler.py:330
2015-08-01 10:42:21.547 19430 DEBUG neutron.scheduler.l3_agent_scheduler [req-827c863f-cb01-4650-a26d-0f176ef84026 None] Router 04f6e792-3b79-4b8f-a577-2ad38d33a2bb is scheduled to L3 agent 4705d27c-5008-4828-b619-bbb2114188ba bind_router /usr/lib/python2.7/dist-packages/neutron/scheduler/l3_agent_scheduler.py:226

当确定的部署数量为2 时,会是下面的情形:

图片描述

2.3 Keepalived notifier script ( VRRP 状态变化时的通知脚本)

Neutron 的 KeepalivedNotifierMixin 类实现了 keepalived 在 VRRP 状态变化时的通知方法。Neturon 添加了 VRRP 状态变为 master,backup 和 fault 状态时的 notifier。

  • 变更为 master 状态时的 notifier script:
#!/usr/bin/env bash
neutron-ns-metadata-proxy --pid_file=/var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid --metadata_proxy_socket=/var/lib/neutron/metadata_proxy --router_id=04f6e792-3b79-4b8f-a577-2ad38d33a2bb --state_path=/var/lib/neutron --metadata_port=9697 --debug --verbose --log-file=neutron-ns-metadata-proxy-04f6e792-3b79-4b8f-a577-2ad38d33a2bb.log --log-dir=/var/log/neutron
echo -n master > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state

第一个命令,在 router network namespace 中启动 neutron-ns-metadata-proxy,这样使用该 router 的虚机就可以通过该 proxy 访问 metadata service 了。关于 metadata service, proxy 和 agent,可以参考文章 (1)(2)(3)。

第二个命令,设置 state 文件内容为 “master”。 Keepalived 不支持直接查询 VRRP 状态,因此,只能通过 state 变化时被调用的脚本文件来保存其状态。

  • 变更为 backup 状态的 notifier script:
#!/usr/bin/env bash
kill -9 $(cat /var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid)
echo -n backup > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state

第一个命令,在 router network namespace 中杀掉 neutron-ns-metadata-proxy 进程,这样使用该 router 的虚机就无法通过该 proxy 访问 metadata service 了。

第二个命令,在 state 文件中记录 VRRP state 为 “backup”。

#!/usr/bin/env bash
kill -9 $(cat /var/lib/neutron/external/pids/04f6e792-3b79-4b8f-a577-2ad38d33a2bb.pid)
echo -n fault > /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/state

因此,第一次 VRRP 选举完成后,即有一个 Master router 被选举出来。这时候,各节点上不同的 script 会执行不同的动作:

  • Master router 的节点上:启动 route namespace 对应的 neutron-ns-metadata-proxy 进程,保存 “master” 至 state 文件,设置 VIP(包括配置文件中 virtual ipaddress 指定的 IP 地址和 virtual_ipaddress_excluded 部分的 IP 地址)和 virtual_routes 部分的 route。
  • Slave router 的节点上:杀掉 route namespace 对应的 neutron-ns-metadata-proxy 进程,保存 “backup” 至 state 文件,清除 VIP 和 virtual_routes 部分的 route。

2.4 依次添加 gateway,subnet,虚机 和 浮动 IP

这些操作都是对已有 HA router 的更新。 当 HA Router 的信息发生改变的时候,执行 DB 操作后,Neutron Server 会通知部署有 router 实例的 L3 Agent,然后它会去获取到更新后的信息,重新生成新的 keepalived 配置文件,然后通知 keepalived 去使用新的配置文件。

2.4.1 添加 gateway

keepalived 的配置文件的 virtual_ipaddress_excluded 部分会增加 gateway 的 interface IP:192.168.1.113/24 dev qg-f19f97bc-e5 作为一个 VIP。这个 VIP 地址会在 Master node 被设置,在 Backup node 上不会被设置。

master:

36: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.113/24 scope global qg-f19f97bc-e5
       valid_lft forever preferred_lft forever

slave:

61: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff

2.4.2 添加 subnet interface

同样地,keepalived 的配置文件的 virtual_ipaddress_excluded 部分会增加 subnet的 gateway IP:10.0.10.1/24 dev qr-73641a84-72 做为一个新的 VIP。这个 VIP 会在 Master node 被设置,在 Backup node 上不会被设置。

master:

37: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff
    inet 10.0.10.1/24 scope global qr-73641a84-72
       valid_lft forever preferred_lft forever

bakckup:

62: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff

2.4.3 添加一个虚机到 10.0.10.1/24 网络

虚机的默认网关为 10.0.10.1,这正是 Master node 上的 qr-73641a84-72 interface 上设置的 VIP 。因此,虚机发送到不是本机所在网络的网络包都会经过本 interface 进入 Master 路由器。

2.4.4 向该 IP 添加一个 浮动 IP 192.168.1.112

同样地,keepalived 的配置文件的 virtual_ipaddress_excluded 部分会增加该 浮动 IP 和它所在的 external port:192.168.1.112/32 dev qg-f19f97bc-e5 作为一个新的 VIP。这个 VIP 会在 Master node 被设置,在 Backup node 上不会被设置。

该 VIP 被加到了 Master router 的 external port 上:

36: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.113/24 scope global qg-f19f97bc-e5
       valid_lft forever preferred_lft forever
    inet 192.168.1.112/32 scope global qg-f19f97bc-e5
       valid_lft forever preferred_lft forever

同样,在 Slave router 的 exernal port 上没有添加。

但是,每一次 router 实例都更新的时候,Master router 和 Backup router 的 iptables 规则保持了一致的更新。 从以上过程可以看出,在某一时刻,只有 Master router 的 interface 在接受虚机发来的网络包,其它 VRRP router 只是在 standby。

至此,Master 和 Backup L3 Agent namespace 的效果为:

图片描述

2.5 HA Router 故障切换

非抢占模式下,会触发 keepalived VRRP failover 的情况包括:

当 slave 节点在规定的时间内没有收到 master 的心跳:比如 一个 L3 Agent 上的 keepalived 进程死了,或者 HA Device down 了,或者 HA network down 了等。除此以外的其它情况,都需要使用 workaround 来触发故障切换:比如使用 peacemaker 监视 L3 Agent,一旦发现它 down 了,立刻将 HA Device down 掉。

当 master 节点上的 externa gateway 在规定的时间内无法访问时:这里有个 bug 报出来了,到目前为止还没有被解决。

为了验证 failover,把 master node 上的 HA Device down 掉。

 ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb ifconfig ha-20894a79-b6 down

这时候,该节点的 VRRP 状态变为 fault。

Aug  1 15:26:58 network Keepalived_vrrp[4415]: Kernel is reporting: interface ha-20894a79-b6 DOWN
Aug  1 15:26:58 network Keepalived_vrrp[4415]: Kernel is reporting: interface ha-20894a79-b6 DOWN
Aug  1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering FAULT STATE
Aug  1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Now in FAULT state
Aug  1 15:26:58 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to FAULT state
Aug  1 15:26:58 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_fault.sh

原 backup node 上的 keepalived 因为在规定时间内收不到 VRRP 通告,主动发起选举过程,将自己变为 Master 状态,触发 notify_master.sh 被调用。这时候,该节点上的 router namespace 的 qr,gg 和 ha interface 的 VIP 全部被配置了,而且 route 规则也增加了。它已经接替原 master 节点向虚机提供路由服务。

60: ha-a8a3a49f-c1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:84:24:7f brd ff:ff:ff:ff:ff:ff
    inet 169.254.192.1/18 brd 169.254.255.255 scope global ha-a8a3a49f-c1
       valid_lft forever preferred_lft forever
    inet 169.254.0.1/24 scope global ha-a8a3a49f-c1
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe84:247f/64 scope link
       valid_lft forever preferred_lft forever
61: qg-f19f97bc-e5: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:14:be:06 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.112/32 scope global qg-f19f97bc-e5
       valid_lft forever preferred_lft forever
    inet 192.168.1.113/24 scope global qg-f19f97bc-e5
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe14:be06/64 scope link
       valid_lft forever preferred_lft forever
62: qr-73641a84-72: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:86:dd:53 brd ff:ff:ff:ff:ff:ff
    inet 10.0.10.1/24 scope global qr-73641a84-72
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe86:dd53/64 scope link
       valid_lft forever preferred_lft forever
root@network:/home/s1# ip netns exec qrouter-04f6e792-3b79-4b8f-a577-2ad38d33a2bb route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 qg-f19f97bc-e5
10.0.10.0       0.0.0.0         255.255.255.0   U     0      0        0 qr-73641a84-72
169.254.0.0     0.0.0.0         255.255.255.0   U     0      0        0 ha-20894a79-b6
169.254.192.0   0.0.0.0         255.255.192.0   U     0      0        0 ha-20894a79-b6
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 qg-f19f97bc-e5

从外网 ping 虚机看, failover 过程中,网络有若干秒的中断,而且有建立起来的连接断了:

64 bytes from 192.168.1.112: icmp_seq=41 ttl=63 time=2.53 ms
64 bytes from 192.168.1.112: icmp_seq=42 ttl=63 time=2.23 ms
64 bytes from 192.168.1.112: icmp_seq=50 ttl=63 time=5.35 ms
64 bytes from 192.168.1.112: icmp_seq=51 ttl=63 time=2.11 ms

这个问题会在后续版本会使用 conntrackd 来解决。

在 HA Device 重新 UP 后,VRRP 状态从 fault 变为 Backup 并一直保持该状态:

Aug  1 15:39:37 network Keepalived_vrrp[4415]: Kernel is reporting: Group(VG_1) UP
Aug  1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Leaving FAULT state
Aug  1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering BACKUP STATE
Aug  1 15:39:37 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to BACKUP state
Aug  1 15:39:37 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_backup.sh

所有 port 的 VIP 和 route 规则都被删除了。

再将 master namespace 上的 HA Device down 掉,backup namespace 中的 keepalive 重新变为 Master:

Aug  1 15:41:58 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Transition to MASTER STATE
Aug  1 15:41:58 network Keepalived_vrrp[4415]: VRRP_Group(VG_1) Syncing instances to MASTER state
Aug  1 15:41:58 network Keepalived_vrrp[4415]: Opening script file /var/lib/neutron/ha_confs/04f6e792-3b79-4b8f-a577-2ad38d33a2bb/notify_master.sh
Aug  1 15:42:00 network Keepalived_vrrp[4415]: VRRP_Instance(VR_1) Entering MASTER STATE

所有 port 的 VIP 和 route 规则都被设置了。

3. Juno VRRP 小结和后续版本的改进

3.1 Juno VRRP 实现小结

  1. Juno VRRP 使用 Keepalived,在每个 HA Route 的实例的 namespace 中启动一个 keepalived 进程,它采用非抢占式模式。
  2. keepalived 进程只监控 HA Device,在其不可用时触发选举机制。
  3. neutron 依赖 keepalived 设置或者清除 VIP (包括 VRRP VIP 和浮动 IP,internal/external port IP)
  4. neutron 依赖 keepalived 设置或者清除 virtual_routes 部分的 route 规则
  5. neutron 在 keepalived VRRP 状态变化时保存 VRRP 的状态到本地文件
  6. 所有 HA Route 实例的 namespace 中,只有 master namespace 提供 port 上的 IP 实际向网络内的虚机提供路由服务。neutron 依赖 keepalived 在 failover 时发送无用 ARP 来更新指定网络内机器的 ARP 表。
  7. Juno 版本中的实现还不完善,比如只能监控 HA Device,不能监控别的 port,包括 internal 和 external 端口,以及 external link 等;还未实现切换时保留 TCP 连接的状态。

3.2 后续版本中的持续改进

Juno 版本中增加 VRRP 的实现实现后,有很多地方需要改进,有大量的 blueprint 被开出来,但是大都进展缓慢。比较有意思的有几个:

  1. 这个 blueprint 增加 conntracked 来支持 keepalived failover 后保存已建立的 TCP 连接。目前还没完成。
    2.通过监视 HA Device 的 ip 来监视 keepalived 节点的状态
    3.这个 blueprint 增加 neutron API 来 report keepalived master。已合并到 M 版本。
    4.这个 blueprint 同时支持 DVR 和 HR,但是还未开工。
    5.这个 bug 打算增加 checker 来监视 external gateway。未完成。

这也能看出,Neutron 在基础部分(L2,L3)的实现比较扎实,但是在高级功能的实现上,包括 VRRP,DVR,VPN/FW/LBaas 等,还是不够扎实,想应用到生成环境中还需要做大量的工作。

4. Kilo 版本更新

Neutron Kilo 版本中,新增了 neutron l3-agent-list-hosting-router 命令来查看 master l3-agent 在哪个网络节点上。

图片描述

root@hkg023:~# neutron l3-agent-list-hosting-router 03cb51be-3558-4253-8062-db177679e141
+--------------------------------------+-------------------+----------------+-------+----------+
| id                                   | host              | admin_state_up | alive | ha_state |
+--------------------------------------+-------------------+----------------+-------+----------+
| 39b13aea-45a9-4336-afb4-c555b488e92d | hkg02kvm          | True           | :-)   |          |
+--------------------------------------+-------------------+----------------+-------+----------+

作者信息:刘世民(Sammy Liu),IBM 云架构师,十余年IT行业从业经历,在电信、企业软件、存储以及云计算等领域做过研发、管理和架构设计等工作。 2012 年开始学习 OpenStack,对其核心模块有较深入的了解;带领过团队开发OpenStack模块。

责编:陈晨 联系请添加微信:violace95 备注公司 职位 姓名。寻求报道或投稿,请请发送至邮箱:chenchenjs@csdn.net

评论