VPC内网通信

前面有篇文章记录了我在迁移 redis 过程。但是在总结的时候对解决问题中的阿里云内网地址充满了兴趣,阿里云到底是怎么实现内网互联?容器中为什么也可以访问阿里云内网?

阿里云内网互联

要说清阿里云的整个 vpc 体系我现在怕是做不到,但是我本着根据实际现象做联想,我应该可以,至少可以在较长时间内可以满足我的知识图谱,如果哪天要是有机会碰上王坚博士,不对叫王坚院士,再和他一探究竟。

很多科学也是,在很长一段时间内都不会被证伪,说明这个科学理论是正确的,如果很长时间之后被证伪,说明证伪的部分也是这个科学理论的一部分,将知识串联起来比知识的真像更重要,发现了前面不对,也不必完全推翻,将 bug 修复了,将新的知识替换上去,将新老知识串联起来。

概念

概念这块我查阅了阿里云官方文档,以及查看了很多相关博客。

VPC

专有网络(Virtual Private Cloud,简称VPC)是用户独有的云上私有网络,用户可以将购买的服务器、数据库、负载均衡都添加到这个专有网络,以达到内部互联互通。

专有网络

阿里云建议一个地域下面只需要一个 VPC,不同地域之间的 VPC 是可以通过 vpn 或者隧道技术联通。

vpc 原理

基于目前主流的隧道技术,专有网络隔离了虚拟网络。每个VPC都有一个独立的隧道号,一个隧道号对应着一个虚拟化网络。一个VPC内的ECS(Elastic Compute Service)实例之间的传输数据包都会加上隧道封装,带有唯一的隧道ID标识,然后送到物理网络上进行传输。不同VPC内的ECS实例因为所在的隧道ID不同,本身处于两个不同的路由平面,所以不同VPC内的ECS实例无法进行通信,天然地进行了隔离。

基于隧道技术和软件定义网络(Software Defined Network,简称SDN)技术,阿里云的研发在硬件网关和自研交换机设备的基础上实现了VPC产品。

vpc 逻辑架构

如下图所示,VPC包含交换机、网关和控制器三个重要的组件。交换机和网关组成了数据通路的关键路径,控制器使用自研的协议下发转发表到网关和交换机,完成了配置通路的关键路径。整体架构里面,配置通路和数据通路互相分离。交换机是分布式的结点,网关和控制器都是集群部署并且是多机房互备的,并且所有链路上都有冗余容灾,提升了VPC产品的整体可用性。

路由器

路由器(VRouter)是专有网络的枢纽。路由器可以连接专有网络的各个交换机,同时也是连接专有网络与其它网络的网关设备。路由器根据路由条目来转发网络流量。这里路由器是一个抽象概念。

路由表

路由表(Route Table)是指路由器上管理路由条目的列表,路由表绑定了VPC和交换机 。可以想象成 excel 里面的一张张 sheet

路由条目

路由条目(Route Entry)是指路由表中的每一条路由信息。路由条目定义了通向指定目标网段的网络流量的下一跳地址。路由条目包括系统路由和自定义路由两种类型。可以想象成 excel 里面的一张 sheet 中的每一行。

交换机

交换机(VSwitch)是组成专有网络的基础网络设备。交换机可以连接不同的云资源。在专有网络内创建云资源时,必须指定云资源所连接的交换机。默认一个可用区会有一台交换机,每台交换机都是 vpc 所定义的一个子网。

网关

网关其实也是一个抽象概念,可以想象中现实中的海关,如果一个用户不出国,就接触不到海关,但是一个用户想要出国,就必须从海关通过,网关管理着内外网的联系。其实我一直不太理解路由器和网关的关系,觉得他们有关系但是不知道到底是怎样的关系,直到我看到知乎上的一段回复:

首先‘网关’一个大概念,不具体特指一类产品,只要连接两个不同的网络的设备都可以叫网关;而‘路由器’一般特指能够实现路由寻找和转发的特定类产品,路由器很显然能够实现网关的功能。当然电信行业说的‘路由器’又和家用的‘路由器’两个概念,这个暂且不表。回到题目中你说问的默认网关是什么,默认网关事实上不是一个产品而是一个网络层的概念,PC本身不具备路由寻址能力,所以PC要把所有的IP包发送到一个默认的中转地址上面进行转发,也就是默认网关。这个网关可以在路由器上,可以在三层交换机上,可以在防火墙上,可以在服务器上,所以和物理的设备无关。

简单一句话总结就是:

网关是连接了两个网络的设备,路由器是连接了局域网和广域网,路由器算网关

NET 网关

这里我特地想介绍一下阿里云 net 网关,因为这个设备我一直和网关混淆,阿里云整套系统都是基于软件定义,这里的 net网关其实就是将内(网)外的端口或者地址转发的到外(内)网一个设备。

net 网关提供 SNAT 和 DNAT 功能,

  • DNAT是NAT网关上的一张配置表,用于NAT网关上的DNAT功能配置。可以实现端口映射(Port mapping)和IP映射(IP mapping),将NAT网关上的公网IP映射给ECS实例。
  • SNAT是NAT网关上的一张配置表,用于NAT网关上的SNAT功能配置。可以以交换机和ECS为粒度进行SNAT规则配置,以交换机为粒度:指定交换机下的所有ECS实例均使用指定的公网IP访问互联网,以ECS为粒度:指定的ECS实例使用指定的公网IP访问互联网。

可以看到 net 网关也是网关的一种而已。

弹性IP

弹性公网IP(Elastic IP Address,简称EIP)是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例、专有网络类型的辅助弹性网卡、NAT网关和高可用虚拟IP上。

EIP是一种NAT IP。它实际位于阿里云的公网网关上,通过NAT方式映射到了被绑定的资源上。和云资源绑定后,云资源可以通过EIP与公网通信.。

可以看出 EIP 也完全是一种抽象的概念,世界上本没有弹性 IP,阿里云这样的公司真是厉害,通过将阿里云公网上的 ip 以 net网关的形式映射到用户的绑定的资源上。

补充网络协议

upload successful

容器内访问阿里云内网

这部分内容有个前提,就是在阿里云的 ECS 里面已经可以和 rds 之类的服务连通。

实验

一般我们获得阿里云内网地址的时候,都会得到 rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com 类似的链接。

这里使用 dig 命令获取对应内网地址所对应的 ip。

缺少 dig 命令的?ubuntu 系统

1
$ apt install dnsutils

宿主机测试访问内网地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ dig rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48397
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com. IN A

;; ANSWER SECTION:
rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com. 60 IN A 192.168.0.250

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Thu Dec 19 16:24:40 CST 2019
;; MSG SIZE rcvd: 93

可以看到这里的 dns 是 127.0.0.53 ,这个地址 ubuntu 默认的 dns 的 ip,
rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com 对应的 ip 是
192.168.0.250

容器内测试访问内网地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ dig rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com

; <<>> DiG 9.11.5-P4-5.1-Debian <<>> rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57655
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 6cc1d7afb7bf77ce8f82d65a5dfb3603e95f17f7eeb8a82a (good)
;; QUESTION SECTION:
;rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com. IN A

;; ANSWER SECTION:
rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com. 60 IN A 192.168.0.250

;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Thu Dec 19 08:34:11 UTC 2019
;; MSG SIZE rcvd: 121

这里的 dns 是 127.0.0.11,这个是 docker 默认的 dns 所对应的 ip,但是这里的得到的 ip 还是 192.168.0.250

小结

其实这里的测试结果还很令我惊奇的,为什么在容器里面也可以得到和宿主机同样的 ip 呢?以前我也一直这样使用,直到上次迁移 redis 我才想起问为什么。

容器中的DNS名称解析优先级顺序为:

  • 内置DNS服务器127.0.0.11。
  • 通过–dns等参数为容器配置的DNS服务器。
  • docker守护进程的–dns服务配置(默认为8.8.8.8和8.8.4.4)
  • 宿主机上的DNS设置。

由于默认情况是没有第二步和第三步,所以在向 docker 内部 dns 服务器请求 rm-uf688k07qu8lt251p90130.mysql.rds.aliyuncs.com 的时候,内部 dns 是无法给到地址的,所以这个地址又交回到宿主机了。宿主机也不知道地址,随即通过交换机递交到路由器,路由器当然是知道这个地址是多少,就将 192.168.0.250 返回了。我们在购买 rds 服务的时候需要选择一个可用区,而这个 rds 就是挂在这个可用区所对应的交换机上。

继续探讨

这里容器内已经获得 rds 所对应的 ip 了,但是在容器内如何能访问到 192.168.0.250 这个地址呢?
我们可以先获取容器的 ip 地址

1
2
3
4
5
$ docker inspect $CONTAINER

...
"Gateway": "192.168.64.1",
"IPAddress": "192.168.64.5",

现在问题变成 192.168.64.5 想去连接 192.168.0.250
192.168.64.5 发现自己和 192.168.0.250 不在一个子网内.

1
2
3
4
5
6
7
8
9
10
$ route -n

Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.0.253 0.0.0.0 UG 0 0 0 eth0
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.16.0 0.0.0.0 255.255.240.0 U 0 0 0 br-317146d44671
192.168.32.0 0.0.0.0 255.255.240.0 U 0 0 0 br-dcf4529fa753
192.168.48.0 0.0.0.0 255.255.240.0 U 0 0 0 br-a5d4178cf1f7
192.168.64.0 0.0.0.0 255.255.240.0 U 0 0 0 br-568ac4a0657a
192.168.200.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0

把请求交给了 192.168.0.0 处理,这样 192.168.64.5 终于连上了 192.168.0.250

参考文档

坚持原创技术分享,您的支持将鼓励我继续创作!