1. 简介
迁移到 IPv6 的主要挑战之一是保持对仅限 IPv4 的端点和网络的连通性。解决此挑战的一项领先技术是结合使用 DNS64(在 RFC6147 中定义)将 A 记录转换为客户端的 AAAA 记录,然后结合使用 NAT64(在 RFC6146 中定义)将特殊格式的 IPv6 地址转换为 IPv4 地址,其中 IPv4 地址嵌入到特殊 IPv6 地址中。此 Codelab 将引导用户在 Google Cloud Platform (GCP) Virtual Private Cloud (VPC) 上配置这两项功能。如果将 GCP NAT64 和 DNS64 搭配使用,仅限 IPv6 的实例便可与互联网上仅限 IPv4 的服务器进行通信。
在本实验中,您将设置一个具有不同类型 IPv6 子网和实例的 VPC:仅限 IPv6 的 GUA(全球单播地址)、仅限 IPv6 的 ULA(唯一本地地址)和双栈 ULA。然后,您将配置并测试 Google Cloud 的托管 DNS64 和 NAT64 服务,以便从这些服务访问仅支持 IPv4 的网站。
2. 学习内容
- 如何创建仅限 IPv6 的子网和实例
- 如何为 VPC 启用 Google Cloud 的托管式 DNS64 服务。
- 如何创建配置为 NAT64 的 Google Cloud NAT 网关。
- 如何测试从仅 IPv6 的实例到仅 IPv4 的目的地的 DNS64 解析。
- 单栈实例和双栈实例的 DNS64 和 NAT64 行为有何不同。
- 如何为 NAT64 配置 NAT 网关。
- 如何测试从仅 IPv6 的实例到仅 IPv4 的目的地的 NAT64 连接。
3. 准备工作
更新项目以支持 Codelab
此 Codelab 使用了 $variables 来帮助在 Cloud Shell 中实现 gcloud 配置。
在 Cloud Shell 中,执行以下操作
gcloud config list project
gcloud config set project [YOUR-PROJECT-ID]
export projectname=$(gcloud config list --format="value(core.project)")
export zonename=[COMPUTE ZONE NAME]
export regionname=[REGION NAME]
实验室整体架构
为了演示 NAT64 和 DNS64 如何与不同的 IPv6 子网类型交互,您将创建一个包含 GUA 和 ULA 风格的 IPv6 子网的 VPC。您还将创建一个双栈子网(使用 ULA 寻址),以演示 DNS64 和 NAT64 如何不适用于双栈虚拟机。
然后,您将配置 DNS64 和 NAT64,并测试与互联网上 IPv6 和 IPv4 目的地的连接。
4. 准备步骤
首先,在 Google Cloud 项目中设置必要的服务账号、IAM、网络基础设施和实例。
创建服务账号和 IAM 绑定
我们首先创建一个新的服务账号,以允许实例使用 gcloud 通过 SSH 相互连接。我们需要此功能,因为我们无法使用 IAP 访问仅支持 GUA IPv6 的实例,而 Cloud Shell 尚不允许直接访问 IPv6。在 Cloud Shell 中运行以下命令。
gcloud iam service-accounts create ipv6-codelab \
--description="temporary service account for a codelab" \
--display-name="ipv6codelabSA" \
--project $projectname
gcloud projects add-iam-policy-binding $projectname \
--member=serviceAccount:ipv6-codelab@$projectname.iam.gserviceaccount.com \
--role=roles/compute.instanceAdmin.v1
gcloud iam service-accounts add-iam-policy-binding \
ipv6-codelab@$projectname.iam.gserviceaccount.com \
--member=serviceAccount:ipv6-codelab@$projectname.iam.gserviceaccount.com \
--role=roles/iam.serviceAccountUser
创建 VPC 并启用 ULA
在 Cloud Shell 中运行以下命令,创建具有自定义子网模式且启用了 ULA 内部 IPv6 的 VPC 网络。
gcloud compute networks create ipv6-only-vpc \
--project=$projectname \
--subnet-mode=custom \
--mtu=1500 --bgp-routing-mode=global \
--enable-ula-internal-ipv6
创建防火墙规则
创建防火墙规则以允许通过 SSH 进行访问。一条规则允许来自整个 ULA 范围 (fd20::/20) 的 SSH 连接。另外两条规则允许来自 IAP 预定义的 IPv6 和 IPv4 范围(分别为 2600:2d00:1:7::/64、35.235.240.0/20)的流量。
在 Cloud Shell 中运行以下命令:
gcloud compute firewall-rules create allow-v6-ssh-ula \
--direction=INGRESS --priority=200 \
--network=ipv6-only-vpc --action=ALLOW \
--rules=tcp:22 --source-ranges=fd20::/20 \
--project=$projectname
gcloud compute firewall-rules create allow-v6-iap \
--direction=INGRESS --priority=300 \
--network=ipv6-only-vpc --action=ALLOW \
--rules=tcp --source-ranges=2600:2d00:1:7::/64 \
--project=$projectname
gcloud compute firewall-rules create allow-v4-iap \
--direction=INGRESS --priority=300 \
--network=ipv6-only-vpc --action=ALLOW \
--rules=tcp --source-ranges=35.235.240.0/20 \
--project=$projectname
创建子网
创建仅限 GUA v6 的子网、仅限 ULA v6 的子网和双栈 ULA 子网。在 Cloud Shell 中运行以下命令:
gcloud compute networks subnets create gua-v6only-subnet \
--network=ipv6-only-vpc \
--project=$projectname \
--stack-type=IPV6_ONLY \
--ipv6-access-type=external \
--region=$regionname
gcloud compute networks subnets create ula-v6only-subnet \
--network=ipv6-only-vpc \
--project=$projectname \
--stack-type=IPV6_ONLY \
--ipv6-access-type=internal \
--enable-private-ip-google-access \
--region=$regionname
gcloud compute networks subnets create ula-dualstack-subnet \
--network=ipv6-only-vpc \
--project=$projectname \
--stack-type=IPV4_IPV6 \
--range=10.120.0.0/16 \
--ipv6-access-type=internal \
--region=$regionname
创建实例
在您刚刚创建的每个子网中创建实例。仅限 IPv6 的 ULA 实例通过 cloud-platform 指定,以便我们将其用作跳转框来访问仅限 GUA IPv6 的实例。在 Cloud Shell 中运行以下命令:
gcloud compute instances create gua-instance \
--subnet gua-v6only-subnet \
--stack-type IPV6_ONLY \
--zone $zonename \
--scopes=https://www.googleapis.com/auth/cloud-platform \
--service-account=ipv6-codelab@$projectname.iam.gserviceaccount.com \
--project=$projectname
gcloud compute instances create ula-instance \
--subnet ula-v6only-subnet \
--stack-type IPV6_ONLY \
--zone $zonename \
--scopes=https://www.googleapis.com/auth/cloud-platform \
--service-account=ipv6-codelab@$projectname.iam.gserviceaccount.com \
--project=$projectname
gcloud compute instances create dualstack-ula-instance \
--subnet ula-dualstack-subnet \
--stack-type IPV4_IPV6 \
--zone $zonename \
--project=$projectname
初始实例访问权限和设置
通过 SSH 连接到 ULA 实例,该实例默认使用 IAP。在 Cloud Shell 中使用以下命令通过 SSH 连接到 ULA 实例:
gcloud compute ssh ula-instance --project $projectname --zone $zonename
<username>@ula-instance:~$
我们还将使用 ULA 实例作为 GUA 实例的跳板(因为 IAP 不适用于 GUA 实例,并且 Cloudshell 虚拟机无法访问 IPv6 目的地)。
仍在 ULA 实例 shell 中。尝试使用以下 gcloud 命令通过 SSH 连接到 GUA 实例。
首次在实例内运行 SSH 命令时,系统会提示您设置 SSH 密钥对。继续按 Enter 键,直到密钥创建完成并执行 SSH 命令;这会创建一个没有密码的新密钥对。
ula-instance:~$ gcloud compute ssh gua-instance
WARNING: The private SSH key file for gcloud does not exist.
WARNING: The public SSH key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/galhabian/.ssh/google_compute_engine
Your public key has been saved in /home/galhabian/.ssh/google_compute_engine.pub
The key fingerprint is:
SHA256:5PYzydjcpWYiFtzetYCBI6vmy9dqyLsxgDORkB9ynqY galhabian@ula-instance
The key's randomart image is:
+---[RSA 3072]----+
|.. |
|+.o . |
|o= o . + . |
| o= * o o |
|+o. . S o . o |
|Eo . . . O + = . |
| .=. .+ @ * . |
| +ooo... * |
| **.. |
+----[SHA256]-----+
如果成功,SSH 命令将成功执行,并且您将成功通过 SSH 连接到 GUA 实例:
Updating instance ssh metadata...done.
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.3639038240056074485' (ED25519) to the list of known hosts.
Linux gua-instance 6.1.0-34-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.135-1 (2025-04-25) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
<username>@gua-instance:~$
5. 检查仅限 IPv6 的实例。
我们来通过 SSH 连接到这两个仅限 IPv6 的实例,并检查它们的路由表。
检查 GUA 实例
先通过“ula-instance”实例跳转,然后通过 SSH 连接到“gua-instance”。
gcloud compute ssh ula-instance --project $projectname --zone $zonename
<username>@ula-instance:~$ gcloud compute ssh gua-instance
我们使用以下命令查看实例的 IPv6 路由表
<username>@gua-instance:~$ ip -6 route
2600:1900:4041:461::/65 via fe80::56:11ff:fef9:88c1 dev ens4 proto ra metric 100 expires 81sec pref medium
fe80::/64 dev ens4 proto kernel metric 256 pref medium
default via fe80::56:11ff:fef9:88c1 dev ens4 proto ra metric 100 expires 81sec mtu 1500 pref medium
我们注意到路由表中有三个条目
- 实例所属的 GUA 子网的 /65 路由,其派生的下一个跃点使用默认网关的链路本地地址。请注意,/65 以上的范围是为 IPv6 直通式网络负载平衡器预留的
- 针对链路本地单播前缀 fe80::/64 的内置 /64 路由
- 指向子网默认网关的链路本地地址的默认路由。
我们来发出以下命令,查看 IPv4 路由表
<username>@gua-instance:~$ ip -4 route
default via 169.254.1.1 dev ens4 proto dhcp src 169.254.1.2 metric 100
169.254.1.1 dev ens4 proto dhcp scope link src 169.254.1.2 metric 100
169.254.169.254 via 169.254.1.1 dev ens4 proto dhcp src 169.254.1.2 metric 100
令人惊讶?事实上,我们确实在仅限 IPv6 的实例中维护一个 IPv4 路由表,严格来说是为了允许访问 Compute 元数据服务器 (169.254.169.154),因为该服务器仍然是仅限 IPv4 的端点。
由于实例在仅 IPv6 的情况下会采用 IP 169.254.1.2。此 IP 只能路由到计算元数据服务器,因此实例实际上与所有 IPv4 网络隔离。
Curl 测试
我们来使用 curl 测试与仅支持 IPv4 和仅支持 IPv6 的网站的实际连接。
<username>@gua-instance:~$ curl -vv --connect-timeout 10 v6.ipv6test.app
<username>@gua-instance:~$ curl -vv --connect-timeout 10 v4.ipv6test.app
以下是输出示例。
<username>@gua-instance:~$ curl -vv --connect-timeout 10 v6.ipv6test.app
* Trying [2600:9000:20be:cc00:9:ec55:a1c0:93a1]:80...
* Connected to v6.ipv6test.app (2600:9000:20be:cc00:9:ec55:a1c0:93a1) port 80 (#0)
> GET / HTTP/1.1
> Host: v6.ipv6test.app
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
!! Rest of output truncated
<username>@gua-instance:~$ curl -vv --connect-timeout 10 v4.ipv6test.app
* Trying 3.163.165.4:80...
* ipv4 connect timeout after 4985ms, move on!
* Trying 3.163.165.50:80...
* ipv4 connect timeout after 2492ms, move on!
* Trying 3.163.165.127:80...
* ipv4 connect timeout after 1246ms, move on!
* Trying 3.163.165.37:80...
* ipv4 connect timeout after 1245ms, move on!
* Failed to connect to v4.ipv6test.app port 80 after 10000 ms: Timeout was reached
* Closing connection 0
curl: (28) Failed to connect to v4.ipv6test.app port 80 after 10000 ms: Timeout was reached
正如预期的那样,仅限 IPv6 的实例无法访问 IPv4 互联网端点。如果不预配 DNS64 和 NAT64,仅限 IPv6 的实例将无法访问 IPv4 目的地。由于实例具有 GUA IPv6 地址,因此可以正常访问 IPv6 目的地。
检查 ULA 实例
通过 SSH 登录“ula-instance”实例(默认使用 IAP)。
gcloud compute ssh ula-instance --project $projectname --zone $zonename
我们使用以下命令查看实例的 IPv6 路由表
<username>@ula-instance:~$ ip -6 route
fd20:f06:2e5e:2000::/64 via fe80::55:82ff:fe6b:1d7 dev ens4 proto ra metric 100 expires 84sec pref medium
fe80::/64 dev ens4 proto kernel metric 256 pref medium
default via fe80::55:82ff:fe6b:1d7 dev ens4 proto ra metric 100 expires 84sec mtu 1500 pref medium
我们注意到,路由表中有三个条目,与 GUA 实例类似,只是掩码为 /64 而不是 /65。子网路由属于 ULA 范围。(在 fd20::/20 汇总下)
我们来发出以下命令,查看 IPv4 路由表
<username>@ula-instance:~$ ip -4 route
default via 169.254.1.1 dev ens4 proto dhcp src 169.254.1.2 metric 100
169.254.1.1 dev ens4 proto dhcp scope link src 169.254.1.2 metric 100
169.254.169.254 via 169.254.1.1 dev ens4 proto dhcp src 169.254.1.2 metric 100
这与 GUA 实例的情况类似。
Curl 测试
使用 curl 重复执行连接测试,以测试仅支持 IPv4 和仅支持 IPv6 的网站。
<username>@ula-instance:~$ curl -vv --connect-timeout 10 v6.ipv6test.app
<username>@ula-instance:~$ curl -vv --connect-timeout 10 v4.ipv6test.app
以下是输出示例。
<username>@ula-instance:~$ curl -vv --connect-timeout 10 v6.ipv6test.app
* Trying [2600:9000:20be:8400:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 4986ms, move on!
* Trying [2600:9000:20be:9000:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 2493ms, move on!
* Trying [2600:9000:20be:d600:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 1246ms, move on!
* Trying [2600:9000:20be:b000:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 622ms, move on!
* Trying [2600:9000:20be:7200:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 312ms, move on!
* Trying [2600:9000:20be:8600:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 155ms, move on!
* Trying [2600:9000:20be:7a00:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 77ms, move on!
* Trying [2600:9000:20be:ce00:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 77ms, move on!
* Failed to connect to v6.ipv6test.app port 80 after 10000 ms: Timeout was reached
* Closing connection 0
<username>@ula-instance:~$ curl -vv --connect-timeout 10 v4.ipv6test.app
* Trying 3.163.165.4:80...
* ipv4 connect timeout after 4985ms, move on!
* Trying 3.163.165.50:80...
* ipv4 connect timeout after 2492ms, move on!
* Trying 3.163.165.127:80...
* ipv4 connect timeout after 1246ms, move on!
* Trying 3.163.165.37:80...
* ipv4 connect timeout after 1245ms, move on!
* Failed to connect to v4.ipv6test.app port 80 after 10000 ms: Timeout was reached
* Closing connection 0
curl: (28) Failed to connect to v4.ipv6test.app port 80 after 10000 ms: Timeout was reached
在 ULA 实例的情况下,无法同时访问这两个互联网端点,因为对于 IPv6 端点,我们无法使用 ULA 地址进行外部通信,并且实例作为仅限 IPv6 的实例无法访问 IPv4。
6. 启用 NAT64 和 DNS64
为 VPC 配置托管式 DNS64 和 NAT64 服务。
DNS64
为您的 VPC 启用 DNS64 服务器政策。这会告知 VPC 的 DNS 解析器为仅包含 A 记录的响应合成 AAAA 记录。在 Cloud Shell 中运行以下命令:
gcloud beta dns policies create allow-dns64 \
--description="Enable DNS64 Policy" \
--networks=ipv6-only-vpc \
--enable-dns64-all-queries \
--project $projectname
NAT64
创建 Cloud Router,这是 Cloud NAT 所必需的。然后,创建一个配置为 NAT64 的 Cloud NAT 网关,为所有仅限 IPv6 的子网 IP 范围启用该网关,并自动分配外部 IP。在 Cloud Shell 中运行以下命令:
gcloud compute routers create nat64-router \
--network=ipv6-only-vpc \
--region=$regionname \
--project=$projectname
gcloud beta compute routers nats create nat64-natgw \
--router=nat64-router \
--region=$regionname \
--auto-allocate-nat-external-ips \
--nat64-all-v6-subnet-ip-ranges \
--project=$projectname
7. 测试 NAT64 和 DNS64
现在,我们来测试仅限 IPv6 的实例中的 NAT64 和 DNS64 配置,先测试 GUA 实例,再测试 ULA 实例。
从 GUA 实例测试 DNS64/NAT64
先通过“ula-instance”实例跳转,然后通过 SSH 连接到“gua-instance”。
gcloud compute ssh ula-instance --project $projectname --zone $zonename
<username>@ula-instance:~$ gcloud compute ssh gua-instance
DNS 测试
测试仅限 IPv6 的网站(例如v6.ipv6test.app,但任何仅限 IPv6 的网站都应产生类似的结果)。
<username>@gua-instance:~$ host -t AAAA v6.ipv6test.app
<username>@gua-instance:~$ host -t A v6.ipv6test.app
我们预计只会返回 IPv6 AAAA 答案。
输出示例
<username>@gua-instance:~$ host -t AAAA v6.ipv6test.app
v6.ipv6test.app has IPv6 address 2600:9000:269f:1000:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:6600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:b600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:3e00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:9c00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:b200:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:a600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:1400:9:ec55:a1c0:93a1
<username>@gua-instance:~$ host -t A v6.ipv6test.app
v6.ipv6test.app has no A record
测试仅 IPv4 网站(例如 v4.ipv6test.app)的 DNS 解析。您会看到一条 A 记录(原始 IPv4)和一条由 DNS64 使用众所周知的前缀 64:ff9b::/96 合成的 AAAA 记录。
<username>@gua-instance:~$ host -t AAAA v4.ipv6test.app
<username>@gua-instance:~$ host -t A v4.ipv6test.app
输出示例
<username>@gua-instance:~$ host -t AAAA v4.ipv6test.app
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3318
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3344
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:333c
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3326
<username>@gua-instance:~$ host -t A v4.ipv6test.app
v4.ipv6test.app has address 54.192.51.68
v4.ipv6test.app has address 54.192.51.24
v4.ipv6test.app has address 54.192.51.60
v4.ipv6test.app has address 54.192.51.38
在上面的示例中,十进制 IPv4 地址 (54.192.51.38) 将转换为十六进制 (36 c0 33 26),因此我们预计 AAAA 记录的答案为 (64:ff9b::36c0:3326),这与我们收到的其中一个 AAAA 答案相符。
Curl 测试
我们来测试一下使用 curl 通过 IPv6 连接到同一 v4-only 和 v6-only 端点的实际连接
<username>@gua-instance:~$ curl -vv -6 v6.ipv6test.app
<username>@gua-instance:~$ curl -vv -6 v4.ipv6test.app
以下是输出示例。
<username>@gua-instance:~$ curl -vv -6 v6.ipv6test.app
* Trying [2600:9000:269f:1000:9:ec55:a1c0:93a1]:80...
* Connected to v6.ipv6test.app (2600:9000:269f:1000:9:ec55:a1c0:93a1) port 80 (#0)
> GET / HTTP/1.1
##
## <Output truncated for brevity>
##
<username>@gua-instance:~$ curl -vv -6 v4.ipv6test.app
* Trying [64:ff9b::36c0:333c]:80...
* Connected to v4.ipv6test.app (64:ff9b::36c0:333c) port 80 (#0)
> GET / HTTP/1.1
##
## <Output truncated for brevity>
##
两个 curl 命令均成功执行。请注意,由于 NAT64 和 DNS64 协同工作,成功实现了连接,因此可以通过 IPv6 连接到仅限 IPv4 的网站。
检查来源 IP
我们来使用 IP 反射服务检查目的地观察到的来源 IP。
<username>@gua-instance:~$ curl -6 v4.ipv6test.app
<username>@gua-instance:~$ curl -6 v6.ipv6test.app
示例输出
<username>@gua-instance:~$ curl -6 v4.ipv6test.app
34.47.60.91
<username>@gua-instance:~$ curl -6 v6.ipv6test.app
2600:1900:40e0:6f:0:1::
报告的 IPv6 地址应与实例的 IPv6 地址一致。此地址应与实例上“ip -6 address”命令的输出相匹配。举例来说
<username>@gua-instance:~$ ip -6 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2600:1900:40e0:6f:0:1::/128 scope global dynamic noprefixroute
valid_lft 79912sec preferred_lft 79912sec
inet6 fe80::86:d9ff:fe34:27ed/64 scope link
valid_lft forever preferred_lft forever
不过,报告的 IPv4 地址应与 Cloud NAT 网关的外部 IP 地址一致,因为该网关会在出站到互联网之前执行 NAT64 功能。您可以在 Cloud Shell 中运行以下 gcloud 命令来验证这一点
gcloud compute routers get-nat-ip-info \
nat64-router \
--region=$regionname
示例输出
result:
- natIpInfoMappings:
- mode: AUTO
natIp: 34.47.60.91
usage: IN_USE
natName: nat64-natgw
请注意,输出中报告的“natIp”与从 IP 反射网站收到的输出一致。
从 ULA 实例测试 DNS64/NAT64
首先,通过 SSH 连接到 ULA 实例“ula-instance”
gcloud compute ssh ula-instance --project $projectname --zone $zonename
<username>@ula-instance:~$
DNS 测试
测试仅限 IPv6 的网站(例如v6.ipv6test.app,但任何仅限 IPv6 的网站都应产生类似的结果)。
<username>@ula-instance:~$ host -t AAAA v6.ipv6test.app
<username>@ula-instance:~$ host -t A v6.ipv6test.app
我们预计只会返回 IPv6 AAAA 答案。
输出示例
<username>@ula-instance:~$ host -t AAAA v6.ipv6test.app
v6.ipv6test.app has IPv6 address 2600:9000:269f:1000:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:6600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:b600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:3e00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:9c00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:b200:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:a600:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:1400:9:ec55:a1c0:93a1
<username>@ula-instance:~$ host -t A v6.ipv6test.app
v6.ipv6test.app has no A record
测试仅 IPv4 网站(例如 v4.ipv6test.app)的 DNS 解析。您会看到一条 A 记录(原始 IPv4)和一条由 DNS64 使用众所周知的前缀 64:ff9b::/96 合成的 AAAA 记录。
<username>@ula-instance:~$ host -t AAAA v4.ipv6test.app
<username>@ula-instance:~$ host -t A v4.ipv6test.app
输出示例
<username>@gua-instance:~$ host -t AAAA v4.ipv6test.app
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3318
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3344
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:333c
v4.ipv6test.app has IPv6 address 64:ff9b::36c0:3326
<username>@gua-instance:~$ host -t A v4.ipv6test.app
v4.ipv6test.app has address 54.192.51.68
v4.ipv6test.app has address 54.192.51.24
v4.ipv6test.app has address 54.192.51.60
v4.ipv6test.app has address 54.192.51.38
在上面的示例中,十进制 IPv4 地址 (54.192.51.38) 将转换为十六进制 (36 c0 33 26),因此我们预计 AAAA 记录的答案为 (64:ff9b::36c0:3326),这与我们收到的其中一个 AAAA 答案相符。
Curl 测试
我们来使用 curl 测试与同一纯 IPv4 和纯 IPv6 端点的实际连接。
首先,我们来证明无法通过 IPv4 实现可达性,因为该实例是仅 IPv6 的实例。
<username>@ula-instance:~$ curl -vv -4 --connect-timeout 10 v6.ipv6test.app
<username>@ula-instance:~$ curl -vv -4 --connect-timeout 10 v4.ipv6test.app
而这两个 curl 命令都会失败。它们会因各种原因而失败。以下是输出示例。
<username>@ula-instance:~$ curl -vv -4 v6.ipv6test.app
* Could not resolve host: v6.ipv6test.app
* Closing connection 0
curl: (6) Could not resolve host: v6.ipv6test.app
<username>@ula-instance:~$ curl -vv -4 --connect-timeout 10 v4.ipv6test.app
* Trying 54.192.51.68:80...
* ipv4 connect timeout after 4993ms, move on!
* Trying 54.192.51.38:80...
* ipv4 connect timeout after 2496ms, move on!
* Trying 54.192.51.24:80...
* ipv4 connect timeout after 1248ms, move on!
* Trying 54.192.51.60:80...
* Connection timeout after 10000 ms
* Closing connection 0
curl: (28) Connection timeout after 10000 ms
针对仅支持 IPv6 的端点的 IPv4 curl 失败,因为 A 记录的 DNS 解析失败(如 DNS 测试期间所示)。针对仅限 IPv4 的端点的 IPv4 curl 失败,因为仅限 IPv6 的实例无法访问任何 IPv4 地址,因此我们收到超时错误。
现在,我们来测试一下通过 IPv6 的可达性。
<username>@ula-instance:~$ curl -vv -6 v6.ipv6test.app
<username>@ula-instance:~$ curl -vv -6 v4.ipv6test.app
以下是输出示例。
<username>@ula-instance:~$ curl -vv -6 v6.ipv6test.app
* Trying [2600:9000:20be:c000:9:ec55:a1c0:93a1]:80...
* connect to 2600:9000:20be:c000:9:ec55:a1c0:93a1 port 80 failed: Connection timed out
* Trying [2600:9000:20be:f000:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 84507ms, move on!
* Trying [2600:9000:20be:ae00:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 42253ms, move on!
* Trying [2600:9000:20be:2000:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 21126ms, move on!
* Trying [2600:9000:20be:b600:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 10563ms, move on!
* Trying [2600:9000:20be:7600:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 5282ms, move on!
* Trying [2600:9000:20be:b000:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 2640ms, move on!
* Trying [2600:9000:20be:3400:9:ec55:a1c0:93a1]:80...
* ipv6 connect timeout after 2642ms, move on!
* Failed to connect to v6.ipv6test.app port 80 after 300361 ms: Timeout was reached
* Closing connection 0
<username>@ula-instance:~$ curl -vv -6 v4.ipv6test.app
* Trying [64:ff9b::36c0:333c]:80...
* Connected to v4.ipv6test.app (64:ff9b::36c0:333c) port 80 (#0)
> GET / HTTP/1.1
##
## <Output truncated for brevity>
##
虽然对仅限 IPv6 的网站执行 curl 操作会失败,因为 ULA 子网无法直接访问互联网。对仅支持 IPv4 的网站执行的 curl 命令成功,这是因为 DNS64 和 NAT64 对 GUA 和 ULA 实例的运行方式相同;唯一的要求是实例仅支持 IPv6。
从双堆栈 ULA 实例测试 DNS64/NAT64
首先,通过 SSH 连接到双栈 ULA 实例“dualstack-ula-instance”。我们需要使用“--tunnel-through-iap”标志来强制 gcloud 使用 IPv4 地址进行 IAP。
gcloud compute ssh dualstack-ula-instance --project $projectname --zone $zonename --tunnel-through-iap
<username>@dualstack-ula-instance:~$
现在,我们使用“host”实用程序来测试 DNS64。
<username>@dualstack-ula-instance:~$ host v4.ipv6test.app
v4.ipv6test.app has address 54.192.51.38
v4.ipv6test.app has address 54.192.51.24
v4.ipv6test.app has address 54.192.51.68
v4.ipv6test.app has address 54.192.51.60
<username>@dualstack-ula-instance:~$ host v6.ipv6test.app
v6.ipv6test.app has IPv6 address 2600:9000:269f:fc00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:1c00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:a200:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:8a00:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:c800:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:c200:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:5800:9:ec55:a1c0:93a1
v6.ipv6test.app has IPv6 address 2600:9000:269f:dc00:9:ec55:a1c0:93a1
请注意,仅支持 IPv4 的网站现在仅返回 IPv4 地址,不再返回合成的 DNS64 答案。这是因为 DNS64 仅适用于仅 IPv6 的实例,而不适用于双栈实例。
为了绕过对 DNS64 的需求,我们向 /etc/hosts 文件添加一个条目,以测试 NAT64 是否正常运行。在双堆栈实例中运行以下命令:
<username>@dualstack-ula-instance:~$ echo '64:ff9b::36c0:3326 v4.ipv6test.app' | sudo tee -a /etc/hosts
然后,我们使用 curl 测试通过 IPv6 访问 IPv4 网站
<username>@dualstack-ula-instance:~$ curl -vv -6 --connect-timeout 10 v4.ipv6test.app
以下是上述命令的输出示例
<username>@dualstack-ula-instance:~$ curl -vv -6 --connect-timeout 10 v4.ipv6test.app
* Trying [64:ff9b::36c0:3326]:80...
* ipv6 connect timeout after 10000ms, move on!
* Failed to connect to v4.ipv6test.app port 80 after 10001 ms: Timeout was reached
* Closing connection 0
curl: (28) Failed to connect to v4.ipv6test.app port 80 after 10001 ms: Timeout was reached
curl 应该会超时,因为与 DNS64 一样,NAT64 也要求实例仅限 IPv6 才能应用。
为了确认 NAT64 实际上并未应用于双堆栈实例,我们使用“get-nat-mapping”命令列出 NAT 网关正在应用的所有端口映射。在 Cloud Shell 中运行以下命令:
gcloud compute routers get-nat-mapping-info \
nat64-router --region $regionname \
--project $projectname
您应该会看到类似于以下代码段的输出:
---
instanceName: gua-instance
interfaceNatMappings:
- natIpPortRanges:
- 34.47.60.91:1024-1055
numTotalDrainNatPorts: 0
numTotalNatPorts: 32
sourceAliasIpRange: ''
sourceVirtualIp: '2600:1900:40e0:6f:0:1::'
- natIpPortRanges:
- 34.47.60.91:32768-32799
numTotalDrainNatPorts: 0
numTotalNatPorts: 32
sourceAliasIpRange: ''
sourceVirtualIp: '2600:1900:40e0:6f:0:1::'
---
instanceName: ula-instance
interfaceNatMappings:
- natIpPortRanges:
- 34.47.60.91:1056-1087
numTotalDrainNatPorts: 0
numTotalNatPorts: 32
sourceAliasIpRange: ''
sourceVirtualIp: fd20:9c2:93fc:2800:0:0:0:0
- natIpPortRanges:
- 34.47.60.91:32800-32831
numTotalDrainNatPorts: 0
numTotalNatPorts: 32
sourceAliasIpRange: ''
sourceVirtualIp: fd20:9c2:93fc:2800:0:0:0:0
NAT 输出显示,NAT64 网关仅为仅限 IPv6 的 GUA 和 ULA 实例分配了端口,但未为双栈实例分配端口。
8. 清理
清理 Cloud Router
在 Cloud Shell 中,执行以下操作:
gcloud compute routers delete nat64-router \
--region $regionname \
--project $projectname --quiet
解除绑定并清理 DNS 政策
在 Cloud Shell 中,执行以下操作:
gcloud beta dns policies update allow-dns64 \
--networks="" \
--project $projectname
gcloud beta dns policies delete allow-dns64 \
--project $projectname --quiet
清理实例
在 Cloud Shell 中,执行以下操作:(请注意,我们使用 gcloud beta 是为了能够使用 no-graceful-shutdown 标志来更快地删除实例)
gcloud beta compute instances delete gua-instance \
--zone $zonename \
--no-graceful-shutdown \
--project=$projectname --quiet
gcloud beta compute instances delete ula-instance \
--zone $zonename \
--no-graceful-shutdown \
--project=$projectname --quiet
gcloud beta compute instances delete dualstack-ula-instance \
--zone $zonename \
--no-graceful-shutdown \
--project=$projectname --quiet
清理子网
在 Cloud Shell 中,执行以下操作:
gcloud compute networks subnets delete gua-v6only-subnet \
--project=$projectname --quiet \
--region=$regionname
gcloud compute networks subnets delete ula-v6only-subnet \
--project=$projectname --quiet \
--region=$regionname
gcloud compute networks subnets delete ula-dualstack-subnet \
--project=$projectname --quiet \
--region=$regionname
清理防火墙规则
在 Cloud Shell 中,执行以下操作:
gcloud compute firewall-rules delete allow-v6-iap \
--project=$projectname \
--quiet
gcloud compute firewall-rules delete allow-v6-ssh-ula \
--project=$projectname \
--quiet
gcloud compute firewall-rules delete allow-v4-iap \
--project=$projectname \
--quiet
清理 VPC
在 Cloud Shell 中,执行以下操作:
gcloud compute networks delete ipv6-only-vpc \
--project=$projectname \
--quiet
清理 IAM 权限和服务账号
在 Cloud Shell 中,执行以下操作:
gcloud projects remove-iam-policy-binding $projectname \
--member=serviceAccount:ipv6-codelab@$projectname.iam.gserviceaccount.com \
--role=roles/compute.instanceAdmin.v1
gcloud iam service-accounts delete \
ipv6-codelab@$projectname.iam.gserviceaccount.com \
--quiet \
--project $projectname
9. 恭喜
您已成功使用 NAT64 和 DNS64 来允许仅限 IPv6 的实例访问互联网上仅限 IPv4 的目的地。
后续操作
查看下列 Codelab…