亚马逊AWS官方博客

亚马逊云科技VPC中的keepalived配置指南

前言

Keepalived是在Linux操作系统中比较流行的基于网络的高可用组件,常用于包含主备节点的高可用集群,例如数据库、DNS服务器、网络防火墙等。

在决定使用keepalived之前,我们建议您首先评估是否可以使用云原生的网络高可用服务替代keepalived。AWS VPC常用的云原生高可用服务包括负载均衡器(应用负载均衡器:Application Load Balancer,  ALB或者网络负载均衡器:Network Load Balancer, NLB,网关负载均衡器:Gateway Load Balancer, GWLB),和基于DNS(AWS Route 53 )的负载均衡/故障切换。采用云原生服务构建的高可用系统,可靠性更高,配置、维护也更加简单便捷,同时系统架构设计也大为简化,进一步降低了系统故障的可能性。

Keepalived在AWS VPC中部署时,需要根据AWS VPC网络的特点进行相应的配置调整。本文将详细介绍如何在 AWS VPC上部署基于keepalived的高可用应用。

AWS VPC与keepalived

AWS VPC与传统网络的区别

与传统网络不同,AWS VPC网络是完全的软件定义网络(Software Defined Networking, SDN)。AWS VPC更加注重网络的安全性,并且根据软件定义网络的能力和特点,对网络转发行为进行了优化。具体表现为:

  • VPC内的IP转发依据子网路由表
  • 子网路由表预置目标为VPC CIDR的本地路由,并且不可修改(例如CIDR为100.0.0/16的VPC内的所有路由表都预置前缀为10.100.0.0/16,下一跳为local的路由)
  • 不支持二层/三层广播
  • ARP请求的响应由VPC网络代答,实际上报文并未在VPC网络内传递

在这里有必要对VPC的路由行为进行详细的说明,因为路由行为对keepalived的配置有直接的影响,并且充分理解VPC路由的转发行为对正确设计VPC网络十分关键。VPC路由转发遵循以下几个原则:

原则1:对于VPC内的一个子网,子网路由表仅作用在出子网方向。子网路由表对入方向的流量不起作用[注1]。

原则2:子网路由表依照最长前缀匹配方式查找。这种查找方式与传统网络的路由查找方式一致。

原则3:在投递IP报文到VPC内的网络接口时,除非另有子网路由配置,VPC网络仅会投递到已分配的IP地址。已分配的IP地址包括ENI接口的主IP、从IP(可以有多个)和委托的前缀(Prefix Delegation)。VPC网络不支持中转,从VPC外投递报文到VPC内时,报文的目标必须为上述已分配IP地址,否则报文会被丢弃[注2]

在VPC中的EC2实例允许绑定多个网卡(ENI: Elastic Network Interface),除了主网卡外,其余网卡均可与EC2实例动态绑定或解绑(主网卡不支持解绑,仅支持选择在实例删除时是否保留)。每个网卡会在所在子网分配一个主IP (Primary IP),这个主IP的生命周期与ENI相同,并且不允许修改。每个ENI可选绑定多个辅助IP (Secondary IP) 或委托前缀 (Prefix Delegation)。辅助IP/IP前缀同样可以动态绑定/解绑。ENI主IP通常是由DHCP动态配置到实例中,通常不需要干预即可在实例内部生效;而DHCP不支持对辅助IP/IP前缀的配置,所以必须依赖实例内部的额外配置才能在实例内部生效。EC2实例可以绑定ENI的数量以及辅助IP/IP前缀的数量与实例规格相关,请参照相关EC2文档。EC2在关联ENI和添加辅助IP/IP前缀的时候,有以下要求:

  • EC2实例可以绑定不同子网、不同VPC的ENI,但是这些ENI需要与EC2实例处于相同的可用区
  • ENI绑定的辅助IP或前缀IP必须与该ENI的主IP位于同一子网

AWS VPC中keepalived的网络设计

keepalived 是一个基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)的,运行于Linux系统上的高可用组件。VRRP允许多个物理节点组成一个虚拟路由器组,并共享同一个虚拟IP地址(VIP: Virtual IP)。在正常运行状态下,整个集群会基于配置中设定的优先级自动选举出主节点,由主节点绑定并响应该VIP,处理所有流向VIP的数据流量,而其余节点作为备用。集群内部通过定时发送心跳报文进行状态同步,一旦主节点故障或宕机时,备节点就会触发自动选举逻辑,由优先级最高的节点接管VIP,从而实现故障切换。

基于前述对AWS VPC及EC2网络的理解,在AWS VPC中配置keepalived时,需要考虑以下方面:

  • AWS VPC中不支持VRRP组播,需要配置keepalived使用单播模式
  • AWS VPC的安全机制不支持使用免费ARP的方式来实现VRRP VIP的动态漂移,必须通过AWS API,将VIP与keepalived master节点绑定

在 VPC 中,VIP的实现方式主要有两个选择。1) 由于ENI可以动态绑定/解绑,我们可以选择使用ENI的主IP承担VIP的角色。2) 实现VIP的另外一个选择是利用辅助IP的动态绑定和解绑能力,来实现VIP在不同节点之间的漂移。这两种方式实现VIP的主要区别如下:

  • ENI的绑定/解绑涉及操作系统中网卡的热插拔,流程相对复杂
    • ENI需要先完成解绑,然后才能执行绑定操作
    • 不是所有的操作系统都支持网卡热插拔
    • 即使是支持网卡热插拔的操作系统,在异常情况下可能无法响应热插拔事件;而在触发keepalived主从切换时,操作系统有一定可能处于异常状态
    • 虽然EC2 API支持强制绑定解绑ENI(无需通知操作系统),但是这种操作无法确保网卡在操作系统中正常工作
    • ENI绑定/解绑操作不具有幂等性,重复解绑/绑定操作API会调用失败
  • 辅助IP的绑定/解绑操作较为简单
    • 只需一个API调用即可完成辅助IP的重新绑定
    • 辅助IP的解绑/绑定API不依赖操作系统的配合
    • 辅助IP的绑定操作是幂等的,重复操作不会导致API调用失败

基于以上原因,在本文中我们使用辅助IP的方式来为keepalived提供VIP[注3]。

接下来我们要解决的问题是如何将VIP漂移与keepalived的状态转换联动。我们需要一种触发机制来实现在keepalived节点发生状态变化的同时进行相应的VIP漂移操作。

keepalived可以配置在发生状态转换时执行特定的脚本(参考keepalived文档)。我们只需要在keepalived节点成为VRRP master时将VIP绑定到该节点,这可以通过如下配置实现:

 # to MASTER transition
 notify_master /path/to_master.sh [username [groupname]]

在上面的/path/to_master.sh文件中,我们通过AWS EC2 CLI命令来实现VIP的漂移:

aws ec2 assign-private-ip-addresses \
    --network-interface-id <eni id> \
    --private-ip-addresses <VIP>    \
    --allow-reassignment

在上面的命令中,我们需要将<eni id>替换为本节点的ENI ID。并且,该节点需要有访问 EC2 AssignPrivateIpAddresses API的IAM权限。

使用EC2基于辅助IP实现VIP漂移的配置流程

图1 基于辅助IP漂移实现keepalived

我们用一个最小环境来演示使用EC2实例在AWS VPC中配置keepalived的具体操作。在这个例子里面,我们用两台EC2实例运行keepalived,分别作为主、备节点。我们在这两台实例上配置nginx提供一个简单的web页面,用于验证VIP是否成功漂移。我们另外创建一台EC2服务器作为客户端,通过这台EC2服务器来访问VIP。完整的配置包括以下几个部分:

  • 创建2个EC2实例用于运行keepalived和nginx web服务器,这两个实例使用相同的子网
  • 为keepalived实例的instance profile添加IAM权限,用于绑定/解绑辅助IP
  • 创建一个EC2实例用于模拟客户端
  • 创建安全组规则,用于keepalived实例之间互相通信和客户端访问keepalived实例web服务。将安全组绑定到各个实例
  • 启动keepalived服务,并触发主备切换。分别从客户端访问VIP,验证VIP绑定到了正确的节点

⚠️ 本文中所有示例基于Amazon Linux 2023操作系统,其它发行版请根据实际情况调整相关命令。

一、环境准备

我们将启动两台t3.micro EC2实例,分别作为MasterBackup节点,实现通过虚拟IP (VIP) 的主备切换。

⚠️ 请确认两台实例位于同一子网。

安装keepalived, AWS CLI和nginx

在两台 EC2 实例上分别执行以下命令:

sudo yum install -y keepalived aws-cli nginx

# 验证安装成功
keepalived -v
aws --version
nginx -v

我们另外启动一台t3.micro实例,用于模拟客户端。客户端使用的curl命令已经包含在Amazon Linux 2023系统镜像中,无需额外安装操作。

二、IAM权限配置

在本例中,我们使用EC2 instance profile来为keepalived实例配置操作辅助VIP的权限。您也可以使用其它方式来为keepalived实例提供必要的IAM权限,例如使用AK/SK。要使 EC2 实例具备动态分配 VIP 的能力,需要为两台实例绑定一个包含以下权限的 IAM 角色:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AssignPrivateIpAddresses",
                "ec2:DescribeNetworkInterfaces"
            ],
            "Resource": "*"
        }
    ]
}

该角色的信任关系需要包含EC2服务,才可以允许绑定到EC2实例上。我们为该角色添加如下信任关系:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

⚠️ 请在AWS控制台为Master和Backup实例分别绑定该IAM角色。

三、添加安全组规则

keepalived集群节点之间需要使用VRRP协议进行通信。VRRP协议是独立于TCP/UDP的IP协议,IP协议号为112。我们可以添加安全组入站规则以放行相应的keepalived协议。在VPC中,我们也可以通过使用default安全组实现节点之间的无限制通信,这也是我们在本文中所使用的方式。在VPC中的default安全组中包含一条入站规则,该规则允许来自本安全组的所有流量。这意味着绑定了default安全组的所有网络接口之间的通信不受限制,同时并未开放来自其它源地址或其它安全组的访问。default安全组的这个设计特别适用于集群节点间的相互通信。

⚠️ 我们为Master、Backup节点和客户端节点分别添加绑定default安全组,同时保留原有安全组

四、创建 VIP 分配脚本

在两台 EC2 实例上分别执行以下命令:

sudo mkdir -p /etc/keepalived/scripts

执行以下脚本生成VIP切换脚本文件,并根据你的实际VIP和ENI ID修改:

#替换为本机ENI id
MY_ENI=eni-<EC2 eni id>

cat << EOF | sudo tee /etc/keepalived/scripts/vip_master.sh
#!/bin/bash
VIP=10.0.1.100
# 本机ENI id
ENI=$MY_ENI
# 分配 VIP 到 ENI(允许重新分配)
/bin/aws ec2 assign-private-ip-addresses \\
    --network-interface-id \$ENI    \\
    --private-ip-addresses \$VIP    \\
    --allow-reassignment
EOF

赋予脚本可执行权限:

sudo chmod +x /etc/keepalived/scripts/vip_master.sh

⚠️ keepalived Master、Backup节点上都需要执行上述操作,并替换ENI ID为该节点的ENI ID

五、创建keepalived配置文件

Master节点配置

执行脚本内容如下:

cat << EOF | sudo tee /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
    state MASTER
    interface ens5   # 替换为实例的网络接口名
    virtual_router_id 51
    priority 100
    advert_int 1

    virtual_ipaddress {
        10.0.1.100
    }
    unicast_peer {
        10.0.1.102  # Backup节点的IP
    }
    notify_master /etc/keepalived/scripts/vip_master.sh nobody
}
EOF

Backup 节点配置

执行脚本内容如下:

cat << EOF | sudo tee /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
    state BACKUP
    interface ens5   # 替换为实例的网络接口名
    virtual_router_id 51
    priority 90
    advert_int 1

    virtual_ipaddress {
        10.0.1.100
    }
    unicast_peer {
        10.0.1.101   # Master节点的IP
    }
    notify_master /etc/keepalived/scripts/vip_master.sh nobody
}
EOF

分别在keepalived Master和Backup节点启动keepalived服务和nginx服务:

sudo systemctl enable keepalived --now
sudo systemctl enable nginx --now
systemctl status keepalived.service
systemctl status nginx.service

六、验证VIP漂移

  1. 验证 VIP 当前绑定在 Master 节点

在 Master 节点上执行以下命令,确认 VIP 10.0.1.100 已绑定到网卡 ens5

ip addr show ens5 | grep 10.0.1.100

输出如下,表示 VIP 当前处于 Master 节点:

inet 10.0.1.100/32 scope global ens5

  1. 创建简单的web页面

在Master节点创建一个简单的页面:

cat << EOF | sudo tee /usr/share/nginx/html/index.html
Hello from node 101                                              
EOF

在Backup节点创建一个简单的页面:

cat << EOF | sudo tee /usr/share/nginx/html/index.html
Hello from node 102
EOF
  1. 验证服务访问正常

在客户端节点,使用curl命令向两个keepalived节点分别发起HTTP请求:

curl 10.0.1.101 10.0.1.102

输出如下:

Hello from node 101
Hello from node 102

在客户端节点,使用curl命令向VIP发起HTTP请求:

curl 10.0.1.100

输出如下:

Hello from node 101

以上输出表明VIP目前绑定在节点101上,即Master节点。

  1. 模拟主节点故障切换

我们可以有多种方式模拟Master节点的故障。keepalived提供多种配置选项用于监控系统的健康情况,比如使用track_script自定义健康检查脚本。本文中通过停止Master节点的 keepalived服务来模拟Master节点的故障。

在 Master 节点执行:

sudo systemctl stop keepalived

在客户端节点,使用curl命令再次向VIP发起HTTP请求:

curl 10.0.1.100

输出如下:

Hello from node 102

以上输出表明VIP已经成功漂移,目前绑定在节点102上,即Backup节点。

通过以上步骤,成功验证了keepalived在Master节点故障时,能够将VIP漂移到Backup节点,确保服务高可用。

总结与讨论

本博客介绍了如何在AWS VPC内通过辅助IP的方式配置keepalived实现EC2间的VIP漂移,完成主备切换。该方案适用于内网流量高可用需求,配置简单。成为Master的节点会执行脚本,将辅助IP分配到自己的网卡。

讨论

  1. keepalived集群节点位于同一可用区的不同子网

由于辅助IP漂移仅限同一子网,当keepalived集群节点位于不同子网时,无法直接使用辅助IP作为VIP。在这种情况下,可以通过添加ENI弹性网络接口的方式,使所有集群节点在某个特定子网中都有关联的弹性网络接口,然后修改keepalived配置文件使用该网络接口来实现本文中描述的方案。

  1. 通过VPC对等连接 (VPC Peering) 、VGW和TGW从VPC外部访问keepalived VIP

由于辅助IP由AWS VPC分配,使用辅助IP作为keepalived VIP时,可以通过VPC Peering、VGW或TGW从外部访问。在必要的网络路由配置之外无需针对VIP进行特殊路由配置。

  1. 面向公网服务的keepalived集群

通过将Elastic IP关联到用于VIP的辅助IP,我们很容易用以上配置来实现keepalived集群同时对公网提供服务。在辅助IP漂移的同时,Elastic IP会保持与辅助IP关联,同步漂移到新的网卡。需要注意的是 1) keepalived节点需要位于公开子网; 2) keepalived节点安全组入站规则需要允许相应的服务访问。

  1. 跨可用区的高可用keepalived集群

无论是使用辅助IP还是ENI弹性网卡实现keepalived的VIP,都要求集群节点位于同一个可用区。在一些场景下,业务可能要求VIP可以跨可用区漂移,以实现更高级别的可用性。在VPC网络中,可以使用更新路由表的方式实现跨可用区的VIP漂移。在这种方式下,我们需要在路由表中创建一条路由,目的前缀为VIP,下一跳为keepalived master节点。具体配置方式我们将在另外一个博客中描述。

注释

[注1] 本文不展开讨论路由表的边缘关联 (Edge Association),并且边缘关联仅针对VPC入方向的流量,对子网内流量没有影响。

[注2] VPC边缘网关所关联的路由表仅在报文进入VPC后才会被索引,所以对报文是否能进入VPC没有影响。使用隧道方式实现网络中转(例如中转网关 (Transit Gateway) 的Connect Attachment)没有直接使用VPC路由,并不违反VPC网络不支持中转的原则。

[注3] 在之前的另外一个亚马逊云科技博客文章中有关keepalived的部分使用了ENI弹性网卡作为配置示例,基于本文描述的理由,我们推荐优先考虑使用辅助IP的实现方案。

本篇作者

刘俊辉

亚马逊云科技高级解决方案架构师,在通信、互联网、云计算行业有多年从业经验,对云原生产品技术及解决方案有非常深入的理解。长期跟踪云计算前沿技术动态,目前专注于亚马逊云科技初创生态。

朱文倩

亚马逊云科技解决方案架构师,负责亚马逊云科技云计算方案咨询和设计。同时致力于生成式 AI 应用方面的研究和推广,并通过可实施的解决方案,帮助客户取得业务价值。