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 规则的设置。
添加网卡
sudo ip addr add 10.0.1.98/24 dev ens5
sudo ip addr add 10.0.1.97/24 dev ens5
sudo ip addr add 10.0.1.96/24 dev ens5
echo 'ip addr add 172.31.2.249/20 dev ens5' >> /etc/rc.local ## 2、加入开机启动
关闭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 # 持久化规则