亚马逊AWS官方博客

在跨可用区部署的双路由器实例间实现冗余网关切换

《利用Site-to-Site VPN在亚马逊云科技中国区实现企业级混合云互连》一文中,我们在亚马逊云科技的云上采用中转网关(Transit Gateway,TGW)结合 Transit VPC(Virtual Private Cloud)中的双路由器实例实现了与云下本地数据中心双路由器构建全冗余 BGP(Border Gateway Protocol)网络的解决方案。在云上,通过 TGW 将多个 VPC 的网段传播到云下数据中心,也通过 TGW 把远端的网段可达信息通告到亚马逊云科技。VPC 内的主机实例将 TGW 作为访问远端网络的网关,而 TGW 作为亚马逊云科技的托管服务,底层硬件有高可用的部署,所以主机实例无需担心 TGW 的失效。

在多 VPC 参与网络互连的场景下,TGW 简化了网络的互联,也由此引入了 TGW 的使用费用。当用户在亚马逊云科技的云上只有一个 VPC 中的主机实例与远端本地数据中心或其他云服务商网络互连时,TGW 不再是必不可少的组件,我们可以将路由器实例直接部署在该 VPC 内,实现通过 Site-to-Site VPN 等联网方式与远端网络互连,从而省去 TGW 的相关费用,见图 1。

图 1. 单 VPC 跨可用区双路由器冗余互连

图 1 所示的亚马逊云科技 VPC 中分别在两个可用区(Availability Zone,AZ)各部署一台基于 EC2 的路由器实例,远端本地数据中心为双路由器部署,之间采用多条 Site-to-Site VPN 隧道通过互联网连接。路由器之间通过 EBGP(External Border Gateway Protocol)协议交换路由信息并实现 ECMP(Equal-Cost Multi-Path),并配合 BFD(Bidirectional Forwarding Detection)技术实现 BGP 快速收敛。

本地数据中心的两台路由器在面向局域网的端口上采用 VRRP(Virtual Router Redundancy Protocol)协议实现冗余网关(以上设计和实现请参阅《利用 Site-to-Site VPN 在亚马逊云科技中国区实现企业级混合云互连》)。

云上的路由器实例每台分别配置两个 eni(elastic network interface)网卡,一个放置在公有子网作为 WAN(Wide Area Network)口,负责与远端路由器互连;另一个放置在私有子网作为 LAN(Local Area Network)口,作为 VPC 内其他实例访问远端网络的网关。

但在云上的 VPC 部署跨可用区的双路由器作为 VPC 子网网关时,无法像在云下本地数据中心环境中采用 HSRP(Hot Standby Router Protocol)/VRRP 协议来为 LAN 用户实现冗余网关,原因如下:

  • 亚马逊云科技 VPC 环境下无法支持组播,而 HSRP/VRRP 通过组播来交换协议信息;
  • HSRP/VRRP 要求两台路由器面向 LAN 的网关地址和虚拟网关地址都在同一网段,但跨可用区部署的路由器 LAN 端口不在一个网段下;
  • 为 LAN(私有子网)中的主机实例路由表建立的网关只能指向本子网的路由器的虚拟网口 eni,无法指向具体的 IP 地址,见图 2。

图 2. 私有子网主机路由表

这将导致如果 Router A 发生故障,私有子网 1 内主机的网关就丢失,Router B 无法切换为私有子网 1 内的主机网关。为了实现跨可用区的路由实例网关故障切换,我们的设计目标是当 Router A 失效时,私有子网 1 的路由表网关由 Router A 的私有子网 eni 切换为 Router B 的私有子网 eni,从而实现业务永续。

以下按照使用两种不同类型的路由器实例设计实现。

1. 采用思科(Cisco)路由器实例

当采用 Cisco CSR1000v 或 Catalyst 8000v 等云端企业级虚拟路由器实例时,可以利用相关网络技术以及这些云虚拟路由器的厂商内置功能来设计实现冗余网关。

1.1 VPC 内的路由器设计

  • 在 Router A 和 Router B 之间建立 GRE(Generic Routing Encapsulation)Tunnel,并且启用 BFD,用来发现虚拟路由器失效;
  • 利用云端服务组件都能够通过 API 进行配置的特点,当 BFD 检测到链路失效时,存活的虚拟路由器发起 API call,把失效路由器所在的子网路由表网关自动改成自己的 eni 端口,从而达到 HA(High Availability)的效果,见图 3。

图 3. 通过 EC2 API 自动修改子网路由表

1.2 Cisco 虚拟路由器的配置

由于无法使用 HSRP 或 VRRP 虚拟网关协议来实现 VPC 内两台路由器的私有子网网关故障切换,因此,根据前述设计,将通过 EC2 API 修改私有子网路由表方式实现。两台路由器实例间通过 WAN 口建立 GRE 隧道,在此 GRE 隧道网段上启用基于 EIGRP 路由协议的 BFD 来互相检测设备失效,GRE 隧道两端的地址设计分配为 Router A: 169.254.222.1/24、Router B: 169.254.222.2/24(示例 IP 地址)。在 Router A 路由器实例上配置示例如下:

interface Tunnel100
 ip address 169.254.222.1 255.255.255.0
bfd interval 300 min_rx 300 multiplier 3	
 tunnel source GigabitEthernet1
 tunnel destination 54.222.236.14
!
router eigrp 1
bfd all-interface
 network 169.254.222.0
 passive-interface GigabitEthernet1

在亚马逊云科技的 VPC 管理控制台中配置私有子网的路由表,将缺省路由网关指向本子网对应的路由器实例的虚拟网口 eni,见图 2,并记录下 eni 的 ID 和路由表 ID。

虚拟路由实例采用 Cisco CSR1000v,该实例支持基于 CentOS 7 的 Linux 容器(称为 guest shell),在容器配置下可以启用 HA 服务。该 HA 服务根据配置在检测到对端路由器实例失效时发起 API Call,EC2 API 会将失效路由器的私有子网路由表的网关改成仍然可用的路由器实例的 LAN 口 eni。该容器与其载体路由器实例的关系为逻辑隔离,因此容器的网络访问通过 VRF(Virtual Router Forwarding,虚拟路由转发)实现。同时,因为该容器需要访问 EC2 API,所以该容器的 VRF 也必须能访问互联网。相关配置在 Router A 的实现如下。

在 Router A 中输入 guestshell 命令进入 Linux 容器操作界面配置 HA:

[guestshell@guestshell ~]$pip install csr_aws_ha –-user command  #安装适用于亚马逊云科技的Python HA应用包
[guestshell@guestshell ~]$ cd cloud						#进入服务所在目录
[guestshell@guestshell cloud]$ sudo systemctl start csr_ha  		#启动HA服务
[guestshell@guestshell cloud]$ create_node.py -i 10 -t rtb-0d0cd687ea009e220 -rg cn-north-1 -n eni-0c7083a3cf21d79f2 -r 0.0.0.0/0 -m primary  		#将自己LAN口的eni作为私有子网1的网关设为主用
[guestshell@guestshell cloud]$ create_node.py -i 20 -t rtb-0dc75033d8af718bf -rg cn-north-1 -n eni-0c7083a3cf21d79f2 -r 0.0.0.0/0 -m secondary 	#将自己LAN口的eni作为私有子网2的网关设为备用

在 Router B 中输入 guestshell 命令进入 linux 容器操作界面配置 HA:

[guestshell@guestshell ~]$pip install csr_aws_ha –-user command  #安装适用于亚马逊云科技的Python HA应用包
[guestshell@guestshell ~]$ cd cloud						#进入服务所在目录
[guestshell@guestshell cloud]$ sudo systemctl start csr_ha  		#启动HA服务
[guestshell@guestshell cloud]$ create_node.py -i 10 -t rtb-0d0cd687ea009e220 -rg cn-north-1 -n eni-08d1a545215f50ccf -r 0.0.0.0/0 -m secondary 	#将自己LAN口的eni作为私有子网1的网关设为备用
[guestshell@guestshell cloud]$ create_node.py -i 20 -t rtb-0dc75033d8af718bf -rg cn-north-1 -n eni-08d1a545215f50ccf -r 0.0.0.0/0 -m primary 	#将自己LAN口的eni作为私有子网2的网关设为主用

在上述两台路由器上为 Guest Shell 容器所使用的 VRF 设置到达互联网的静态路由:

ip route vrf GS 0.0.0.0 0.0.0.0 GigabitEthernet1 10.40.1.1 global

本条静态路由为 Guest Shell 容器设置了到达互联网的路由,没有本条路由,Guest Shell 容器将无法访问 EC2 API。

如使用 Cisco Catalyst 8000v 虚拟路由器,相关配置请参阅:Configure High Availability on Cisco Catalyst 8000V Running on Amazon Web Services

1.3 亚马逊云科技管理控制台上的配置

管理控制台上的配置包括:私有子网路由表的配置(已完成)、为路由器实例授予可以通过 API 修改路由表的权限。

在管理控制台上选择 IAM 面板,在左栏选择“角色”后创建角色,示例名为“CSRChangeRouteRole”,然后选择 EC2 作为角色实体。在创建策略中输入以下 JSON 语句创建对路由表拥有创建、更改、删除的权限策略:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AssociateRouteTable",
                "ec2:CreateRoute",
                "ec2:CreateRouteTable",
                "ec2:DeleteRoute",
                "ec2:DeleteRouteTable",
                "ec2:DescribeRouteTables",
                "ec2:DescribeVPCs",
                "ec2:ReplaceRoute",
                "ec2:DescribeRegions",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DisassociateRouteTable",
                "ec2:ReplaceRouteTableAssociation"
            ],
            "Resource": "*"
        }
    ]
}

在管理控制台的 EC2 面板中,选择路由器实例,在右上角“操作”菜单下依此选择“安全”、“更改 IAM 角色”,为路由器实例赋予前文配置的名为“CSRChangeRouteRole”的 IAM 角色,见图 4。通过此角色的赋予,路由器实例可通过发起 API 请求更改私有子网的路由表。

图 4. 为路由器实例赋予可修改子网路由表权限的角色

在 Router B 上完成的配置类似,不再赘述。

1.4 网关切换实现结果验证

验证亚马逊云科技 VPC 内两台路由器实例的故障保护切换效果时可以建立亚马逊云科技 VPC 的主机实例和云下本地数据中心主机的 ping 长连接。然后停止 Router A 路由实例,ping 没有中断,见图 5。

图 5. 亚马逊云科技 VPC 与本地数据中心长连接测试 HA 效果

在亚马逊云科技管理控制台上查看私有子网 1 的路由表可以看到,路由表网关地址已经切换到 Router B 的 LAN 口 eni 上,见图 6。左边是原始路由表,右边是 Router A 中断后的路由表。

图 6. 路由器故障切换时路由表的变化

当路由器实例恢复运行后,路由表会自动改回到原始状态。

2. 采用开源路由器实例

当采用开源路由器实例以及不支持类似 Cisco 虚拟路由器的 HA 功能的路由器实例时,我们可以利用亚马逊云科技的 Lambda 无服务器计算以及相关服务来实现路由器网关的故障保护切换。

2.1 网关切换的设计

由于路由器实例无法通过自身的功能来修改私有子网路由表的网关来实现故障保护切换,我们需要利用亚马逊云科技提供的工具来修改私有子网路由表中的网关,所利用到的工具以及实现步骤如下:

  1. Amazon EventBridge 是一项无服务器事件总线服务,它充当应用程序组件之间的”中间人”,允许这些组件通过事件相互通信。利用 Amazon EventBridge 可侦测路由器实例的状态,当实例状态在非“running”状态时将匹配到 EventBridge 事件规则;
  2. EventBridge 事件将触发 Lambda 函数的运行,按照配置好的代码,Lambda 将原私有子网路由表的网关 eni 改成另一台正在运行的路由器实例网关 eni,从而完成网关故障保护切换;
  3. 当 EventBridge 侦测到失效的路由器实例恢复运行(“running”状态),将触发另一个 Lambda 函数,用来将路由表中的网关修改回原路由器实例的 eni(可选步骤,本文未演示)。

设计架构见图 7。

图 7. 通过 Lambda 修改路由表中的网关 eni

2.2 网关切换的实现

2.2.1 Lambda 的配置

以修改图 7 中的私有子网 1 中的路由表中的网关 eni 为例,编写 Lambda 实例代码需先记录该路由表 ID、原网关 eni ID(Router A 的 LAN eni)、新网关 eni ID(Router B 的 LAN eni)。在亚马逊云科技管理控制台中转到 Lambda 面板,创建 Lambda 函数 routetable-modifier(示例名称)。输入以下 python 示例代码:

import boto3
import json

def lambda_handler(event, context):
    """
    AWS Lambda 函数,用于修改路由表中的路由项网关
    当收到 EventBridge 事件时触发
    """
    try:
        # 初始化 EC2 客户端
        ec2_client = boto3.client('ec2')
        # 路由表 ID
        route_table_id = 'rtb-00898f873f12c29ac'
        # 原网关 ENI ID
        old_gateway_id = 'eni-04e5f196c4dd7bc47'
        # 新网关 ENI ID
        new_gateway_id = 'eni-0c051084dc8507f4a'
        # 目标 CIDR
        destination_cidr = '0.0.0.0/0'
        # 获取路由表信息
        response = ec2_client.describe_route_tables(
            RouteTableIds=[route_table_id]
        )
        # 检查路由表是否存在
        if not response['RouteTables']:
            return {
                'statusCode': 404,
                'body': json.dumps(f'路由表 {route_table_id} 不存在')
            }
        # 获取路由表中的路由
        routes = response['RouteTables'][0]['Routes']
        # 查找匹配的路由
        route_exists = False
        for route in routes:
            if route.get('DestinationCidrBlock') == destination_cidr and route.get('NetworkInterfaceId') == old_gateway_id:
                route_exists = True
                break
        
        if not route_exists:
            return {
                'statusCode': 404,
                'body': json.dumps(f'未找到目标为 {destination_cidr} 且网关为 {old_gateway_id} 的路由')
            }
        # 替换路由
        ec2_client.replace_route(
            RouteTableId=route_table_id,
            DestinationCidrBlock=destination_cidr,
            NetworkInterfaceId=new_gateway_id
        )
        return {
            'statusCode': 200,
            'body': json.dumps(f'成功将路由表 {route_table_id} 中目标为 {destination_cidr} 的路由网关从 {old_gateway_id} 更改为 {new_gateway_id}')
        }
    except Exception as e:
        print(f'发生错误: {str(e)}')
        return {
            'statusCode': 500,
            'body': json.dumps(f'执行过程中发生错误: {str(e)}')
        }

这段示例代码在接收到 EventBridge 事件触发后运行,将私有子网路由表中的原网关 eni(Router A)修改为新网关 eni(Router B)。

此外,Lambda 函数应具有相关权限来修改路由表,因此需为此 Lambda 附加角色,该角色具有以下权限策略:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeRouteTables",
                "ec2:ReplaceRoute",
                "ec2:CreateRoute",
                "ec2:CreateRouteTable",
                "ec2:DeleteRoute",
                "ec2:DeleteRouteTable",
                "ec2:DescribeVPCs",
                "ec2:DescribeRegions",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DisassociateRouteTable",
                "ec2:ReplaceRouteTableAssociation"
            ],
            "Resource": "*"
        }
    ]
}

2.2.2 Amazon EventBridge 的配置

在亚马逊云科技管理控制台中转到 EventBridge 面板,创建 EventBridge 规则 Router1Failure(示例名称)。在事件模式下输入以下 JSON 语句以侦测路由器实例(ID:i-0eca14b6b382a9a68)的失效状态事件,包括:pending、shutting-down、terminated、stopping、stopped,见图 8。

图 8. 创建 EventBridge 规则

在“Router1Failure”这个事件规则下选择“目标”为前文创建的 Lambda 函数“routetable-modifier”。完成配置后,当路由器实例 Router A 失效时,EventBridge 将触发 Lambda 函数运行。

最后,在 Lambda 的面板上,将 EventBridge 规则“Router1Failure”配置为 Lambda 函数“routetable-modifier“的触发器,见图 9。

图 9. 将 EventBridge 设置为 Lambda 的触发器

通过 EventBridge 和 Lambda 函数的组合,还可以实现当路由器实例恢复运行时,网关切换回原网关。

2.3 网关切换的实现结果验证

私有子网 1 的路由表中 0.0.0.0/0 网段的原网关指向 Router A 的 LAN eni,将 Router A 停止,该路由表的 0.0.0.0/0 网段的网关切换为 Router B 的 LAN eni,由此实现了冗余网关的故障保护,见图 10。

图 10. 路由器实例故障切换时路由表中网关的变化

3. 总结

在单 VPC 中部署路由器实例实现跨可用区的冗余网关故障保护切换时,由于受到亚马逊云科技的环境限制,比如:VPC 不支持组播/广播流量、子网不能跨可用区等,传统的 VRRP/HSRP 等冗余网关协议无法实施。在本文中,我们设计利用了 Cisco云虚拟路由器的 HA 功能,以及亚马逊云科技的 EventBridge 结合 Lambda 函数修改子网路由表这两种方式,分别为 Cisco 和其他开源虚拟路由器在跨可用区部署时实现了冗余网关的故障保护切换提供了配置指南,并验证了结果。

注:

在本文完成之际,亚马逊云科技于 2025 年 4 月 1 日正式宣布在美国东部(弗吉尼亚)、美国东部(俄亥俄州)、美国西部(俄勒冈州)、欧洲地区(爱尔兰)、欧洲地区(法兰克福)和亚太地区(东京)这六个区域推出 VPC Route Server 服务,用于简化 Amazon VPC 中虚拟设备之间的动态路由。借助 Route Server,用户能够通过边界网关协议(BGP)从虚拟设备发布路由信息,并动态更新与子网和互联网网关相关联的 VPC 路由表。这给用户在以上区域部署冗余路由网关提供了新的选项,详情请见:宣布全面推出 Amazon VPC Route Server,相关技术文档也请关注亚马逊云科技的官方文档以及后续推出的技术 Blog。

本篇作者

宁琛

亚马逊云科技资深解决方案架构师,致力于帮助初创企业在亚马逊云平台上实现业务部署。在网络通信和云计算领域有多年的实践经验,拥有亚马逊云科技多项专业技术认证以及Cisco Certified Internetwork Expert(CCIE #4751)等网络技术相关认证。