亚马逊AWS官方博客

私有实例轮询使用多公网 IP 访问公网的实现方案

1.背景说明

众所周知,业务服务器是企业用以提供服务实现盈利的核心,绝大多数情况业务服务器都部署于私有网络中,以保证安全。

在私有网络中的业务服务器提供业务功能时,对网络的诉求会分成两种类型:第一种是经由互联网被访问,可以借助负载均衡器进行服务暴露实现;第二种则是主动访问互联网。

对于第二种类型,如果需要的公网 IP 数量少于或等于 8 个,可以使用 Amazon NAT Gateway 实现;若需要的公网 IP 数量大于 8 个,则需要更加灵活的方案。

本文将介绍两种方案,用于满足“需要的公网 IP 数量大于 8 个”的场景,并在文章的最后,对这两种方案进行优劣势比较。

2.方案实现

2.1 方案 1:通过 iptables 的 SNAT 规则实现

a. 架构图(其中业务服务器 2 仅作为展示,并不会真正创建)

b. 环境搭建(执行命令的机器需要有足够的权限)

1) 创建网络资源

# 0. 进入screen模式,以保证环境变量不会因为退出Session而消失
screen

# 1. 创建VPC
ningxia_vpc_id=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --region cn-northwest-1 --query "Vpc.VpcId" --output text)

# 2. 创建Subnet
# 在AZ1中创建ningxia_subnet_az1_id1(私有子网1)
ningxia_subnet_az1_id1=$(aws ec2 create-subnet --cidr-block 10.0.0.0/24 --vpc-id $ningxia_vpc_id --region cn-northwest-1 --availability-zone cn-northwest-1a --query "Subnet.SubnetId" --output text)
# 在AZ1中创建ningxia_subnet_az1_id2(公有子网)
ningxia_subnet_az1_id2=$(aws ec2 create-subnet --cidr-block 10.0.1.0/24 --vpc-id $ningxia_vpc_id --region cn-northwest-1 --availability-zone cn-northwest-1a --query "Subnet.SubnetId" --output text)

# 3. 创建路由表
# 创建ningxia_route_table_id1(私有子网1路由表)
ningxia_route_table_id1=$(aws ec2 create-route-table --vpc-id $ningxia_vpc_id --region cn-northwest-1 --query "RouteTable.RouteTableId" --output text)
# 创建ningxia_route_table_id2(公有子网路由表)
ningxia_route_table_id2=$(aws ec2 create-route-table --vpc-id $ningxia_vpc_id --region cn-northwest-1 --query "RouteTable.RouteTableId" --output text)

# 4. 更改子网路由表
aws ec2 associate-route-table --route-table-id $ningxia_route_table_id1 --subnet-id $ningxia_subnet_az1_id1 --region cn-northwest-1
aws ec2 associate-route-table --route-table-id $ningxia_route_table_id2 --subnet-id $ningxia_subnet_az1_id2 --region cn-northwest-1

# 5. 修改安全组
# 获取宁夏的groupid
groupid_ningxia=$(aws ec2 describe-security-groups --region cn-northwest-1 --filter "Name=vpc-id,Values=$ningxia_vpc_id" --query "SecurityGroups[].GroupId" --output text)
# 基于获取的groupid,修改安全组
aws ec2 authorize-security-group-ingress --group-id "$groupid_ningxia" --protocol all --port all --cidr "0.0.0.0/0" --region cn-northwest-1

# 6. 创建并附加Internet Gateway
internet_gateway_id_ningxia=$(aws ec2 create-internet-gateway --region cn-northwest-1 --query "InternetGateway.InternetGatewayId" --output text)
aws ec2 attach-internet-gateway --region cn-northwest-1 --internet-gateway-id $internet_gateway_id_ningxia --vpc-id $ningxia_vpc_id

# 7. 修改路由表
aws ec2 create-route --destination-cidr-block 0.0.0.0/0 --gateway-id $internet_gateway_id_ningxia --region cn-northwest-1 --route-table-id $ningxia_route_table_id1
aws ec2 create-route --destination-cidr-block 0.0.0.0/0 --gateway-id $internet_gateway_id_ningxia --region cn-northwest-1 --route-table-id $ningxia_route_table_id2

2) 创建实例资源

# 8. 创建业务服务器1
business_instance_id=$(aws ec2 run-instances --region cn-northwest-1 --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --instance-type t3.micro --key-name id_rsa_test.pub --security-group-ids $groupid_ningxia --subnet-id $ningxia_subnet_az1_id1 --associate-public-ip-address --query 'Instances[].InstanceId' --output text)
# 等待实例创建完成
aws ec2 wait instance-status-ok --instance-ids $business_instance_id --region cn-northwest-1
# 获得business_instance_ip
business_instance_ip=$(aws ec2 describe-instances --instance-ids "$business_instance_id" --region cn-northwest-1 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

# 9. 创建转发服务器
proxy_instance_id=$(aws ec2 run-instances --region cn-northwest-1 --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --instance-type t3.medium --key-name id_rsa_test.pub --security-group-ids $groupid_ningxia --subnet-id $ningxia_subnet_az1_id2 --associate-public-ip-address --query 'Instances[].InstanceId' --output text)
# 等待实例创建完成
aws ec2 wait instance-status-ok --instance-ids $proxy_instance_id --region cn-northwest-1
# 获得proxy_instance_ip
proxy_instance_ip=$(aws ec2 describe-instances --instance-ids "$proxy_instance_id" --region cn-northwest-1 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

# 创建好转发服务器,需要修改业务服务器1的路由表,将路由条目0.0.0.0/0的下一跳指向转发服务器的主网卡,这样所有流量都将流向转发服务器。

c. 配置转发服务器

1) 原理解释

iptables 中提供两种模式实现四层负载均衡,分别是 random(随机)和 nth(轮询)。

random 模式基于概率实现负载均衡,请参考如下示例说明:

iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017 -m statistic --mode random --probability 0.33  -j DNAT --to-destination 10.0.0.2:1234
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017 -m statistic --mode random --probability 0.5 -j DNAT --to-destination 10.0.0.3:1234
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017  -j DNAT --to-destination 10.0.0.4:1234

rules 说明:

第一条规则中,指定 –probability 0.33 ,则说明该规则有 33% 的概率会命中。

第二条规则也有 33% 的概率命中,因为规则中指定 –probability 0.5。 则命中的概率为:50% * (1 – 33%)=0.33。

第三条规则中,没有指定 –probability 参数,因此意味着当匹配走到第三条规则时,则一定命中,此时走到第三条规则的概率为:1 – 0.33 -0.33 ≈ 0.33。

由上可见,可以通过修改 –probability 参数调整规则命中率。

probability 计算规则说明:

假设有 n 个 server,则可以设定 n 条 rule 将流量均分到 n 个 server 上,其中 –probability 参数的值可通过以下公式计算得到:p=1/(n−i+1)

# 其中 i 代表规则的序号(第一条规则的序号为1)

# n 代表规则/server的总数

# p 代表第 i 条规则中 –probability 的参数值

nth 模式基于轮询实现负载均衡,请参考如下示例说明:

# every:每n个包匹配一次规则
# packet:从第p个包开始
 
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017 -m statistic --mode nth --every 1 --packet 0 -j DNAT --to-destination 10.0.0.1:1234
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017 -m statistic --mode nth --every 2 --packet 0  -j DNAT --to-destination 10.0.0.2:1234
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 27017 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 10.0.0.3:1234

rules 说明:

第一条规则是从第 0 个包开始计算,匹配第 1 个包

第二条规则是从第 0 个包开始计算,匹配第 2 个包

第三条规则是从第 0 个包开始计算,匹配第 3 个包

第 4 个包回到原点,再次重新匹配,以此反复循环

2) 部署转发服务器(这次实验采用 nth 方式进行)

为了实现多公网 IP 轮询,首先需要转发服务器拥有多个公网 IP,可以将 Elastic IP 与主网卡的多个 Secondary Private IP 进行绑定实现。又由于路由的原因,无法使用 Secondary Network Interface。所以,转发服务器可以拥有多少个公网 IP,将由实例的主网卡可以拥有多少个 Secondary Private IP 决定。这次实验使用的是 t3.medium,此实例类型主网卡可以拥有最多 6 个私网地址,去掉已经使用的 Primary Private IP,总计有 5 个 Secondary Private IP。具体哪种实例可以有多少 IP,请参考如下链接:Elastic network interfaces – Amazon Elastic Compute Cloud

# 10. 创建Secondary Private IP并绑定EIP
# 获取主网卡id
network_interface_id=$(aws ec2 describe-instances --region cn-northwest-1 --instance-ids $proxy_instance_id --query "Reservations[].Instances[].NetworkInterfaces[].NetworkInterfaceId" --output text)
# 分配私网地址
private_ip_list=$(aws ec2 assign-private-ip-addresses --region cn-northwest-1 --network-interface-id $network_interface_id --secondary-private-ip-address-count 5 --query "AssignedPrivateIpAddresses[].PrivateIpAddress" --output text)
# 申请并绑定EIP
count=1; for i in $private_ip_list;
do
  allocation_id=$(aws ec2 allocate-address --region cn-northwest-1 --query "AllocationId" --output text);
  allocation_id_list[$count]=$allocation_id;
  association_id=$(aws ec2 associate-address --region cn-northwest-1 --allocation-id $allocation_id --network-interface-id $network_interface_id --private-ip-address $i --output text)
  association_id_list[$count]=$association_id
  ((count++));
done

创建完 Secondary Private IP 并绑定 EIP 后,下一步就是关闭实例的 source/destination check 以及登录到转发服务器进行 iptables nth 规则的设置。

# 关闭source/destination check
aws ec2 modify-instance-attribute --region cn-northwest-1 --no-source-dest-check --instance-id $proxy_instance_id
# 登录转发服务器(以下命令通过root账号执行)
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 1 --packet 0 -j SNAT --to-source 10.0.1.184
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 2 --packet 0 -j SNAT --to-source 10.0.1.177
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 3 --packet 0 -j SNAT --to-source 10.0.1.9
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 4 --packet 0 -j SNAT --to-source 10.0.1.252
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 5 --packet 0 -j SNAT --to-source 10.0.1.46
iptables -t nat -I POSTROUTING -m statistic --mode nth --every 6 --packet 0 -j SNAT --to-source 10.0.1.63
yum install iptables-services -y 
service iptables save # 持久化规则

d. 方案 1 测试

登录到业务服务器 1,通过 curl ip.sb 获取访问公网的 IP 地址,可以看到是以轮训的方式获取到了不同的 IP 地址,而这些 IP 地址就是在转发服务器上绑定的 EIP。

e. 环境清理

下载方案 1 环境清理:按照“方案 1 环境清理”中记载的步骤进行清理。

2.2 方案 2:借助 Private Hosted Zone、Amazon Network Load Balancer 和 Nginx 实现

a. 架构图(其中业务服务器 2 仅作为展示,并不会真正创建)

  1. 通过 Private Hosted Zone 将要访问的网站域名进行劫持到 Network Load Balancer 的域名,这次实验中使用了 com 作为域名,访问的网站是 www.baidu.com,因此会将 www.baidu.com 劫持为 NLB 的域名。
  2. 请求通过 Peering Connection 送达到 Network Load Balancer。使用两个 VPC 的原因在于,如果业务服务器和 Nginx 服务器同属一个 VPC,那么访问的 baidu.com 就都会被劫持为 NLB 的域名,Nginx 就无法将请求转发到正确的地址。
  3. Network Load Balancer 监听 TCP 443 端口,并将请求转发到后端 Nginx 集群。使用 TCP 协议的原因在于,只需要将请求 443 的流量进行转发即可。
  4. Nginx 运行于 Stream 模式,在握手阶段拿到访问的域名,并转发到想要访问的网站。在这次实验中就是 baidu.com。
  5. Nginx 集群处于 Auto Scaling Group 中,根据请求流量进行动态扩缩容(这次实验不会实施,仅供参考)。

b. 环境搭建(执行命令的机器需要有足够的权限)

1) 创建网络资源

# 0. 进入screen模式,以保证环境变量不会因为退出Session而消失
screen

# 1. 创建VPC1和VPC2
ningxia_vpc1_id=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --region cn-northwest-1 --query "Vpc.VpcId" --output text)
ningxia_vpc2_id=$(aws ec2 create-vpc --cidr-block 172.31.0.0/16 --region cn-northwest-1 --query "Vpc.VpcId" --output text)
# 修改VPC2属性
aws ec2 modify-vpc-attribute --region cn-northwest-1 --vpc-id $ningxia_vpc2_id --enable-dns-hostnames
aws ec2 modify-vpc-attribute --region cn-northwest-1 --vpc-id $ningxia_vpc2_id --enable-dns-support

# 2. 创建Subnet
# 2.1 创建VPC1的子网
# 在AZ1中创建vpc1_ningxia_subnet_az1_id1(私有子网1)
vpc1_ningxia_subnet_az1_id1=$(aws ec2 create-subnet --cidr-block 10.0.0.0/24 --vpc-id $ningxia_vpc1_id --region cn-northwest-1 --availability-zone cn-northwest-1a --query "Subnet.SubnetId" --output text)
# 2.2 创建VPC2的子网
# 在AZ1中创建vpc2_ningxia_subnet_az1_id1(私有子网1)
vpc2_ningxia_subnet_az1_id1=$(aws ec2 create-subnet --cidr-block 172.31.0.0/24 --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --availability-zone cn-northwest-1a --query "Subnet.SubnetId" --output text)
# 在AZ2中创建vpc2_ningxia_subnet_az2_id2(私有子网2)
vpc2_ningxia_subnet_az2_id2=$(aws ec2 create-subnet --cidr-block 172.31.1.0/24 --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --availability-zone cn-northwest-1b --query "Subnet.SubnetId" --output text)
# 在AZ1中创建vpc2_ningxia_subnet_az1_id3(公有子网1)
vpc2_ningxia_subnet_az1_id3=$(aws ec2 create-subnet --cidr-block 172.31.2.0/24 --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --availability-zone cn-northwest-1a --query "Subnet.SubnetId" --output text)
# 在AZ2中创建vpc2_ningxia_subnet_az2_id4(公有子网2)
vpc2_ningxia_subnet_az2_id4=$(aws ec2 create-subnet --cidr-block 172.31.3.0/24 --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --availability-zone cn-northwest-1b --query "Subnet.SubnetId" --output text)

# 3. 创建路由表
# 3.1 创建VPC1的路由表
# 创建vpc1_ningxia_route_table_id1(私有子网路由表)
vpc1_ningxia_route_table_id1=$(aws ec2 create-route-table --vpc-id $ningxia_vpc1_id --region cn-northwest-1 --query "RouteTable.RouteTableId" --output text)
# 3.2 创建VPC2的路由表
# 创建vpc2_ningxia_route_table_id1(私有子网路由表)
vpc2_ningxia_route_table_id1=$(aws ec2 create-route-table --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --query "RouteTable.RouteTableId" --output text)
# 创建vpc2_ningxia_route_table_id2(公有子网路由表)
vpc2_ningxia_route_table_id2=$(aws ec2 create-route-table --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --query "RouteTable.RouteTableId" --output text)

# 4. 更改子网路由表
# 4.1 更改VPC1中的子网路由表
aws ec2 associate-route-table --route-table-id $vpc1_ningxia_route_table_id1 --subnet-id $vpc1_ningxia_subnet_az1_id1 --region cn-northwest-1
# 4.2 更改VPC2中的子网路由表
# 4.2.1 更改VPC2中的私有子网路由表
aws ec2 associate-route-table --route-table-id $vpc2_ningxia_route_table_id1 --subnet-id $vpc2_ningxia_subnet_az1_id1 --region cn-northwest-1
aws ec2 associate-route-table --route-table-id $vpc2_ningxia_route_table_id1 --subnet-id $vpc2_ningxia_subnet_az2_id2 --region cn-northwest-1
# 4.2.2 更改VPC2中的公有子网路由表
aws ec2 associate-route-table --route-table-id $vpc2_ningxia_route_table_id2 --subnet-id $vpc2_ningxia_subnet_az1_id3 --region cn-northwest-1
aws ec2 associate-route-table --route-table-id $vpc2_ningxia_route_table_id2 --subnet-id $vpc2_ningxia_subnet_az2_id4 --region cn-northwest-1

# 5. 修改安全组
# 5.1 修改VPC1的安全组
# 获取VPC1的groupid
vpc1_groupid_ningxia=$(aws ec2 describe-security-groups --region cn-northwest-1 --filter "Name=vpc-id,Values=$ningxia_vpc1_id" --query "SecurityGroups[].GroupId" --output text)
# 基于获取的groupid,修改安全组
aws ec2 authorize-security-group-ingress --group-id "$vpc1_groupid_ningxia" --protocol all --port all --cidr "0.0.0.0/0" --region cn-northwest-1
# 5.2 修改VPC2的安全组
# 获取VPC2的groupid
vpc2_groupid_ningxia=$(aws ec2 describe-security-groups --region cn-northwest-1 --filter "Name=vpc-id,Values=$ningxia_vpc2_id" --query "SecurityGroups[].GroupId" --output text)
# 基于获取的groupid,修改安全组
aws ec2 authorize-security-group-ingress --group-id "$vpc2_groupid_ningxia" --protocol all --port all --cidr "0.0.0.0/0" --region cn-northwest-1

# 6. 创建并附加Internet Gateway
# 6.1 VPC1中创建并附加Internet Gateway
vpc1_internet_gateway_id_ningxia=$(aws ec2 create-internet-gateway --region cn-northwest-1 --query "InternetGateway.InternetGatewayId" --output text)
aws ec2 attach-internet-gateway --region cn-northwest-1 --internet-gateway-id $vpc1_internet_gateway_id_ningxia --vpc-id $ningxia_vpc1_id
# 6.2 VPC2中创建并附加Internet Gateway
vpc2_internet_gateway_id_ningxia=$(aws ec2 create-internet-gateway --region cn-northwest-1 --query "InternetGateway.InternetGatewayId" --output text)
aws ec2 attach-internet-gateway --region cn-northwest-1 --internet-gateway-id $vpc2_internet_gateway_id_ningxia --vpc-id $ningxia_vpc2_id

# 7. 修改路由表
# 7.1 修改VPC1中的路由表
aws ec2 create-route --destination-cidr-block 0.0.0.0/0 --gateway-id $vpc1_internet_gateway_id_ningxia --region cn-northwest-1 --route-table-id $vpc1_ningxia_route_table_id1
# 7.2 修改VPC2中的路由表
aws ec2 create-route --destination-cidr-block 0.0.0.0/0 --gateway-id $vpc2_internet_gateway_id_ningxia --region cn-northwest-1 --route-table-id $vpc2_ningxia_route_table_id1
aws ec2 create-route --destination-cidr-block 0.0.0.0/0 --gateway-id $vpc2_internet_gateway_id_ningxia --region cn-northwest-1 --route-table-id $vpc2_ningxia_route_table_id2

# 8. 创建Peering Connection
vpc_peering_connection_id=$(aws ec2 create-vpc-peering-connection --region cn-northwest-1 --vpc-id $ningxia_vpc1_id --peer-vpc-id $ningxia_vpc2_id --peer-region cn-northwest-1 --query "VpcPeeringConnection.VpcPeeringConnectionId" --output text)
aws ec2 accept-vpc-peering-connection --region cn-northwest-1 --vpc-peering-connection-id $vpc_peering_connection_id --query "VpcPeeringConnection.VpcPeeringConnectionId" --output text
# 8.1 添加路由,使VPC1和VPC2可以互通
aws ec2 create-route --region cn-northwest-1 --route-table-id $vpc1_ningxia_route_table_id1 --destination-cidr-block 172.31.0.0/16 --vpc-peering-connection-id $vpc_peering_connection_id
aws ec2 create-route --region cn-northwest-1 --route-table-id $vpc2_ningxia_route_table_id1 --destination-cidr-block 10.0.0.0/16 --vpc-peering-connection-id $vpc_peering_connection_id

2) 创建实例资源

# 9. 创建Nginx实例
# 9.1 创建Nginx1
nginx_id1=$(aws ec2 run-instances --region cn-northwest-1 --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --instance-type t3.micro --key-name id_rsa_test.pub --security-group-ids $vpc2_groupid_ningxia --subnet-id $vpc2_ningxia_subnet_az1_id3 --associate-public-ip-address --query 'Instances[].InstanceId' --output text)
# 等待实例创建完成
aws ec2 wait instance-status-ok --instance-ids $nginx_id1 --region cn-northwest-1
# 获得nginx_ip1
nginx_ip1=$(aws ec2 describe-instances --instance-ids "$nginx_id1" --region cn-northwest-1 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
# 9.2 创建Nginx2
nginx_id2=$(aws ec2 run-instances --region cn-northwest-1 --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --instance-type t3.micro --key-name id_rsa_test.pub --security-group-ids $vpc2_groupid_ningxia --subnet-id $vpc2_ningxia_subnet_az2_id4 --associate-public-ip-address --query 'Instances[].InstanceId' --output text)
# 等待实例创建完成
aws ec2 wait instance-status-ok --instance-ids $nginx_id2 --region cn-northwest-1
# 获得nginx_ip2
nginx_ip2=$(aws ec2 describe-instances --instance-ids "$nginx_id2" --region cn-northwest-1 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

# 10. 创建业务服务器1
business_instance_id=$(aws ec2 run-instances --region cn-northwest-1 --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --instance-type t3.micro --key-name id_rsa_test.pub --security-group-ids $vpc1_groupid_ningxia --subnet-id $vpc1_ningxia_subnet_az1_id1 --associate-public-ip-address --query 'Instances[].InstanceId' --output text)
# 等待实例创建完成
aws ec2 wait instance-status-ok --instance-ids $business_instance_id --region cn-northwest-1
# 获得business_instance_ip
business_instance_ip=$(aws ec2 describe-instances --instance-ids "$business_instance_id" --region cn-northwest-1 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

c. 配置 Nginx 实例

Nginx 在 1.9.0 版本新增了 stream 模块,用以实现四层协议的代理。对于 TLS 请求,会在 TLS 建连阶段拿到需要访问的域名,并将请求转发到正确的域名。

默认情况下,Nginx 并不包含此模块。因此需要对 Nginx 进行编译安装并在编译时增加”–with-stream”参数。

下载ProxyInstanceDeploymentProcedure,按照ProxyInstanceDeploymentProcedure中记载的步骤进行配置。

d. 创建和配置 Network Load Balancer

# 11.  创建和配置NLB
# 创建目标组
target_group_arn=$(aws elbv2 create-target-group --name my-targets --protocol TCP --port 443 --target-type instance --vpc-id $ningxia_vpc2_id --region cn-northwest-1 --query "TargetGroups[].TargetGroupArn" --output text)
# 注册实例
aws elbv2 register-targets --target-group-arn $target_group_arn --targets Id=$nginx_id1,Port=443 --region cn-northwest-1
aws elbv2 register-targets --target-group-arn $target_group_arn --targets Id=$nginx_id2,Port=443 --region cn-northwest-1
# 创建NLB
load_balancer_arn=$(aws elbv2 create-load-balancer --name my-load-balancer --scheme internal --type network --subnets $vpc2_ningxia_subnet_az1_id1 $vpc2_ningxia_subnet_az2_id2 --region cn-northwest-1 --query LoadBalancers[].LoadBalancerArn --output text)
# 等待NLB创建完成
aws elbv2 wait load-balancer-available --load-balancer-arns $load_balancer_arn --region cn-northwest-1
# 创建NLB监听器
listener_arn=$(aws elbv2 create-listener --load-balancer-arn $load_balancer_arn --protocol TCP --port 443 --default-actions Type=forward,TargetGroupArn=$target_group_arn --region cn-northwest-1 --query "Listeners[].ListenerArn" --output text)

e. 创建和配置 Private Hosted Zone

# 12. 创建和配置Private Hosted Zone
private_hosted_zone_id=$(aws route53 create-hosted-zone --name baidu.com --vpc VPCRegion=cn-northwest-1,VPCId=$ningxia_vpc1_id --caller-reference "`date '+%Y_%m_%d %H:%M:%S'`" --hosted-zone-config Comment="ForBlog",PrivateZone=true --query "HostedZone.Id" --output text)

echo $load_balancer_arn
aws elbv2 describe-load-balancers --query 'LoadBalancers[?LoadBalancerArn==`arn:aws-cn:elasticloadbalancing:cn-northwest-1:332433839685:loadbalancer/net/my-load-balancer/503cc27dc92ff737`][DNSName,CanonicalHostedZoneId]' # 问号后面填写上一个命令的输出,并将这个命令的输出作为值填入sample.json中的HostedZoneId和DNSName项

cat > sample.json << EOF
{
  "Comment": "Creating Alias resource record sets in Route 53",
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "www.baidu.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "ZQEIKTCZ8352D",
          "DNSName": "my-load-balancer-503cc27dc92ff737.elb.cn-northwest-1.amazonaws.com.cn",
          "EvaluateTargetHealth": false
        }
      }
    }
  ]
}
EOF

aws route53 change-resource-record-sets --hosted-zone-id $private_hosted_zone_id --change-batch file://sample.json # 劫持www.baidu.com的dns请求,并指向NLB的域名

f. 方案 2 测试

登录业务服务器 1,通过如下命令进行测试:for i in {1..20}; do curl -I https://www.baidu.com; done

观看 Nginx1 的日志:

观看 Nginx2 的日志:

由此可见,每次访问 www.baidu.com 都是通过不同的 IP 地址进行的访问。

g. 环境清理

下载方案 2 环境清理:,按照”方案 2 环境清理”中记载的步骤进行清理

3.方案对比

方案 1 方案 2
优势 配置简单,费用低,具体费用取决于选择的实例类型 云原生的方案,可以集成亚马逊云科技的各个服务;
方案中的每个组件都具有高可用性,可以应对故障;
Nginx 实例可以线性扩展,因此私有实例可以使用无限多个公网 IP。
劣势 无法同亚马逊云科技的监控、告警和事件等服务集成,出现问题时无法第一时间发现;
每台私有实例都只能指向一台转发服务器,无冗余能力;
私有实例可以使用的公网 IP 数量取决于实例类型,不具备线性扩展能力。
配置复杂,由于使用了更多的服务,费用高于方案 1

4.总结

在这篇文章中,介绍了两种不同方案以实现私有实例轮询使用多公网 IP 访问公网,并在最后进行了两种方案的对比,以帮助大家更好的选择适合自己的方案。

本篇作者

郑毅

西云数据解决方案架构师,曾就职于外企、互联网企业和央企,擅长系统交付、运维和解决方案设计,对于传统 IT 技术以及云计算技术有深入了解和丰富的实战经验。