网络和虚拟化是现代计算基础设施的两根支柱,云计算就是它们的结合。
Intro
网络本质上是在「设备上的服务」之间传消息,设备可以是物理机、虚拟机、容器,甚至是嵌入式设备。局域网里靠像以太网这样的协议解决冲突、寻址和噪声问题,但互联网规模下不可能让所有设备监听所有消息,于是引入了 IP,通过路由器一跳一跳地转发,让消息逐步靠近目标,而发送方并不需要知道完整路径。
互联网协议分为四层:Datalink,Internet(IP 负责寻址和路由),Transport(连接设备上的服务,比如 TCP 和 UDP),Application(HTTP、DNS 等)。接下来将主要讨论 Internet 和 Transport 层。
关于分布式计算,有一些经常被忘记的常识:网络是不可靠的、延迟不是 0、带宽有限、传输有成本、且并不安全。
IP 地址
- IPv4 / IPv6: IPv4(32位)地址已经耗尽,IPv6(128位)提供近乎无限的地址并简化了报头,提高了路由器处理效率。
- 地址分配有两个关键维度,公网/私网,以及静态/动态。
- 公网 / 私网: 私网 IP(如 10.x.x.x)可以重复使用,通过 NAT 与外界通信,配合防火墙,既节省地址又比较安全;127.0.0.1(localhost)用于本机通信。
- 静态 / 动态: 服务器通常用静态 IP,普通设备通过 DHCP 协议动态获取临时 IP。
- 消息传递中,现实世界有很多麻烦,路由器会故障,消息会延迟、重复甚至丢失,最终消息是送到目标操作系统而不是直接到应用(这可能需要经过 Hypervisor)。这些问题决定了为什么上层协议必须设计确认、重试和容错机制。
- IP 协议报头:IP 消息由头和载荷组成,头里包含源地址、目的地址、TTL、协议类型等信息。
- IPv4 的头部复杂,支持分片、有校验和(checksum),但这也带来性能和扩展性的问题;
- IPv6 简化了头部,去掉校验和,因为它认为底层和上层已有校验,同时路由器不再分片,只负责转发,减轻了路由器的负担。
IP 是尽力而为的、不保证可靠的传输基础。
DNS
IP 不适合记忆,也不稳定,所以需要 DNS 把逻辑名称映射到 IP。
- Hostname 的域名解析是从右向左进行的层级结构。举例,对于 www.amazon.com :
- 首先从最右侧的 Root(根服务器) 开始;
- 接着定位到 TLD(顶级域名服务器,如 .com);
- 最后找到管理 amazon.com 的 权威 DNS 服务器 (Authoritative DNS Server),并获取最终 IP。
- 域名解析过程中每一层的 DNS 服务器都会缓存解析结果,但缓存必须过期,否则更新无法传播。DNS 为每个响应设置了 TTL 生存时间,即 IP 地址有效的时间。通常来说,距离目标服务器越近,TTL 值越小。
- DNS 不只是查地址,还可以返回多个 IP,用来做失败重试和简单的负载均衡,比如轮询 DNS(也就是对每个客户端,DNS 服务器返回的 IP 列表顺序不一样)。
端口
IP 只能把消息送到设备,端口才能把消息送到具体服务。 端口就像老式电话总机里的分机号。端口号有明确的分段和约定:
- 0-1023:网络标准协议
- 22 for SSH
- 53 for DNS
- 80 for HTTP, 443 for HTTPS
- 1024-49151:常见中间件和数据库的固定端口
- Zookeeper, PostgreSQL, Kafka
- 49152-65525:临时通信用动态端口
桥接(Bridge)与 NAT 网络
前面的讨论一直围绕着物理设备之间的物理网络连接展开,接下来讲一下虚拟化之后的网络。物理世界里有网线和交换机,但在 VM 和容器里,网络首先是软件造出来的。Hypervisor 会在一台物理机里创建一个虚拟网络,常见做法有两类:bridge 和 NAT。
- NAT 的本质是地址改写,对外看只有一个 IP,内部靠端口和映射区分;
- Host 创建一个内部私有子网,对外共享一个 IP,NAT 负责将进出信息的源地址和目标地址进行转换。对外部而言,整个私有子网只有一个 host 的 IP。
- Bridge 的本质是把虚拟机当成真实接在外部网络上的设备,每个 VM 都有自己完整可见的 IP。
- Type 1 VM 的 bare-metal hypervisor 通常使用 bridge,而 Type 2 VM 上的 hosted hypervisor 多用 NAT,因为后者还需要管理 VM 以外的其他服务。
- Type 2 VM 用于在一个 host 上运行不同的 OS,经常会连接到企业网络,包括网络准入控制等安全措施(比如 DHCP 服务器会忽略未知计算机的分配 IP 请求,或者必须通过 VPN 连接),所以 bridge 行不通。
Docker 虽然叫 bridge networking,但它的行为实际上更像 Hypervisor 的 NAT 模式:容器之间在 host 内部有一个私有子网,对外则共享 host 的 IP,只暴露映射出来的端口(每个容器将其端口映射到 host 的端口,类似于防火墙)。这引出一个约束:因为容器共享 host 的 OS 和 IP,就会产生端口冲突问题,Type 1 VM 可以让多个服务都跑在 80 端口,但容器不行。解决方案通常是改端口、反向代理,或者靠编排系统做更复杂的流量分发。
用容器交付服务,不仅要设计服务本身,还要设计服务如何被分组、如何共享 bridge 网络、如何避免端口冲突。
TCP
就像 IP 地址需要 IPv4 等协议来定义如何解读消息,端口也有几种协议可以用于解读发送到某个服务的消息,其中最常见的就是 TCP。
- TCP 通过序列号(Sequence Number)确保顺序,通过确认号(ACK) 和重传机制确保不丢包,通过校验和(Checksum) 检测错误。
- TCP 包里既有源端口也有目的端口,而且和 IP 一样,端口在传输过程中是可能被改写的。
虽然 TCP 是可靠的,但它的 timeout 检测在分布式系统中通常很慢(可能需要几十秒甚至几分钟)。在微服务架构中,开发者往往需要自己实现应用层的超时、重试、幂等、重定向,而不是完全信任 TCP。
构建网络
网络结构不是自然形成的,而是人为设计的,下面说明如何通过逻辑划分来管理复杂的网络。
子网 (Subnets)
子网就是直接互相可见的一组设备,不经过路由器,任何消息都可以被所有设备看到。创建子网的原因有三:
- 本地的网络规模和流量都较大时,用于提高网络性能
- 安全性,子网防火墙可以验证是否能访问子网上的资源
- 子网也是 IP 地址空间的组织方式,通过前缀让路由器快速判断应该将消息发到哪里,降低消息传播的成本
Partitioning
Partitioning 的其他原因包括保护机制和逻辑简化,易于管理;此类结构化的机制依赖位于消息进出网络路径上的专用设备。这些 gatekeeper 名字各异:防火墙,NAT,代理服务器(proxy server),网关,但它们都承担着监控和控制本地网络进出流量的功能。
- 防火墙基于规则过滤流量(黑名单/白名单,也会检查 payload 的内容)。将对外服务的 Web 服务器放在 DMZ 区域,内网数据库放在 Intranet 区域,即使 Web 服务器被黑,攻击者也无法轻易跨过第二层防火墙进入核心内网。
- 防火墙的另一个价值是失败隔离,把不稳定、实验性的、测试环境用防火墙隔开,为了防止内部事故变成全网事故。
- 防火墙主要在 Internet 层运行,对 Transport 层也有一定影响;而 NAT 主要在 Transport 层运行。
- NAT 可以隐藏网络组织的内部结构、简化配置,但直接破坏了端到端的可达性(无法从外部网络直接访问),所以才会有 NAT traversal、TURN/STUN 这类复杂协议。
- 运行在 Application 层的 gatekeeper 叫系统代理服务器(system proxy servers),其中 HTTP 代理是最常见的。代理和防火墙类似,都可以配置黑名单/白名单,根据目标地址过滤请求,还可以修改 HTTP 协议头中的参数。
- 代理服务器的配置和防火墙是分开的,因此可能会出现配置冲突;Tunneling 技术就可以将消息穿透过防火墙。
Tunneling
VPN 可以让用户从公网访问私网,其工作原理就是 tunneling 协议。核心思想是:把原本要发的整个 IP 包加密后,塞进另一个连接的 payload 里,通过防火墙允许的通道送进去。
Tunneling 服务器通常和 Web 服务器一样在 DMZ 中,通过身份验证后,就允许远程用户像在办公室一样访问内网资源。
注意:身份认证一定发生在 tunnel 建立之前;tunnel 一旦建立,几乎所有流量都能被合法地送入,所以安全是一切的前提。