使用 Cloud NAT 动态端口分配

1. 概览

动态端口分配 (DPA) 是 Cloud NAT 的一项新功能。启用 DPA 后,Cloud NAT 会根据实例的需要为实例动态增加/减少端口分配。DPA 配置了端口上下限和上限,因此,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 Access”功能的 Google Cloud 项目。
  • 了解 Cloud NAT 基础知识

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

为了与 GCP 交互,我们将在整个实验中同时使用 Google Cloud 控制台和 Cloud Shell。

Google Cloud Console

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

75eef5f6fd6d7e41

自定进度的环境设置

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

96a9c957bc475304

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 中编写所有相关的 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 和子网后,我们将添加一条简单的入站防火墙规则,以允许针对 TCP 源 IP 地址范围的 IAP。这样,我们就可以使用 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"`

然后尝试通过 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 的幂分配端口,实际上每一步分配都会翻倍,因此我们可以看到连接超时以 2 的 64 到 1024 的幂突出显示。

由于我们将 maxPortsPerVM 设置为 1024,因此连接数应该不得超过 1024 个。我们可以重新运行计数高于 1024 的 curl 循环(在重置过时端口后等待 2 分钟),对此进行测试。

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. 或其关联公司。保留所有权利。请勿散布。