使用 Cloud NAT 动态端口分配

1. 概览

动态端口分配 (DPA) 是 Cloud NAT 中的一项新功能。启用 DPA 后,Cloud NAT 会根据实例的需求动态扩缩实例的端口分配。DPA 配置了端口数下限和上限,因此绝不会将端口数缩减到低于下限,也不会将端口数扩充到超出上限。这样一来,NAT 网关后面的一些实例便可动态扩容其连接数,而无需为 Cloud NAT 后面的所有实例分配更多端口。

如果不使用 DPA,Cloud NAT 后面的所有实例都会分配相同数量的端口,无论使用情况如何,如 minPortsPerVm 参数所定义。

如需了解详情,请参阅有关 NAT DPA 的文档部分

学习内容

  • 如何设置 Cloud NAT 网关以准备 DPA。
  • 如何在没有 DPA 的情况下检查端口分配。
  • 如何为 NAT 网关启用和配置 DPA。
  • 如何通过运行并行出站连接来观察 DPA 的效果。
  • 如何向启用了 DPA 的 NAT 网关添加 NAT 规则。
  • 如何通过运行与多个目的地的出站连接来查看 DPA 在规则下的行为。

所需条件

  • Google Compute Engine 基础知识
  • 网络组建和管理以及 TCP/IP 基础知识
  • Unix/Linux 命令行基础知识
  • 最好先完成 Google Cloud 网络导览,例如“Google Cloud 中的网络”实验。
  • 启用了“Alpha 版访问权限”的 Google Cloud 项目。
  • 了解 Cloud NAT 的基本知识

2. 使用 Google Cloud 控制台和 Cloud Shell

在本实验中,我们将使用 Google Cloud 控制台和 Cloud Shell 与 GCP 进行交互。

Google Cloud Console

您可以通过 https://console.cloud.google.com 访问 Cloud 控制台。

75eef5f6fd6d7e41.png

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串,您可以随时对其进行更新。
  • 项目 ID 在所有 Google Cloud 项目中必须是唯一的,并且不可变(一经设置便无法更改)。Cloud Console 会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(它通常标识为 PROJECT_ID),因此如果您不喜欢某个 ID,请再生成一个随机 ID,还可以尝试自己创建一个,并确认是否可用。然后,项目创建后,ID 会处于“冻结”状态。
  • 第三个值是一些 API 使用的项目编号。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud Console 中启用结算功能,才能使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。要关闭资源以避免产生超出本教程范围的费用,请按照此 Codelab 末尾提供的任何“清理”说明操作。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

在 GCP 控制台中,点击右上角工具栏上的 Cloud Shell 图标:

bce75f34b2c53987.png

预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

f6ef2b5f13479f3a.png

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。只需一个浏览器,即可完成本实验中的所有工作。

3. 实验设置

在本实验中,您将使用一个项目,并创建两个 VPC,每个 VPC 中都有一个子网。您将预留外部 IP 地址,然后创建并配置 Cloud NAT 网关(使用 Cloud Router 路由器)、两个生产者实例以及两个消费者实例。验证默认 Cloud NAT 行为后,您将启用动态端口分配并验证其行为。最后,您还将配置 NAT 规则,并观察 DPA 与 NAT 规则之间的互动。

网络架构概览:

a21caa6c333909d8.png

4. 预留外部 IP 地址

我们来预留本实验中要使用的所有外部 IP 地址。这有助于您在服务使用方 VPC 和服务提供方 VPC 中编写所有相关的 NAT 和防火墙规则。

在 Cloud Shell 中:

gcloud compute addresses  create nat-address-1 nat-address-2 \
 producer-address-1 producer-address-2 --region us-east4

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/nat-address-1].
Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/nat-address-2].
Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/producer-address-1].
Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/producer-address-2].

将预留的 IP 地址填充为环境变量。

export natip1=`gcloud compute addresses list --filter name:nat-address-1 --format="get(address)"`
export natip2=`gcloud compute addresses list --filter name:nat-address-2 --format="get(address)"`
export producerip1=`gcloud compute addresses list --filter name:producer-address-1 --format="get(address)"`
export producerip2=`gcloud compute addresses list --filter name:producer-address-2 --format="get(address)"`

无需输出,但要确认地址已正确填充。我们来输出所有环境变量的值。

env | egrep '^(nat|producer)ip[1-3]'

输出:

producerip1=<Actual Producer IP 1>
producerip2=<Actual Producer IP 2>
natip1=<NAT IP 1>
natip2=<NAT IP 2>

5. 提供方 VPC 和实例设置。

现在,我们将为生产者资源创建资源。在提供方 VPC 中运行的实例将使用两个公共 IP 地址“producer-address-1”和“producer-address-2”提供面向互联网的服务。

首先,我们来创建 VPC。在 Cloud Shell 中:

gcloud compute networks create producer-vpc --subnet-mode custom

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project-ID>/global/networks/producer-vpc].
NAME      SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
producer-vpc  CUSTOM       REGIONAL

Instances on this network will not be reachable until firewall rules
are created. As an example, you can allow all internal traffic between
instances as well as SSH, RDP, and ICMP by running:

$ gcloud compute firewall-rules create <FIREWALL_NAME> --network producer-vpc --allow tcp,udp,icmp --source-ranges <IP_RANGE>
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network producer-vpc --allow tcp:22,tcp:3389,icmp

接下来,我们将在 us-east4 中创建子网。在 Cloud Shell 中:

gcloud compute networks subnets create prod-net-e4 \
   --network producer-vpc --range 10.0.0.0/24 --region us-east4

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/subnetworks/prod-net-e4].
NAME         REGION    NETWORK       RANGE        STACK_TYPE  IPV6_ACCESS_TYPE  IPV6_CIDR_RANGE  EXTERNAL_IPV6_CIDR_RANGE
prod-net-e4  us-east4  producer-vpc  10.0.0.0/24  IPV4_ONLY

接下来,我们来创建 VPC 防火墙规则,以允许 NAT IP 地址通过端口 8080 访问提供方实例。

对于第一条规则,请在 Cloud Shell 中执行以下操作:

gcloud compute firewall-rules create producer-allow-80 \
  --network producer-vpc --allow tcp:80 \
  --source-ranges $natip1,$natip2

输出:

Creating firewall...⠹Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/firewalls/producer-allow-80].
Creating firewall...done.
NAME                 NETWORK       DIRECTION  PRIORITY  ALLOW     DENY  DISABLED
producer-allow-80    producer-vpc  INGRESS    1000      tcp:80          False

下一步是创建两个提供方实例。

生产者实例将运行简单的 nginx 代理部署。

为了快速预配包含所有必需软件的实例,我们将使用启动脚本创建实例,该脚本使用 Debian APT 软件包管理器安装 nginx。

为了能够编写 NAT 规则,我们将为每个实例预配不同的预留 IP 地址。

创建第一个实例。在 Cloud Shell 中:

gcloud compute instances create producer-instance-1 \
--zone=us-east4-a --machine-type=e2-medium \
--network-interface=address=producer-address-1,network-tier=PREMIUM,subnet=prod-net-e4 \
--metadata startup-script="#! /bin/bash
sudo apt update
sudo apt install -y nginx
mkdir /var/www/html/nginx/
cat <<EOF > /var/www/html/nginx/index.html
<html><body><h1>This is producer instance 1</h1>
</body></html>
EOF"

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/zones/us-east4-a/instances/producer-instance-1].
NAME                 ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
producer-instance-1  us-east4-a  e2-medium                  10.0.0.2     <Producer IP1>  RUNNING

然后创建第二个实例。在 Cloud Shell 中:

gcloud compute instances create producer-instance-2 \
--zone=us-east4-a --machine-type=e2-medium \
--network-interface=address=producer-address-2,network-tier=PREMIUM,subnet=prod-net-e4 \
--metadata startup-script="#! /bin/bash
sudo apt update
sudo apt install -y nginx
mkdir /var/www/html/nginx/
cat <<EOF > /var/www/html/nginx/index.html
<html><body><h1>This is producer instance 2</h1>
</body></html>
EOF"

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/zones/us-east4-a/instances/producer-instance-2].
NAME                 ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
producer-instance-2  us-east4-a  e2-medium                  10.0.0.3     <Producer IP2>  RUNNING

6. 设置使用方 VPC、Cloud NAT 和实例

现在,您已创建提供方服务,接下来可以创建使用方 VPC 及其 Cloud NAT 网关了。

创建 VPC 和子网后,我们将添加一条简单的入站防火墙规则,以允许 IAP 访问 TCP 源 IP 地址范围。这样,我们就可以使用 gcloud 直接通过 SSH 连接到消费者实例。

然后,我们将以手动分配模式创建一个简单的 Cloud NAT 网关,并将其与预留地址“nat-address-1”相关联。在本 Codelab 的后续部分中,我们将更新网关的配置以启用动态端口分配,并在稍后添加自定义规则。

首先,我们来创建 VPC。在 Cloud Shell 中:

gcloud compute networks create consumer-vpc --subnet-mode custom

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/networks/consumer-vpc].
NAME          SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
consumer-vpc  CUSTOM       REGIONAL

Instances on this network will not be reachable until firewall rules
are created. As an example, you can allow all internal traffic between
instances as well as SSH, RDP, and ICMP by running:

$ gcloud compute firewall-rules create <FIREWALL_NAME> --network consumer-vpc --allow tcp,udp,icmp --source-ranges <IP_RANGE>
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network consumer-vpc --allow tcp:22,tcp:3389,icmp

接下来,我们将在 us-east4 中创建一个子网。在 Cloud Shell 中:

gcloud compute networks subnets create cons-net-e4 \
   --network consumer-vpc --range 10.0.0.0/24 --region us-east4

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/subnetworks/cons-net-e4].
NAME         REGION    NETWORK       RANGE        STACK_TYPE  IPV6_ACCESS_TYPE  IPV6_CIDR_RANGE  EXTERNAL_IPV6_CIDR_RANGE
cons-net-e4  us-east4  consumer-vpc  10.0.0.0/24  IPV4_ONLY

接下来,我们来创建 VPC 防火墙规则,以允许 IAP 范围地址通过端口 22 访问使用方实例。

对于第一个防火墙规则,请在 Cloud Shell 中运行以下命令:

gcloud compute firewall-rules create consumer-allow-iap \
  --network consumer-vpc --allow tcp:22 \
  --source-ranges 35.235.240.0/20

输出:

Creating firewall...⠹Created [https://www.googleapis.com/compute/v1/projects/<Project-ID>/global/firewalls/consumer-allow-iap].
Creating firewall...done.
NAME                 NETWORK       DIRECTION  PRIORITY  ALLOW     DENY  DISABLED
consumer-allow-iap  consumer-vpc  INGRESS    1000      tcp:22        False

在创建 NAT 网关之前,我们需要先创建一个 Cloud Router 实例(我们使用专用 ASN 编号,但它与本实验的活动无关)。在 Cloud Shell 中:

gcloud compute routers create consumer-cr \
--region=us-east4 --network=consumer-vpc \
 --asn=65501

输出:

Creating router [consumer-cr]...done.
NAME         REGION       NETWORK
consumer-cr  us-east4  consumer-vpc

然后,创建 NAT 网关实例。在 Cloud Shell 中:

gcloud compute routers nats create consumer-nat-gw \
    --router=consumer-cr \
    --router-region=us-east4 \
    --nat-all-subnet-ip-ranges \
    --nat-external-ip-pool=nat-address-1

输出:

Creating NAT [consumer-nat-gw] in router [consumer-cr]...done.

请注意,默认情况下,创建的 Cloud NAT 网关的 minPortsPerVm 设置为 64

创建使用方测试实例。我们在此处填充预留的制作方 IP,以便稍后在实例中引用它们。在 Cloud Shell 中:

gcloud compute instances create consumer-instance-1 --zone=us-east4-a \
--machine-type=e2-medium --network-interface=subnet=cons-net-e4,no-address \
--metadata=producer-service-ip1=$producerip1,producer-service-ip2=$producerip2

gcloud compute instances create consumer-instance-2 --zone=us-east4-a \
--machine-type=e2-medium --network-interface=subnet=cons-net-e4,no-address \
--metadata=producer-service-ip1=$producerip1,producer-service-ip2=$producerip2

输出:

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/zones/us-east4-a/instances/consumer-instance-1].
NAME                ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP  STATUS
consumer-instance-1  us-east4-a  e2-medium                  10.0.0.2                  RUNNING

Created [https://www.googleapis.com/compute/v1/projects/<Project ID>/zones/us-east4-a/instances/consumer-instance-2].
NAME                ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP  STATUS
consumer-instance-2  us-east4-a  e2-medium                  10.0.0.3                  RUNNING

7. 验证默认 Cloud NAT 行为

此时,消费者实例使用默认的 Cloud NAT 行为,即使用相同的预留 IP“nat-address-1”与所有外部地址通信。Cloud NAT 也尚未启用 DPA。

我们来运行以下命令,验证 Cloud NAT 为我们的消费者实例分配了哪些端口

gcloud  compute routers get-nat-mapping-info consumer-cr --region=us-east4

示例输出

---
instanceName: consumer-instance-1
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Consumer IP1>:1024-1055
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
- natIpPortRanges:
  - <NAT Consumer IP1>:32768-32799
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
---
instanceName: consumer-instance-2
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1056-1087
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3
- natIpPortRanges:
  - <NAT Address IP1>:32800-32831
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3

从上面的输出中可以看出,Cloud NAT 已为每个实例从同一外部 IP nat-address-1 分配了 64 个端口

在启用 DPA 之前,我们先验证可以并行打开多少个连接。

通过 SSH 连接到第一个消费者实例。在 Cloud Shell 中:

gcloud compute ssh consumer-instance-1 --zone=us-east4-a

您现在应该位于实例 shell 中。

示例输出(为简洁起见,完整输出已被截断)

External IP address was not found; defaulting to using IAP tunneling.
...
...
<username>@consumer-instance-1:~$

从使用方实例中,我们先获取两个提供方 IP,并将其填充为环境变量

export producerip1=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip1" -H "Metadata-Flavor: Google"`

export producerip2=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip2" -H "Metadata-Flavor: Google"`

然后,尝试向两个 Producer 实例发出 curl 请求,确保可以成功访问它们。

<username>@consumer-instance-1:~$ curl http://$producerip1/nginx/
<html><body><h1>This is producer instance 1</h1>
</body></html>
<username>@consumer-instance-1:~$ curl http://$producerip2/nginx/
<html><body><h1>This is producer instance 2</h1>
</body></html>

现在,让我们尝试通过循环运行 curl 来创建与某个生产者实例的许多并行连接。请注意,Cloud NAT 不允许在 2 分钟内重复使用已关闭的套接字。因此,只要我们能在 2 分钟内循环完成所有连接尝试,就能以这种方式模拟并行连接。

在实例 SSH 会话中运行以下命令

while true; do for i in {1..64}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip1/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

您应该能够成功打开 64 个并行连接,并且脚本应输出以下内容

Connection # 64 successful

Loop Done, Sleeping for 150s
Connection # 64 successful

Loop Done, Sleeping for 150s

为了表明我们无法超出 64 个并行连接,请先等待 2 分钟,以便清除所有旧套接字。然后,将同一单行命令调整为以下内容,并重新运行

while true; do for i in {1..70}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip1/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

您现在应该会看到以下输出

Connection # 64 successful
Connection # 65 failed
Connection # 66 failed
Connection # 67 failed
Connection # 68 failed
Connection # 69 failed
Connection # 70 failed

Loop Done, Sleeping for 150s

这表明,虽然前 64 个连接成功,但由于端口不可用,剩余的 6 个连接失败。

那我们就来解决这个问题吧。退出 SSH shell,然后在下一部分中启用 DPA。

8. 启用 DPA 并验证其行为

运行以下 gcloud 命令,该命令可启用 DPA,并将每个虚拟机的最小端口分配数设置为 64,最大端口分配数设置为 1024。

gcloud alpha compute routers nats update consumer-nat-gw --router=consumer-cr \
--region=us-east4 --min-ports-per-vm=64 --max-ports-per-vm=1024 \
--enable-dynamic-port-allocation

该命令会输出以下内容

Updating nat [consumer-nat-gw] in router [consumer-cr]...done.

现在,我们重新运行 get-nat-mapping-info,以确认两个实例仍然只分配了 64 个端口

gcloud  compute routers get-nat-mapping-info consumer-cr --region=us-east4

示例输出(为简洁起见,已截断)

---
instanceName: consumer-instance-1
...
  - <NAT Consumer IP1>:1024-1055
  numTotalNatPorts: 32
...
- natIpPortRanges:
  - <NAT Consumer IP1>:32768-32799
  numTotalNatPorts: 32
...
---
instanceName: consumer-instance-2
...
  - <NAT Address IP1>:1056-1087
  numTotalNatPorts: 32
...
  - <NAT Address IP1>:32800-32831
  numTotalNatPorts: 32
...

由于实例尚未主动使用任何端口,因此端口分配方面没有太大变化。

让我们通过 SSH 重新登录到实例:

gcloud compute ssh consumer-instance-1 --zone=us-east4-a

重新导出制作者 IP 环境变量。

export producerip1=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip1" -H "Metadata-Flavor: Google"`

export producerip2=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip2" -H "Metadata-Flavor: Google"`

并重新运行之前的循环来模拟并行连接:

while true; do for i in {1..70}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip1/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

现在,我们应该会看到以下输出内容

Connection # 64 successful
Connection # 65 failed

Connection # 66 failed
Connection # 70 successful
Loop Done, Sleeping for 150s

那么,这里发生了什么?Cloud NAT 会在端口使用量增加时逐步增加端口分配,但需要一些时间才能在整个网络层中完成编程。因此,在成功完成其余连接尝试之前,我们会看到 1-3 次连接超时。

我们为 curl 指定了较短的超时时间(5 秒),但具有较长超时时间的应用程序应该能够在 DPA 增加端口分配时成功完成连接。

当我们运行循环进行 1024 次连接尝试时,可以更清楚地看到这种升温行为,如下所示

while true; do for i in {1..1024}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip1/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

现在,我们预计会看到以下输出

Connection # 64 successful
Connection # 65 failed

Connection # 66 failed
Connection # 129 successful
Connection # 130 failed

Connection # 131 failed
Connection # 258 successful
Connection # 259 failed

Connection # 260 failed
Connection # 515 successful
Connection # 516 failed

Connection # 1024 successful
Loop Done, Sleeping for 150s

由于 Cloud NAT 以 2 的幂次方分配端口,基本上每一步都会使分配量翻倍,因此我们看到连接超时问题主要出现在 64 到 1024 之间的 2 的幂次方附近。

由于我们将 maxPortsPerVM 设置为 1024,因此我们预计无法建立超过 1024 个连接。我们可以通过重新运行 curl 循环(等待 2 分钟以重置过时的端口)来测试这一点,并将计数设置为高于 1024。

while true; do for i in {1..1035}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip1/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

正如预期的那样,输出显示超过 1024 的连接开始失败

<truncated output>
...
Connection # 1028 successful
Connection # 1029 failed
Connection # 1030 failed
Connection # 1031 failed
Connection # 1032 failed
Connection # 1033 failed
Connection # 1034 failed
Connection # 1035 failed
...
Loop Done, Sleeping for 150s

通过将 maxPortsPerVM 设置为 1024,我们指示 Cloud NAT 永远不要将每个虚拟机的端口分配量扩展到 1024 以上。

如果我们退出 SSH 会话并快速重新运行 get-nat-mapping-info,则可以看到分配的额外端口

gcloud  compute routers get-nat-mapping-info consumer-cr --region=us-east4

并观察以下输出

---
instanceName: consumer-instance-1
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1024-1055
  - <NAT Address IP1>1088-1119
  -<NAT Address IP1>:1152-1215
  - <NAT Address IP1>:1280-1407
  - <NAT Address IP1>:1536-1791
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 512
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
- natIpPortRanges:
  - <NAT Address IP1>:32768-32799
  - <NAT Address IP1>:32832-32863
  - <NAT Address IP1>:32896-32959
  - <NAT Address IP1>:33024-33151
  - <NAT Address IP1>:33536-33791
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 512
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
---
instanceName: consumer-instance-2
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1056-1087
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3
- natIpPortRanges:
  - <NAT Address IP1>:32800-32831
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3

请注意,consumer-instance-1 分配了 1024 个端口,而 consumer-instance-2 仅分配了 64 个端口。在 DPA 之前,这很难实现,而这恰恰凸显了 DPA 对 Cloud NAT 的强大作用。

如果您等待 2 分钟后再重新运行 get-nat-mapping-info 命令,您会发现 consumer-instance-1 恢复到其最小值,即仅分配了 64 个端口。不仅展示了 DPA 增加端口分配的能力,还展示了在不使用时释放端口的能力,以便同一 NAT 网关后面的其他实例可以潜在地使用这些端口。

9. 使用 DPA 测试 Cloud NAT 规则

我们最近还发布了 Cloud NAT 的 NAT 规则功能,让客户能够编写规则,针对特定外部目标使用特定 NAT IP。如需了解详情,请参阅 NAT 规则文档页面

在本练习中,我们将观察 DPA 和 NAT 规则之间的互动。我们先定义一条 NAT 规则,以便在访问 producer-address-2 时使用 nat-address-2

运行以下 gcloud 命令,该命令使用以下参数创建 NAT 规则

gcloud alpha compute routers nats rules create 100 \
 --match='destination.ip == "'$producerip2'"' \
 --source-nat-active-ips=nat-address-2 --nat=consumer-nat-gw \
 --router=consumer-cr --router-region=us-east4

您应该会看到以下输出内容

Updating nat [consumer-nat-gw] in router [consumer-cr]...done.

现在,我们重新运行 get-nat-mapping-info,看看新的 NAT 规则的效果。

gcloud alpha compute routers get-nat-mapping-info consumer-cr --region=us-east4

该命令应输出以下内容

---
instanceName: consumer-instance-1
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1024-1055
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:1024-1055
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
- natIpPortRanges:
  - <NAT Address IP1>:32768-32799
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:32768-32799
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2

请注意,现在我们已在 ruleMappings 层次结构下专门为 nat-address-2 分配了额外的端口(同样为指定的最小值 64)。

那么,如果实例打开了许多与 NAT 规则指定的目标之间的连接,会发生什么情况?让我们来一探究竟。

让我们通过 SSH 重新登录到实例:

gcloud compute ssh consumer-instance-1 --zone=us-east4-a

重新导出制作者 IP 环境变量。

export producerip1=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip1" -H "Metadata-Flavor: Google"`

export producerip2=`curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/producer-service-ip2" -H "Metadata-Flavor: Google"`

现在,我们再次针对 producerip2 运行 curl 循环

while true; do for i in {1..1024}; do curl -s -o /dev/null --connect-timeout 5 http://$producerip2/nginx/; if [ $? -ne 0 ] ; then echo -e "\nConnection # $i failed" ; else echo -en "\rConnection # $i successful"; fi; done; echo -e "\nLoop Done, Sleeping for 150s"; sleep 150; done

您应该会看到类似于以下内容的输出

Connection # 64 successful
Connection # 65 failed

Connection # 66 failed
Connection # 129 successful
Connection # 130 failed

Connection # 131 failed
Connection # 258 successful
Connection # 259 failed

Connection # 260 failed
Connection # 515 successful
Connection # 516 failed

Connection # 1024 successful
Loop Done, Sleeping for 150s

基本上与上一个测试相同。我们退出实例的 SSH 会话,然后再次查看 NAT 映射。

gcloud alpha compute routers get-nat-mapping-info consumer-cr --region=us-east4

该命令应输出以下内容

---
instanceName: consumer-instance-1
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1024-1055
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:1024-1055
    - <NAT Address IP2>:1088-1119
    - <NAT Address IP2>:1152-1215
    - <NAT Address IP2>:1280-1407
    - <NAT Address IP2>:1536-1791
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 512
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
- natIpPortRanges:
  - <NAT Address IP1>:32768-32799
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:32768-32799
    - <NAT Address IP2>:32832-32863
    - <NAT Address IP2>:32896-32959
    - <NAT Address IP2>:33024-33151
    - <NAT Address IP2>:33280-33535
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 512
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
---
instanceName: consumer-instance-2
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1056-1087
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:1056-1087
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3
- natIpPortRanges:
  - <NAT Address IP1>:32800-32831
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:32800-32831
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.3

---
instanceName: consumer-instance-1
interfaceNatMappings:
- natIpPortRanges:
  - <NAT Address IP1>:1024-1055
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:1024-1055
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2
- natIpPortRanges:
  - <NAT Address IP1>:32768-32799
  numTotalDrainNatPorts: 0
  numTotalNatPorts: 32
  ruleMappings:
  - natIpPortRanges:
    - <NAT Address IP2>:32768-32799
    numTotalDrainNatPorts: 0
    numTotalNatPorts: 32
    ruleNumber: 100
  sourceAliasIpRange: ''
  sourceVirtualIp: 10.0.0.2

如上所示,consumer-instance-1 的默认 NAT IP(nat-address-1 的 IP)仍然只分配了 64 个端口,但 NAT 规则的 IP(nat-address-2 的 IP)分配了 1024 个端口。与此同时,consumer-instance-2 仍为所有 NAT IP 保留了默认的 64 个端口分配。

您可以尝试反向情况,作为练习。让 Cloud NAT 取消分配所有额外端口,然后针对 producerip1 运行 curl 循环,并观察 get-nat-mapping-info 输出中的效果

10. 清理步骤

为避免重复收费,您应删除与本 Codelab 关联的所有资源。

首先删除所有实例。

在 Cloud Shell 中:

gcloud compute instances delete consumer-instance-1 consumer-instance-2 \
 producer-instance-1 producer-instance-2 \
 --zone us-east4-a --quiet

预期输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project Id>/zones/us-east4-a/instances/consumer-instance-1].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project Id>/zones/us-east4-a/instances/consumer-instance-2].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project Id>/zones/us-east4-a/instances/producer-instance-1].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project Id>/zones/us-east4-a/instances/producer-instance-2].

接下来,删除 Cloud Router。在 Cloud Shell 中:

gcloud compute routers delete consumer-cr \
 --region us-east4 --quiet

您应该会看到以下输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/routers/consumer-cr].

释放所有外部 IP 地址。在 Cloud Shell 中:

gcloud compute addresses delete nat-address-1 \
 nat-address-2 producer-address-1 \
 producer-address-2 --region us-east4 --quiet

您应该会看到以下输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/nat-address-1].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/nat-address-2].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/nat-address-3].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/producer-address-1].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/addresses/producer-address-2].

删除 VPC 防火墙规则。在 Cloud Shell 中:

gcloud compute firewall-rules delete consumer-allow-iap \
 producer-allow-80 --quiet

您应该会看到以下输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/firewalls/consumer-allow-iap].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/firewalls/producer-allow-80].

删除子网。在 Cloud Shell 中:

gcloud compute networks subnets delete cons-net-e4 \
 prod-net-e4 --region=us-east4 --quiet

您应该会看到以下输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/subnetworks/cons-net-e4].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/regions/us-east4/subnetworks/prod-net-e4].

最后,我们来删除 VPC。在 Cloud Shell 中:

gcloud compute networks delete consumer-vpc \
 producer-vpc --quiet

您应该会看到以下输出:

Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/networks/consumer-vpc].
Deleted [https://www.googleapis.com/compute/v1/projects/<Project ID>/global/networks/producer-vpc].

11. 恭喜!

您已完成 Cloud NAT DPA 实验!

所学内容

  • 如何设置 Cloud NAT 网关以准备 DPA。
  • 如何在没有 DPA 的情况下检查端口分配。
  • 如何为 NAT 网关启用和配置 DPA。
  • 如何通过运行并行出站连接来观察 DPA 的效果。
  • 如何向启用了 DPA 的 NAT 网关添加 NAT 规则。
  • 如何通过运行与多个目的地的出站连接来查看 DPA 在规则下的行为。

后续步骤

©Google, Inc. 或其关联公司。保留所有权利。请勿分发。