跳转到: 导航, 搜索

Neutron/L3 High Availability VRRP

摘要

此蓝图的目的是在虚拟路由器上添加高可用性功能。

高可用性功能将作为扩展和驱动程序实现。第一个扩展/驱动程序将基于 VRRP。

还将添加一个新的调度器,以便能够在多个代理上生成相同路由器的多个实例。


用例

目前我们能够生成许多 L3 代理,但是每个 L3 代理都是单点故障 (SPOF)。如果 L3 代理发生故障,该代理的所有虚拟路由器都将丢失,因此所有连接到这些虚拟路由器的虚拟机都将被隔离。

这里的目的是能够生成高可用虚拟路由器。

在路由器创建时

  • 我们应该能够指定路由器是否会托管两次。
  • L3 代理将托管虚拟路由器的 master 和 slave 版本。因此,两个 L3 代理保持活动状态以托管任何类型的路由器。


当虚拟路由器发生故障时

  • 代理的 VIP 驱动程序将向控制器发送通知。
  • 控制器可以将故障的虚拟路由器重新生成到另一个 L3 代理作为 slave。
  • 控制器和/或托管故障路由器的代理还可以在状态转换期间启动一些额外的脚本。


管理层

我们应该能够手动将 master 设置为故障,例如为了维护目的。此操作应将 slave 切换到 master 模式,并将路由器重新安排到另一个 L3 代理作为备份。Keepalived 的优先级参数也可以用作状态参数。在这种情况下,所有 keepalived 实例都将是 master,并且“当前”master 将根据其优先级进行选举。


实现概述

主要目的是通过添加一种新型的路由器来解决此问题,该路由器将在两个不同的代理上生成两次。一个代理将负责此路由器的 master 版本,另一个 L3 代理将负责 slave 路由器。将向每个虚拟路由器添加两个新的接口,以便允许交换管理流量,例如路由器的健康状态或 tcp 连接会话。原始接口(内部和外部)将被转换为 VIP 接口。


VIP 管理

Keepalived 将用于管理 VIP 接口。每个虚拟路由器一个 keepalived 实例,然后每个命名空间一个。


TCP 会话

Conntrackd 将用于维护通过路由器传递的 TCP 会话。每个虚拟路由器一个 conntrackd 实例,然后每个命名空间一个。


管理流量

可以为每个租户创建一个新的网络,该网络将用于管理流量交换。创建此新网络是为了将管理流量与租户流量隔离。另一种解决方案是使用私有网络。


使用专用网络的方案

Proposal with dedicated link


数据模型变更

将添加的表。

routers_vrrp

新的表,用于将路由器与 VR_ID 关联。

笔记
router_id 枚举 路由器 ID
vr_id 整数 虚拟路由器标识符(用于 keepalived 模板,请参阅附录)

routers_vrrp_port

新的表,用于将 VRRP 路由器与许多 vrrp 管理端口关联。

笔记
router_id 枚举 路由器 ID
port_id uuid 用于 HA 路由器 master 版本的端口的 ID。用于管理流量。

RouterL3AgentBinding

RouterL3AgentBinding 也将被修改,或者将添加一个新的表,以指示代理将托管路由器的哪个版本(master/slave)。

笔记
running_state 枚举 托管版本 Master/Slave

工作流程

路由器创建

l3 ha router creation


路由器更新:浮动 IP/网关/内部接口

l3 ha router update


故障

l3 ha router failure

配置变量

将在插件配置中添加一个新参数,以设置代理将生成 keepalived/conntrackd 配置的路径。还将添加一个新的参数到中子服务器配置中,以设置模式和路由器实例的数量。

插件配置配置

/etc/neutron/neutron.conf

[DEFAULT]
# Number of L3 agents scheduled to host a virtual router. This enables VRRP.
l3_agents_per_router = 3

L3 代理配置

/etc/neutron/l3_agent.ini

[DEFAULT]
vrrp_confs = $state_path/vrrp

CLI 要求

限制

  • 由于 VRID 长度为 8 位,并且根据此方案每个租户只有一个管理网络,因此每个租户的 HA 虚拟路由器数量限制为 255 个。


链接

蓝图

RFC VRRP

附录

以下是用于生成 keepalived 和 conntrackd 配置文件的两个模板。


Keepalived 模板

global_defs {
    router_id ${VR_ID}
}
vrrp_sync_group VG${VR_GROUP_ID} {
    group {
        VI_HA
    }
    % if NOTIFY_SCRIPT:
    notify_master ${NOTIFY_SCRIPT}
    % endif
}

vrrp_instance VI_HA {
    % if TYPE == 'MASTER':
    state MASTER
    % else:
    state SLAVE
    % endif
    interface ${L3_AGENT.get_ha_device_name(TRACK_PORT_ID)}
    virtual_router_id ${VR_ID}
    priority ${PRIORITY}
    track_interface {
        ${L3_AGENT.get_ha_device_name(TRACK_PORT_ID)}
    }
    virtual_ipaddress {
        % if EXTERNAL_PORT:
        ${EXTERNAL_PORT['ip_cidr']} dev ${L3_AGENT.get_external_device_name(EXTERNAL_PORT['id'])}
        % if FLOATING_IPS:
        ${FLOATING_IPS[0]['floating_ip_address']}/32 dev ${L3_AGENT.get_external_device_name(EXTERNAL_PORT['id'])}
        % endif
        % endif

        % if INTERNAL_PORTS:
        ${INTERNAL_PORTS[0]['ip_cidr']} dev ${L3_AGENT.get_internal_device_name(INTERNAL_PORTS[0]['id'])}
        % endif
    }
    virtual_ipaddress_excluded {
        % if EXTERNAL_PORT:
        % for FLOATING_IP in FLOATING_IPS[1:]:
        ${FLOATING_IP['floating_ip_address']}/32 dev ${L3_AGENT.get_external_device_name(EXTERNAL_PORT['id'])}
        % endfor
        % endif

        % for INTERNAL_PORT in INTERNAL_PORTS[1:]:
        ${INTERNAL_PORT['ip_cidr']} dev ${L3_AGENT.get_internal_device_name(INTERNAL_PORT['id'])}
        % endfor
    }

    % if EXTERNAL_PORT:
    virtual_routes {
        0.0.0.0/0 via ${EXTERNAL_PORT['ip_cidr'].split('/')[0]} dev ${L3_AGENT.get_external_device_name(EXTERNAL_PORT['id'])}
    }
    % endif
}

Conntrackd 模板

General {
    HashSize 8192
    HashLimit 65535
    Syslog on
    LockFile ${LOCK}
    UNIX {
           Path ${SOCK}
           Backlog 20
    }
    SocketBufferSize 262142
    SocketBufferSizeMaxGrown 655355
    Filter {
           Protocol Accept {
                   TCP
           }
          Address Ignore {
                  IPv4_address 127.0.0.1
          }
       }
}
Sync {
    Mode FTFW {
       }
    UDP Default {
           IPv4_address ${TRACK_PORT_LOCAL['ip_cidr'].split('/')[0]}
           IPv4_Destination_Address ${TRACK_PORT_REMOTE['ip_cidr'].split('/')[0]}
           Port 3780
           Interface ${L3_AGENT.get_ha_device_name(TRACK_PORT_ID)}
           SndSocketBuffer 24985600
           RcvSocketBuffer 24985600
           Checksum on
    }
}