摘要:本文讲述网络协议在当下热门领域的应用,比如云计算、容器和微服务,并手把手带你使用新技术,进一步加深对网络协议概念的理解。

本文是趣谈网络协议第二部分,第一部分在这里

想成为技术牛人,先搞定网络协议。

数据中心:我是开发商,自己拿地盖别墅

数据中心用到了前面学过的所有知识,数据中心里面是一堆服务器。服务器被放在一个个叫作机架(Rack)的架子上面,出入口也是漏油器,在数据中心的边界,所以叫做边界路由器,为了高可用,边界路由器有多个,而且会连接多个运营商网络,防止一个运营商网络出问题。

交换机往往是放在机架顶端的,所以经常称为TOR(Top Of Rack)交换机。这一层的交换机常常称为接入层(Access Layer)

当一个机架放不下的时候,就需要多个机架,还需要有交换机将多个机架连接在一起。这些交换机对性能的要求更高,带宽也更大。这些交换机称为汇聚层交换机(AggregationLayer)

数据中心里面的每一个连接都是需要考虑高可用的。这里首先要考虑的是,如果一台机器只有一个网卡,上面连着一个网线,接入到 TOR 交换机上。如果网卡坏了,或者不小心网线掉了,机器就上不去了。所以,需要至少两个网卡、两个网线插到 TOR 交换机上,但是两个网卡要工作得像一张网卡一样,这就是常说的网卡绑定(bond)

这就需要服务器和交换机都支持一种协议LACP(Link Aggregation Control Protocol)。它们互相通信,将多个网卡聚合称为一个网卡,多个网线聚合成一个网线,在网线之间可以进行负载均衡,也可以为了高可用作准备。

TOR 交换机也需要高可用,同理接入层和汇聚层的连接也需要高可用性,也不能单线连着。

最传统的方法是,部署两个接入交换机、两个汇聚交换机。服务器和两个接入交换机都连接,接入交换机和两个汇聚都连接,当然这样会形成环,所以需要启用 STP 协议,去除环,但是这样两个汇聚就只能一主一备了。STP 协议里我们学过,只有一条路会起作用。

交换机有一种技术叫作堆叠,所以另一种方法是,将多个交换机形成一个逻辑的交换机,服务器通过多根线分配连到多个接入层交换机上,而接入层交换机多根线分别连接到多个交换机上,并且通过堆叠的私有协议,形成双活的连接方式。

汇聚层将大量的计算节点相互连接在一起,形成一个集群。在这个集群里面,服务器之间通过二层互通,这个区域常称为一个POD(Point Of Delivery),有时候也称为一个可用区(Available Zone)

当节点数目再多的时候,一个可用区放不下,需要将多个可用区连在一起,连接多个可用区的交换机称为核心交换机

核心交换机吞吐量更大,高可用要求更高,肯定需要堆叠,但是往往仅仅堆叠,不足以满足吞吐量,因而还是需要部署多组核心交换机。核心和汇聚交换机之间为了高可用,也是全互连模式的。

VPN:朝中有人好做官

数据中心,里面很复杂,但是有的公司有多个数据中心,需要将多个数据中心连接起来,或者需要办公室和数据中心连接起来。怎么办?
走公网不安全,租用专线成本高,使用 VPN,安全有不贵。

VPN,全名 Virtual Private Network,虚拟专用网,就是利用开放的公众网络,建立专用数据传输通道,将远程的分支机构、移动办公人员等连接起来。

VPN 是如何工作的

VPN 通过隧道技术在公众网络上仿真一条点到点的专线,是通过利用一种协议来传输另外一种协议的技术,这里面涉及三种协议:乘客协议、隧道协议承载协议

以 IPsec 协议为例:

IPsec VPN。这是基于 IP 协议的安全隧道协议,为了保证在公网上面信息的安全,因而采取了一定的机制保证安全性。

  • 机制一:私密性,防止信息泄漏给未经授权的个人,通过加密把数据从明文变成无法读懂的密文,从而确保数据的私密性。前面讲 HTTPS 的时候,说过加密可以分为对称加密和非对称加密。对称加密速度快一些。
    而 VPN 一旦建立,需要传输大量数据,因而我们采取对称加密。但是同样,对称加密还是存在加密秘钥如何传输的问题,这里需要用到因特网密钥交换(IKE,Internet Key Exchange)协议。
  • 机制二:完整性,数据没有被非法篡改,通过对数据进行 hash 运算,产生类似于指纹的数据摘要,以保证数据的完整性。
  • 机制三:真实性,数据确实是由特定的对端发出,通过身份认证可以保证数据的真实性。

如何保证对方就是真正的那个人呢?

  • 第一种方法就是预共享密钥,也就是双方事先商量好一个暗号,比如“天王盖地虎,宝塔镇河妖”,对上了,就说明是对的。
  • 另外一种方法就是用数字签名来验证

基于以上三个特性,组成了IPsec VPN 的协议簇

  • AH(Authentication Header),只能进行数据摘要,不能实现数据加密。
  • ESP(Encapsulating Security Payload),能够进行数据加密和数据摘要。
  • IKE 组件,用于 VPN 的双方要进行对称密钥的交换。
  • SA(Security Association)组件,用于 VPN 的双方对连接进行维护

IPsec VPN 的建立过程

分两个阶段。

第一个阶段,建立 IKE 自己的 SA。这个 SA 用来维护一个通过身份认证和安全保护的通道,为第二个阶段提供服务。通过 DH(Diffie-Hellman)算法计算出一个对称密钥 K。

DH 算法是一个比较巧妙的算法。客户端和服务端约定两个公开的质数 p 和 q,然后客户端随机产生一个数 a 作为自己的私钥,服务端随机产生一个 b 作为自己的私钥,客户端可以根据 p、q 和 a 计算出公钥 A,服务端根据 p、q 和 b 计算出公钥 B,然后双方交换公钥 A 和 B。

到此客户端和服务端可以根据已有的信息,各自独立算出相同的结果 K,就是对称密钥。但是这个过程,对称密钥从来没有在通道上传输过,只传输了生成密钥的材料,通过这些材料,截获的人是无法算出的。

第二个阶段,建立 IPsec SA。在这个 SA 里面,双方会生成一个随机的对称密钥 M,由 K 加密传给对方,然后使用 M 进行双方接下来通信的数据。对称密钥 M 是有过期时间的,会过一段时间,重新生成一次,从而防止被破解。

IPsec SA 里面有以下内容:

  • SPI(Security Parameter Index),用于标识不同的连接
  • 双方商量好的加密算法、哈希算法和封装模式
  • 生存周期,超过这个周期,就需要重新生成一个 IPsec SA,重新生成对称密钥

当 IPsec 建立好,接下来就可以开始打包封装传输了。

左面是原始的 IP 包,在 IP 头里面,会指定上一层的协议为 TCP。ESP 要对 IP 包进行封装,因而 IP 头里面的上一层协议为 ESP。在 ESP 的正文里面,ESP 的头部有双方商讨好的 SPI,以及这次传输的序列号。

接下来全部是加密的内容。可以通过对称密钥进行解密,解密后在正文的最后,指明了里面的协议是什么。如果是 IP,则需要先解析 IP 头,然后解析 TCP 头,这是从隧道出来后解封装的过程。

有了 IPsec VPN 之后,客户端发送的明文的 IP 包,都会被加上 ESP 头和 IP 头,在公网上传输,由于加密,可以保证不被窃取,到了对端后,去掉 ESP 的头,进行解密。

这种点对点的基于 IP 的 VPN,能满足互通的要求,但是速度往往比较慢,这是由底层 IP 协议的特性决定的。IP 不是面向连接的,是尽力而为的协议,每个 IP 包自由选择路径,到每一个路由器,都自己去找下一跳,丢了就丢了,是靠上一层 TCP 的重发来保证可靠性。

因为 IP 网络从设计的时候,就认为是不可靠的,所以即使同一个连接,也可能选择不同的道路,这样的好处是,一条道路崩溃的时候,总有其他的路可以走。当然,带来的代价就是,不断的路由查找,效率比较差。

和 IP 对应的另一种技术称为 ATM。这种协议和 IP 协议的不同在于,它是面向连接的。你可以说 TCP 也是面向连接的啊。这两个不同,ATM 和 IP 是一个层次的,和 TCP 不是一个层次的。

另外,TCP 所谓的面向连接,是不停地重试来保证成功,其实下层的 IP 还是不面向连接的,丢了就丢了。ATM 是传输之前先建立一个连接,形成一个虚拟的通路,一旦连接建立了,所有的包都按照相同的路径走,不会分头行事。

好处是不需要每次都查路由表的,虚拟路径已经建立,每个包都按相同的路径走,这样效率会高很多。但是一旦虚拟路径上的某个路由器坏了,则这个连接就断了,什么也发不过去了,因为其他的包还会按照原来的路径走,都掉坑里了,它们不会选择其他的路径走。

多协议标签交换(MPLS,Multi-Protocol Label Switching),将两者的优点结合起来。

移动网络:去巴塞罗那,手机也上不了脸书

移动网络的发展历程

你一定知道手机上网有 2G、3G、4G 的说法,究竟这都是什么意思呢?有一个通俗的说法就是:用 2G 看 txt,用 3G 看 jpg,用 4G 看 avi。

2G 网络

在 2G 时代,上网使用的不是 IP 网络,而是电话网络,走模拟信号,专业名称为公共交换电话网(PSTN,Public Switched TelephoneNetwork)。

那手机不连网线,也不连电话线,它是怎么上网?

手机是通过收发无线信号来通信的,专业名称是 Mobile Station,简称 MS,需要嵌入 SIM。手机是客户端,而无线信号的服务端,就是基站子系统(BSS,Base Station SubsystemBSS)。

无论无线通信如何无线,最终还是要连接到有线的网络里

基站子系统分两部分,一部分对外提供无线通信,叫作基站收发信台(BTS,Base Transceiver Station),另一部分对内连接有线网络,叫作基站控制器(BSC,Base StationController)。
基站收发信台通过无线收到数据后,转发给基站控制器。

这部分属于无线的部分,统称为无线接入网(RAN,Radio Access Network)。

基站控制器通过有线网络,连接到提供手机业务的运营商的数据中心,这部分称为核心网(CN,Core Network)。核心网还没有真的进入互联网,这部分还是主要提供手机业务,是手机业务的有线部分。

首先接待基站来的数据的是移动业务交换中心(MSC,Mobile Service Switching Center),它是进入核心网的入口,但是它不会让你直接连接到互联网上。

因为在让你的手机真正进入互联网之前,提供手机业务的运营商,需要认证是不是合法的手机接入。别你自己造了一张手机卡,就连接上来。鉴权中心(AUC,Authentication Center)和设备识别寄存器(EIR,Equipment Identity Register)主要是负责安全性的。

另外,需要看你是本地的号,还是外地的号,这个牵扯到计费的问题,异地收费还是很贵的。访问位置寄存器(VLR,Visit Location Register)是看你目前在的地方,归属位置寄存器 (HLR,Home Location Register)是看你的号码归属地。

当你的手机卡既合法又有钱的时候,才允许你上网,这个时候需要一个网关,连接核心网和真正的互联网。网关移动交换中心(GMSC ,Gateway Mobile Switching Center)就是干这个的,然后是真正的互连网。
在 2G 时代,还是电话网络 PSTN。

数据中心里面的这些模块统称为网络子系统(NSS,Network and Switching Subsystem)。

  • 手机通过无线信号连接基站
  • 基站一面朝前接无线,一面朝后接核心网
  • 核心网一面朝前接到基站请求,一是判断你是否合法,二是判断你是不是本地号,还有没有钱,一面通过网关连接电话网络

2.5G 网络

在原来电路交换的基础上,加入了分组交换业务,支持 Packet 的转发,从而支持 IP 网络。多了一个分组控制单元(PCU,Packet Control Unit),用以提供分组交换通道。

在核心网里面,有个朝前的接待员(SGSN,Service GPRS Supported Node)和朝后连接 IP 网络的网关型 GPRS 支持节点(GGSN,Gateway GPRS Supported Node)。

3G 网络

线通信技术有了改进,大大增加了无线的带宽。

以 W-CDMA 为例,理论最高 2M 的下行速度,因而基站改变了,一面朝外的是 Node B,一面朝内连接核心网的是无线网络控制器(RNC,Radio Network Controller)。核心网以及连接的 IP 网络没有什么变化。

4G 网络

基站为 eNodeB,包含了原来 Node B 和 RNC 的功能,下行速度向百兆级别迈进。另外,核心网实现了控制面和数据面的分离,这个怎么理解呢?

在前面的核心网里面,有接待员 MSC 或者 SGSN,你会发现检查是否合法是它负责,转发数据也是它负责,也即控制面和数据面是合二为一的,这样灵活性比较差,因为控制面主要是指令,多是小包,往往需要高的及时性;数据面主要是流量,多是大包,往往需要吞吐量。

HSS 用于存储用户签约信息的数据库,其实就是你这个号码归属地是哪里的,以及一些认证信息。

MME 是核心控制网元,是控制面的核心,当手机通过 eNodeB 连上的时候,MME 会根据 HSS 的信息,判断你是否合法。如果允许连上来,MME 不负责具体的数据的流量,而是 MME 会选择数据面的 SGW 和 PGW,然后告诉 eNodeB,我允许你连上来了,你连接它们吧。

于是手机直接通过 eNodeB 连接 SGW,连上核心网,SGW 相当于数据面的接待员,并通过 PGW 连到 IP 网络。PGW 就是出口网关。在出口网关,有一个组件 PCRF,称为策略和计费控制单元,用来控制上网策略和流量的计费。

手机上网流程

手机开机之后上网的流程,这个过程称为Attach。可以看出来,移动网络还是很复杂的。因为这个过程要建立很多的隧道,分配很多的隧道 ID:

  1. 手机开机以后,在附近寻找基站 eNodeB,找到后给 eNodeB 发送 Attach Request,说“我来啦,我要上网”。
  2. eNodeB 将请求发给 MME,说“有个手机要上网”。
  3. MME 去请求手机,一是认证,二是鉴权,还会请求 HSS 看看有没有钱,看看是在哪里上网。
  4. 当 MME 通过了手机的认证之后,开始分配隧道,先告诉 SGW,说要创建一个会话(Create Session)。在这里面,会给 SGW 分配一个隧道 ID t1,并且请求 SGW 给自己也分配一个隧道 ID。
  5. SGW 转头向 PGW 请求建立一个会话,为 PGW 的控制面分配一个隧道 ID t2,也给 PGW 的数据面分配一个隧道 ID t3,并且请求 PGW 给自己的控制面和数据面分配隧道 ID。
  6. PGW 回复 SGW 说“创建会话成功”,使用自己的控制面隧道 ID t2,回复里面携带着给 SGW 控制面分配的隧道 ID t4 和控制面的隧道 ID t5,至此 SGW 和 PGW 直接的隧道建设完成。
    双方请求对方,都要带着对方给自己分配的隧道 ID,从而标志是这个手机的请求。
  7. 接下来 SGW 回复 MME 说“创建会话成功”,使用自己的隧道 ID t1 访问 MME,回复里面有给 MME 分配隧道 ID t6,也有 SGW 给 eNodeB 分配的隧道 ID t7。
  8. 当 MME 发现后面的隧道都建设成功之后,就告诉 eNodeB,“后面的隧道已经建设完毕,SGW 给你分配的隧道 ID 是 t7,你可以开始连上来了,但是你也要给 SGW 分配一个隧道 ID”。
  9. eNodeB 告诉 MME 自己给 SGW 分配一个隧道,ID 为 t8。
  10. MME 将 eNodeB 给 SGW 分配的隧道 ID t8 告知 SGW,从而前面的隧道也建设完毕。

异地上网问题

为什么要分 SGW 和 PGW 呢,一个 GW 不可以吗?SGW 是你本地的运营商的设备,而 PGW 是你所属的运营商的设备。

如果你在巴塞罗那,一下飞机,手机开机,周围搜寻到的肯定是巴塞罗那的 eNodeB。通过 MME 去查寻国内运营商的 HSS,看你是否合法,是否还有钱。如果允许上网,你的手机和巴塞罗那的 SGW 会建立一个隧道,然后巴塞罗那的 SGW 和国内运营商的 PGW 建立一个隧道,然后通过国内运营商的 PGW 上网。

这样判断你是否能上网的在国内运营商的 HSS,控制你上网策略的是国内运营商的 PCRF,给手机分配的 IP 地址也是国内运营商的 PGW 负责的,给手机分配的 IP 地址也是国内运营商里统计的。运营商由于是在 PGW 里面统计的,这样你的上网流量全部通过国内运营商即可,只不过巴塞罗那运营商也要和国内运营商进行流量结算。由于你的上网策略是由国内运营商在 PCRF 中控制的,因而你还是上不了脸书。

云中网络:自己拿地成本高,购买公寓更灵活

数据中心里面堆着一大片一大片的机器,但是维护起来很麻烦。

从物理机到虚拟机

为了解决这些问题,人们发明了一种叫虚拟机的东西,并基于它产生了云计算技术。

我们常把物理机比喻为自己拿地盖房子,而虚拟机则相当于购买公寓,更加灵活方面,随时可买可卖。

它用的是软件模拟硬件的方式。刚才说了,数据中心里面用的 qemu-kvm。从名字上来讲,emu 就是 Emulator(模拟器)的意思,主要会模拟 CPU、内存、网络、硬盘,使得虚拟机感觉自己在使用独立的设备,但是真正使用的时候,当然还是使用物理的设备。

简单比喻,虚拟化软件就像一个“骗子”,向上“骗”虚拟机里面的应用,让它们感觉独享资源,其实自己啥都没有,全部向下从物理机里面弄。

虚拟网卡的原理

首先,虚拟机要有一张网卡。对于 qemu-kvm 来说,这是通过 Linux 上的一种 TUN/TAP 技术来实现的。

虚拟机是物理机上跑着的一个软件。这个软件可以像其他应用打开文件一样,打开一个称为 TUN/TAP 的 Char Dev(字符设备文件)。打开了这个字符设备文件之后,在物理机上就能看到一张虚拟 TAP 网卡。
虚拟化软件作为“骗子”,会将打开的这个文件,在虚拟机里面虚拟出一张网卡,让虚拟机里面的应用觉得它们真有一张网卡。于是,所有的网络包都往这里发。

当然,网络包会到虚拟化软件这里。它会将网络包转换成为文件流,写入字符设备,就像写一个文件一样。内核中 TUN/TAP 字符设备驱动会收到这个写入的文件流,交给 TUN/TAP 的虚拟网卡驱动。
这个驱动将文件流再次转成网络包,交给 TCP/IP 协议栈,最终从虚拟 TAP 网卡发出来,成为标准的网络包

就这样,几经转手,数据终于从虚拟机里面,发到了虚拟机外面。

虚拟网卡连接到云中

虚拟 TAP 网卡怎么接入庞大的数据中心网络中。

云计算中的网络需要注意的点:

  • 共享:尽管每个虚拟机都会有一个或者多个虚拟网卡,但是物理机上可能只有有限的网卡。那这么多虚拟网卡如何共享同一个出口?
  • 隔离:分两个方面,一个是安全隔离,两个虚拟机可能属于两个用户,那怎么保证一个用户的数据不被另一个用户窃听?一个是流量隔离,两个虚拟机,如果有一个疯狂下片,会不会导致另外一个上不了网?
  • 互通:分两个方面,一个是如果同一台机器上的两个虚拟机,属于同一个用户的话,这两个如何相互通信?另一个是如果不同物理机上的两个虚拟机,属于同一个用户的话,这两个如何相互通信?
  • 灵活:虚拟机和物理不同,会经常创建删除,从一个机器漂移到另一台机器,有的互通,有的不通,灵活性比物理网络要好的多,需要能够灵活配置。

共享与互通问题

首先,一台物理机上有多个虚拟网卡,这些虚拟网卡如何连在一起,进行相互访问,并且可以访问外网?

你可以想象物理机就是你的宿舍,虚拟机就是你的个人电脑,这些电脑怎么连接起来,需要一个交换机。

在物理机上,应该有一个虚拟的交换机,在 Linux 上有个命令brctl,可以常见虚拟网桥brctl addbr br0。创建出来之后,将虚拟网卡连接到虚拟网桥上brctl addif br0 tap0,将两个虚拟机配置相同的子网网段,两台虚拟机就可以互相通信了。

虚拟机如何连外网呢?

这里面,host-only 的网络对应的,其实就是上面两个虚拟机连到一个 br0 虚拟网桥上,而且不考虑访问外部的场景,只要虚拟机之间能够相互访问就可以了。

如果要访问外部,往往有两种方式。

  1. 一种方式称为桥接。如果在桌面虚拟化软件上选择桥接网络,则在你的笔记本电脑上,就会形成下面的结构。

每个虚拟机都会有虚拟网卡,在你的笔记本电脑上,会发现多了几个网卡,其实是虚拟交换机。这个虚拟交换机将虚拟机连接在一起。在桥接模式下,物理网卡也连接到这个虚拟交换机上,物理网卡在桌面虚拟化软件上,在“界面名称”那里选定。

如果使用桥接网络,当你登录虚拟机里看 IP 地址的时候会发现,你的虚拟机的地址和你的笔记本电脑的,以及你旁边的同事的电脑的网段是一个网段。这是为什么呢?这其实相当于将物理机和虚拟机放在同一个网桥上,相当于这个网桥上有三台机器,是一个网段的,全部打平了。我将图画成下面的样子你就好理解了。

在数据中心里面,采取的也是类似的技术,只不过都是 Linux,在每台机器上都创建网桥 br0,虚拟机的网卡都连到 br0 上,物理网卡也连到 br0 上,所有的 br0 都通过物理网卡出来连接到物理交换机上。

在这种方式下,不但解决了同一台机器的互通问题,也解决了跨物理机的互通问题,因为都在一个二层网络里面,彼此用相同的网段访问就可以了。但是当规模很大的时候,会存在问题。

在一个二层网络里面,最大的问题是广播。一个数据中心的物理机已经很多了,广播已经非常严重,需要通过 VLAN 进行划分。如果使用了虚拟机,假设一台物理机里面创建 10 台虚拟机,全部在一个二层网络里面,那广播就会很严重,所以除非是你的桌面虚拟机或者数据中心规模非常小,才可以使用这种相对简单的方式。

  1. 另外一种方式称为NAT。如果在桌面虚拟化软件中使用 NAT 模式,在你的笔记本电脑上会出现如下的网络结构。

在这种方式下,你登录到虚拟机里面查看 IP 地址,会发现虚拟机的网络是虚拟机的,物理机的网络是物理机的,两个不相同。虚拟机要想访问物理机的时候,需要将地址 NAT 成为物理机的地址。

除此之外,它还会在你的笔记本电脑里内置一个 DHCP 服务器,为笔记本电脑上的虚拟机动态分配 IP 地址。因为虚拟机的网络自成体系,需要进行 IP 管理。为什么桥接方式不需要呢?因为桥接将网络打平了,虚拟机的 IP 地址应该由物理网络的 DHCP 服务器分配。

在数据中心里面,也是使用类似的方式。这种方式更像是真的将你宿舍里面的情况,搬到一台物理机上来。

虚拟机是你的电脑,路由器和 DHCP Server 相当于家用路由器或者寝室长的电脑,物理网卡相当于你们宿舍的外网网口,用于访问互联网。所有电脑都通过内网网口连接到一个网桥 br0 上,虚拟机要想访问互联网,需要通过 br0 连到路由器上,然后通过路由器将请求 NAT 成为物理网络的地址,转发到物理网络。

隔离问题

如果一台机器上的两个虚拟机不属于同一个用户,怎么办?brctl创建的网桥也是支持 VLAN 功能的,可以设置两个虚拟机的 tag,这样在这个虚拟网桥上,两个虚拟机是不互通的。

但是如何跨物理机互通,并且实现 VLAN 的隔离?由于brctl创建的网桥上面的 tag 是没办法在网桥之外的范围内起作用的,于是我们需要寻找其他的方式。

有一个命令vconfig,可以基于物理网卡 eth0 创建带 VLAN 的虚拟网卡,所有从这个虚拟网卡出去的包,都带这个 VLAN,如果这样,跨物理机的互通和隔离就可以通过这个网卡来实现。

云计算的关键技术是虚拟化,这里我们重点关注的是,虚拟网卡通过打开 TUN/TAP 字符设备的方式,将虚拟机内外连接起来

云中的网络重点关注四个方面,共享、隔离、互通、灵活。其中共享和互通有两种常用的方式,分别是桥接和 NAT,隔离可以通过 VLAN 的方式

软件定义网络:共享基础设施的小区物业管理办法

可以这样比喻,云计算就像大家一起住公寓,要共享小区里面的基础设施,其中网络就相当于小区里面的电梯、楼道、路、大门等,大家都走,往往会常出现问题,尤其在上班高峰期,出门的人太多,对小区的物业管理就带来了挑战。

如果物业管理人员有一套智能的控制系统,在物业监控室里就能看到小区里每个单元、每个电梯的人流情况,然后在监控室里面,只要通过远程控制的方式,拨弄一个手柄,电梯的速度就调整了,栅栏门就打开了,某个入口就改出口了。

这就是软件定义网络(SDN)。它主要有以下三个特点。

  • 控制与转发分离:转发平面就是一个个虚拟或者物理的网络设备,就像小区里面的一条条路。控制平面就是统一的控制中心,就像小区物业的监控室。它们原来是一起的,物业管理员要从监控室出来,到路上去管理设备,现在是分离的,路就是走人的,控制都在监控室。
  • 控制平面与转发平面之间的开放接口:控制器向上提供接口,被应用层调用,就像总控室提供按钮,让物业管理员使用。控制器向下调用接口,来控制网络设备,就像总控室会远程控制电梯的速度。这里经常使用两个名词,前面这个接口称为北向接口,后面这个接口称为南向接口,上北下南嘛。
  • 逻辑上的集中控制:逻辑上集中的控制平面可以控制多个转发面设备,也就是控制整个物理网络,因而可以获得全局的网络状态视图,并根据该全局网络状态视图实现对网络的优化控制,就像物业管理员在监控室能够看到整个小区的情况,并根据情况优化出入方案。

OpenFlow 和 OpenvSwitch

一种开源的 SDN 实现方式。

OpenFlow 是 SDN 控制器和网络设备之间互通的南向接口协议,OpenvSwitch 用于创建软件的虚拟交换机。OpenvSwitch 是支持 OpenFlow 协议的,当然也有一些硬件交换机也支持 OpenFlow 协议。
它们都可以被统一的 SDN 控制器管理,从而实现物理机和虚拟机的网络连通。

SDN 控制器是如何通过 OpenFlow 协议控制网络的?

在 OpenvSwitch 里面,有一个流表规则,任何通过这个交换机的包,都会经过这些规则进行处理,从而接收、转发、放弃。

云中的网络安全:虽然不是土豪,也需要基本安全和保障

对于公有云上的虚拟机,建议是仅仅开放需要的端口,而将其他的端口一概关闭。这个时候,你只要通过安全措施守护好这个唯一的入口就可以了。采用的方式常常是用 ACL(Access Control List,访问控制列表)来控制 IP 和端口。

设置好了这些规则,只有指定的 IP 段能够访问指定的开放接口,就算有个有漏洞的后台进程在那里,也会被屏蔽,黑客进不来。在云平台上,这些规则的集合常称为安全组。那安全组怎么实现呢?

首先拿下 MAC 头看看,是不是我的。如果是,则拿下 IP 头来。得到目标 IP 之后呢,就开始进行路由判断。在路由判断之前,这个节点我们称为PREROUTING。如果发现 IP 是我的,包就应该是我的,就发给上面的传输层,这个节点叫作INPUT。如果发现 IP 不是我的,就需要转发出去,这个节点称为FORWARD。如果是我的,上层处理完毕完毕后,一般会返回一个处理结果,这个处理结果会发出去,这个节点称为OUTPUT,无论是 FORWARD 还是 OUTPUT,都是路由判断之后发生的,最后一个节点是POSTROUTING

整个包的处理过程还是原来的过程,只不过为什么要格外关注这五个节点呢?

因为在 Linux 内核中,有一个框架叫 Netfilter。它可以在这些节点插入 hook 函数。这些函数可以截获数据包,对数据包进行干预。例如做一定的修改,然后决策是否接着交给 TCP/IP 协议栈处理;或者可以交回给协议栈,那就是ACCEPT;或者过滤掉,不再传输,就是DROP;还有就是QUEUE,发送给某个用户态进程处理。

有了这个 Netfilter 框架就太好了,你可以在 IP 转发的过程中,随时干预这个过程,只要你能实现这些 hook 函数。

一个著名的实现,就是内核模块 ip_tables。它在这五个节点上埋下函数,从而可以根据规则进行包的处理。按功能可分为四大类:连接跟踪(conntrack)、数据包的过滤(filter)、网络地址转换(nat)和数据包的修改(mangle)。其中连接跟踪是基础功能,被其他功能所依赖。其他三个可以实现包的过滤、修改和网络地址转换。

在用户态,还有一个你肯定知道的客户端程序 iptables,用命令行来干预内核的规则。内核的功能对应 iptables 的命令行来讲,就是表和链的概念。

iptables 的表分为四种:raw–>mangle–>nat–>filter。这四个优先级依次降低,raw 不常用,所以主要功能都在其他三种表里实现。每个表可以设置多个链。

filter 表处理过滤功能,主要包含三个链:

  • INPUT 链:过滤所有目标地址是本机的数据包
  • FORWARD 链:过滤所有路过本机的数据包
  • OUTPUT 链:过滤所有由本机产生的数据包

nat 表主要是处理网络地址转换,可以进行 Snat(改变数据包的源地址)、Dnat(改变数据包的目标地址),包含三个链:

  • PREROUTING 链:可以在数据包到达防火墙时改变目标地址
  • OUTPUT 链:可以改变本地产生的数据包的目标地址
  • POSTROUTING 链:在数据包离开防火墙时改变数据包的源地址

mangle 表主要是修改数据包,包含:

  • PREROUTING 链
  • INPUT 链
  • FORWARD 链
  • OUTPUT 链
  • POSTROUTING 链

将 iptables 的表和链加入到上面的过程图中,就形成了下面的图和过程。

  1. 数据包进入的时候,先进 mangle 表的 PREROUTING 链。在这里可以根据需要,改变数据包头内容之后,进入 nat 表的 PREROUTING 链,在这里可以根据需要做 Dnat,也就是目标地址转换。
  2. 进入路由判断,要判断是进入本地的还是转发的。
  3. 如果是进入本地的,就进入 INPUT 链,之后按条件过滤限制进入。
  4. 之后进入本机,再进入 OUTPUT 链,按条件过滤限制出去,离开本地。
  5. 如果是转发就进入 FORWARD 链,根据条件过滤限制转发。
  6. 之后进入 POSTROUTING 链,这里可以做 Snat,离开网络接口。

有了 iptables 命令,我们就可以在云中实现一定的安全策略。例如我们可以处理前面的偷窥事件。首先我们将所有的门都关闭。

iptables -t filter -A INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -j DROP

-s表示源 IP 地址段,-d表示目标地址段,DROP表示丢弃,也即无论从哪里来的,要想访问我这台机器,全部拒绝,谁也黑不进来。

但是你发现坏了,ssh 也进不来了,都不能远程运维了,可以打开一下。

iptables -I INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -p tcp --dport 22 -j ACCEPT

如果这台机器是提供的是 web 服务,80 端口也应该打开,当然一旦打开,这个 80 端口就需要很好的防护,但是从规则角度还是要打开。

iptables -A INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -p tcp --dport 80 -j ACCEPT

这些规则都可以在虚拟机里,自己安装 iptables 自己配置。但是如果虚拟机数目非常多,都要配置,对于用户来讲就太麻烦了,能不能让云平台把这部分工作做掉呢?

当然可以了。在云平台上,一般允许一个或者多个虚拟机属于某个安全组,而属于不同安全组的虚拟机之间的访问以及外网访问虚拟机,都需要通过安全组进行过滤。

例如图中,我们会创建一系列的网站,都是前端在 Tomcat 里面,对外开放 8080 端口。数据库使用 MySQL,开放 3306 端口。

为了方便运维,我们创建两个安全组,将 Tomcat 所在的虚拟机放在安全组 A 里面。在安全组 A 里面,允许任意 IP 地址0.0.0.0/0访问 8080 端口,但是对于 ssh 的 22 端口,仅仅允许管理员网段203.0.113.0/24访问。

我们将 MySQL 所在的虚拟机在安全组 B 里面。在安全组 B 里面,仅仅允许来自安全组 A 的机器访问 3306 端口,但是对于 ssh 的 22 端口,同样允许管理员网段 203.0.113.0/24 访问。

前面的章节我们说过,在设计云平台的时候,我们想让虚拟机之间的网络和物理网络进行隔离,但是虚拟机毕竟还是要通过物理网和外界通信的,因而需要在出物理网的时候,做一次网络地址转换,也即 nat,这个就可以用 iptables 来做。

我们学过,IP 头里面包含源 IP 地址和目标 IP 地址,这两种 IP 地址都可以转换成其他地址。转换源 IP 地址的,我们称为 Snat;转换目标 IP 地址的,我们称为 Dnat

你有没有思考过这个问题,TCP 的访问都是一去一回的,而你在你家里连接 WIFI 的 IP 地址是一个私网 IP,192.168.1.x。当你通过你们家的路由器访问 163 网站之后,网站的返回结果如何能够到达你的笔记本电脑呢?肯定不能通过192.168.1.x,这是个私网 IP,不具有公网上的定位能力,而且用这个网段的人很多,茫茫人海,怎么能够找到你呢?

所以当你从你家里访问 163 网站的时候,在你路由器的出口,会做 Snat 的,运营商的出口也可能做 Snat,将你的私网 IP 地址,最终转换为公网 IP 地址,然后 163 网站就可以通过这个公网 IP 地址返回结果,然后再 nat 回来,直到到达你的笔记本电脑。

云平台里面的虚拟机也是这样子的,它只有私网 IP 地址,到达外网网口要做一次 Snat,转换成为机房网 IP,然后出数据中心的时候,再转换为公网 IP。

这里有一个问题是,在外网网口上做 Snat 的时候,是全部转换成一个机房网 IP 呢,还是每个虚拟机都对应一个机房网 IP,最终对应一个公网 IP 呢?前面也说过了,公网 IP 非常贵,虚拟机也很多,当然不能每个都有单独的机房网和公网 IP 了,于是这种 Snat 是一种特殊的 Snat,MASQUERADE(地址伪装)

这种方式下,所有的虚拟机共享一个机房网和公网的 IP 地址,所有从外网网口出去的,都转换成为这个 IP 地址。那又一个问题来了,都变成一个公网 IP 了,当 163 网站返回结果的时候,给谁呢,再 nat 成为哪个私网的 IP 呢?

这就是 Netfilter 的连接跟踪(conntrack)功能了。对于 TCP 协议来讲,肯定是上来先建立一个连接,可以用“源/目的 IP+源/目的端口”唯一标识一条连接,这个连接会放在 conntrack 表里面。当时是这台机器去请求 163 网站的,虽然源地址已经 Snat 成公网 IP 地址了,但是 conntrack 表里面还是有这个连接的记录的。当 163 网站返回数据的时候,会找到记录,从而找到正确的私网 IP 地址。

如果虚拟机做服务器呢?也就是说,如果虚拟机里面部署的就是 163 网站呢?

这个时候就需要给这个网站配置固定的物理网的 IP 地址和公网 IP 地址了。这时候就需要显示的配置 Snat 规则和 Dnat 规则了

当外部访问进来的时候,外网网口会通过 Dnat 规则将公网 IP 地址转换为私网 IP 地址,到达虚拟机,虚拟机里面是 163 网站,返回结果,外网网口会通过 Snat 规则,将私网 IP 地址转换为那个分配给它的固定的公网 IP 地址。

  • 源地址转换(Snat):iptables -t nat -A -s 私网IP -j Snat --to-source 外网IP
  • 目的地址转换(Dnat):iptables -t nat -A -PREROUTING -d 外网IP -j Dnat --to-destination 私网IP

云中的网络 QoS:邻居疯狂下电影,我该怎么办?

你租房子的时候,有没有碰到这样的情况:本来合租共享 WIFI,一个人狂下小电影,从而你网都上不去,是不是很懊恼?

在云平台上,也有这种现象,好在有一种流量控制的技术,可以实现QoS(Quality of Service),从而保障大多数用户的服务质量。

对于控制一台机器的网络的 QoS,分两个方向,一个是入方向,一个是出方向。

其实我们能控制的只有出方向,通过 Shaping,将出的流量控制成自己想要的模样。而进入的方向是无法控制的,只能通过 Policy 将包丢弃。

控制网络的 QoS 有哪些方式?

在 Linux 下,可以通过 TC 控制网络的 QoS,主要就是通过队列的方式。

无类别排队规则

  1. pfifo_fast
    这是一种不把网络包分类的技术。

pfifo_fast 分为三个先入先出的队列,称为三个 Band。根据网络包里面 TOS,看这个包到底应该进入哪个队列。TOS 总共四位,每一位表示的意思不同,总共十六种类型。

通过命令行tc qdisc show dev eth0,可以输出结果 priomap,也是十六个数字。在 0 到 2 之间,和 TOS 的十六种类型对应起来,表示不同的 TOS 对应的不同的队列。其中 Band 0 优先级最高,发送完毕后才轮到 Band 1 发送,最后才是 Band 2。

  1. 随机公平队列
    会建立很多的 FIFO 的队列,TCP Session 会计算 hash 值,通过 hash 值分配到某个队列。在队列的另一端,网络包会通过轮询策略从各个队列中取出发送。这样不会有一个 Session 占据所有的流量。

  2. 令牌桶规则

所有的网络包排成队列进行发送,但不是到了队头就能发送,而是需要拿到令牌才能发送。令

牌根据设定的速度生成,所以即便队列很长,也是按照一定的速度进行发送的。

当没有包在队列中的时候,令牌还是以既定的速度生成,但是不是无限累积的,而是放满了桶为止。设置桶的大小为了避免下面的情况:当长时间没有网络包发送的时候,积累了大量的令牌,突然来了大量的网络包,每个都能得到令牌,造成瞬间流量大增。

基于类别的队列规则

  1. 分层令牌桶规则(HTB,Hierarchical Token Bucket)

HTB 往往是一棵树,接下来我举个具体的例子,通过 TC 如何构建一棵 HTB 树来带你理解。

使用 TC 可以为某个网卡 eth0 创建一个 HTB 的队列规则,需要付给它一个句柄为(1:)。

这是整棵树的根节点,接下来会有分支。例如图中有三个分支,句柄分别为(:10)、(:11)、(:12)。最后的参数 default 12,表示默认发送给 1:12,也即发送给第三个分支。

tc qdisc add dev eth0 root handle 1: htb default 12

对于这个网卡,需要规定发送的速度。一般有两个速度可以配置,一个是rate,表示一般情况下的速度;一个是ceil,表示最高情况下的速度。对于根节点来讲,这两个速度是一样的,于是创建一个 root class,速度为(rate=100kbps,ceil=100kbps)。

tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps

接下来要创建分支,也即创建几个子 class。每个子 class 统一有两个速度。三个分支分别为(rate=30kbps,ceil=100kbps)、(rate=10kbps,ceil=100kbps)、(rate=60kbps,ceil=100kbps)。

tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps

发现三个 rate 加起来,是整个网卡允许的最大速度。

HTB 有个很好的特性,同一个 root class 下的子类可以相互借流量,如果不直接在队列规则下面创建一个 root class,而是直接创建三个 class,它们之间是不能相互借流量的。借流量的策略,可以使得当前不使用这个分支的流量的时候,可以借给另一个分支,从而不浪费带宽,使带宽发挥最大的作用。

创建叶子队列规则,分别为fifosfq

tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5
tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5
tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10

基于这个队列规则,我们还可以通过 TC 设定发送规则:从1.2.3.4来的,发送给 port 80 的包,从第一个分支 1:10 走;其他从1.2.3.4发送来的包从第二个分支 1:11 走;其他的走默认分支。

tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11

如何控制 QoS?

使用 OpenvSwitch 将云中的网卡连通在一起,那如何控制 QoS?
OpenvSwitch 支持两种:

  1. 对于进入的流量,可以设置策略 Ingress policy
  2. 对于发出的流量,可以设置 QoS 规则 Egress shaping,支持 HTB

云中网络的隔离 GRE、VXLAN:虽然住一个小区,也要保护隐私

云平台中的隔离问题,前面咱们用的策略一直都是 VLAN,但是我们也说过这种策略的问题,VLAN 只有 12 位,共 4096 个。当时设计的时候,看起来是够了,但是现在绝对不够用,怎么办呢?

扩展,在原来包的格式的基础上扩展出一个头,里面包含足够用于区分租户的 ID,外层的包的格式尽量和传统的一样,依然兼容原来的格式。一旦遇到需要区分用户的地方,我们就用这个特殊的程序,来处理这个特殊的包的格式。

这个概念很像隧道理论,之前讲的 VPN 协议,扩展的包头主要是用于加密的,而我们现在需要的包头是要能够区分用户的。

底层的物理网络设备组成的网络我们称为Underlay 网络,而用于虚拟机和云中的这些技术组成的网络称为Overlay 网络,这是一种基于物理网络的虚拟化网络实现。

GRE

GRE,全称 Generic Routing Encapsulation,它是一种 IP-over-IP 的隧道技术。它将 IP 包封装在 GRE 包里,外面加上 IP 头,在隧道的一端封装数据包,并在通路上进行传输,到另外一端的时候解封装。你可以认为 Tunnel 是一个虚拟的、点对点的连接。

GRE 还需要有一个地方来封装和解封装 GRE 的包,这个地方往往是路由器或者有路由功能的 Linux 机器。

GRE 的问题:

  • Tunnel 的数量问题。GRE 是一种点对点隧道,如果有三个网络,就需要在每两个网络之间建立一个隧道。如果网络数目增多,这样隧道的数目会呈指数性增长。
  • GRE 不支持组播,因此一个网络中的一个虚机发出一个广播帧后,GRE 会将其广播到所有与该节点有隧道连接的节点。
  • 目前还是有很多防火墙和三层网络设备无法解析 GRE,因此它们无法对 GRE 封装包做合适地过滤和负载均衡。

VXLAN

第二种 Overlay 的技术称为 VXLAN。和三层外面再套三层的 GRE 不同,VXLAN 则是从二层外面就套了一个 VXLAN 的头,这里面包含的 VXLAN ID 为 24 位,也够用了。在 VXLAN 头外面还封装了 UDP、IP,以及外层的 MAC 头。

VXLAN 作为扩展性协议,也需要一个地方对 VXLAN 的包进行封装和解封装,实现这个功能的点称为 VTEP(VXLAN Tunnel Endpoint)。

VTEP 相当于虚拟机网络的管家。每台物理机上都可以有一个 VTEP。每个虚拟机启动的时候,都需要向这个 VTEP 管家注册,每个 VTEP 都知道自己上面注册了多少个虚拟机。当虚拟机要跨 VTEP 进行通信的时候,需要通过 VTEP 代理进行,由 VTEP 进行包的封装和解封装。

VXLAN 不是点对点的,而是支持通过组播的来定位目标机器的,而非一定是这一端发出,另一端接收。

当一个 VTEP 启动的时候,它们都需要通过 IGMP 协议。加入一个组播组,就像加入一个邮件列表,或者加入一个微信群一样,所有发到这个邮件列表里面的邮件,或者发送到微信群里面的消息,大家都能收到。而当每个物理机上的虚拟机启动之后,VTEP 就知道,有一个新的 VM 上线了,它归我管。

如图,虚拟机 1、2、3 属于云中同一个用户的虚拟机,因而需要分配相同的 VXLAN ID=101。在云的界面上,就可以知道它们的 IP 地址,于是可以在虚拟机 1 上 ping 虚拟机 2。
虚拟机 1 发现,它不知道虚拟机 2 的 MAC 地址,因而包没办法发出去,于是要发送 ARP 广播。

ARP 请求到达 VTEP1 的时候,VTEP1 知道,我这里有一台虚拟机,要访问一台不归我管的虚拟机,需要知道 MAC 地址,可是我不知道啊,这该咋办呢?
VTEP1 想,我不是加入了一个微信群么?可以在里面@all 一下,问问虚拟机 2 归谁管。于是 VTEP1 将 ARP 请求封装在 VXLAN 里面,组播出去。
当然在群里面,VTEP2 和 VTEP3 都收到了消息,因而都会解开 VXLAN 包看,里面是一个 ARP。
VTEP3 在本地广播了半天,没人回,都说虚拟机 2 不归自己管。
VTEP2 在本地广播,虚拟机 2 回了,说虚拟机 2 归我管,MAC 地址是这个。通过这次通信,VTEP2 也学到了,虚拟机 1 归 VTEP1 管,以后要找虚拟机 1,去找 VTEP1 就可以了。

VTEP2 将 ARP 的回复封装在 VXLAN 里面,这次不用组播了,直接发回给 VTEP1。
VTEP1 解开 VXLAN 的包,发现是 ARP 的回复,于是发给虚拟机 1。通过这次通信,VTEP1 也学到了,虚拟机 2 归 VTEP2 管,以后找虚拟机 2,去找 VTEP2 就可以了。
虚拟机 1 的 ARP 得到了回复,知道了虚拟机 2 的 MAC 地址,于是就可以发送包了。

虚拟机 1 发给虚拟机 2 的包到达 VTEP1,它当然记得刚才学的东西,要找虚拟机 2,就去 VTEP2,于是将包封装在 VXLAN 里面,外层加上 VTEP1 和 VTEP2 的 IP 地址,发送出去。
网络包到达 VTEP2 之后,VTEP2 解开 VXLAN 封装,将包转发给虚拟机 2。

虚拟机 2 回复的包,到达 VTEP2 的时候,它当然也记得刚才学的东西,要找虚拟机 1,就去 VTEP1,于是将包封装在 VXLAN 里面,外层加上 VTEP1 和 VTEP2 的 IP 地址,也发送出去。
网络包到达 VTEP1 之后,VTEP1 解开 VXLAN 封装,将包转发给虚拟机 1。

容器网络:来去自由的日子,不买公寓去合租

如果说虚拟机是买公寓,容器则相当于合租,有一定的隔离,但是隔离性没有那么好。

云计算解决了基础资源层的弹性伸缩,却没有解决 PaaS 层应用随基础资源层弹性伸缩而带来的批量、快速部署问题。于是,容器应运而生。

容器的思想就是要变成软件交付的集装箱。集装箱的特点,一是打包,二是标准。比如在把货从一个码头运到另一个码头,使用集装箱把货物打包,就可以一整箱搬上船,在整箱搬到另一个码头。如果没有集装箱,就要先一件件搬上船码好,在一件件搬到另一个码头。

容器如何对应用打包

容器实现隔离主要用了两种技术,一种是看起来是隔离的技术,称为namespace,即每个 namespace 中的应用看到的是不同的 IP 地址、用户空间、程号等。
一种是用起来是隔离的技术,称为cgroup,也即明明整台机器有很多的 CPU、内存,而一个应用只能用其中的一部分。

所谓镜像,就是将你焊好集装箱的那一刻,将集装箱的状态保存下来,然后将这一刻的状态保存成一系列文件。无论从哪里运行这个镜像,都能完整地还原当时的情况。

namespace

在 Linux 下很多的资源都是全局的。比如进程有全局的进程 ID,网络也有全局的路由表。但是,当一台 Linux 上跑多个进程的时候,如果我们觉得使用不同的路由策略,这些进程可能会冲突,那就需要将这个进程放在一个独立的 namespace 里面,这样就可以独立配置网络了

cgroup

cgroup 全称 control groups,是 Linux 内核提供的一种可以限制、隔离进程使用的资源机制。

cgroup 能控制哪些资源呢?它有很多子系统:

  • CPU 子系统使用调度程序为进程控制 CPU 的访问
  • cpuset,如果是多核心的 CPU,这个子系统会为进程分配单独的 CPU 和内存
  • memory 子系统,设置进程的内存限制以及产生内存资源报告
  • blkio 子系统,设置限制每个块设备的输入输出控制
  • net_cls,这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包

cgroup 提供了一个虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 cgroup,必须挂载 cgroup 文件系统,一般情况下都是挂载到/sys/fs/cgroup目录下。

容器网络中如何融入物理网络?

如果你使用 docker run 运行一个容器,你应该能看到这样一个拓扑结构。

是不是和虚拟机很像?容器里面有张网卡,容器外有张网卡,容器外的网卡连到 docker0 网桥,通过这个网桥,容器直接实现相互访问。如果你用 brctl 查看 docker0 网桥,你会发现它上面连着一些网卡。其实这个网桥和用 brctl 创建的网桥没什么两样。

那连接容器和网桥的那个网卡和虚拟机一样吗?在虚拟机场景下,有一个虚拟化软件,通过 TUN/TAP 设备虚拟一个网卡给虚拟机,但是容器场景下并没有虚拟化软件,这该怎么办呢?在 Linux 下,可以创建一对veth pair的网卡,从一边发送包,另一边就能收到。

一台机器内部容器的如何访问外网?就是虚拟机里面的桥接模式和 NAT 模式。Docker 默认使用 NAT 模式。NAT 模式分为 SNAT 和 DNAT,如果是容器内部访问外部,就需要通过 SNAT。在宿主机上,有这么一条 iptables 规则:

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

所有从容器内部发出来的包,都要做地址伪装,将源 IP 地址,转换为物理网卡的 IP 地址。如果有多个容器,所有的容器共享一个外网的 IP 地址,但是在 conntrack 表中,记录下这个出去的连接。当服务器返回结果的时候,到达物理机,会根据 conntrack 表中的规则,取出原来的私网 IP,通过 DNAT 将地址转换为私网 IP 地址,通过网桥 docker0 实现对内的访问。

Docker 有两种方式,一种是通过一个进程docker-proxy的方式,监听 10080,转换为 80 端口。

/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10080 -container-ip 172.17.0.2 -container-port 80

另外一种方式是通过 DNAT 方式,在-A PREROUTING 阶段加一个规则,将到端口 10080 的 DNAT 称为容器的私有网络。

-A DOCKER -p tcp -m tcp --dport 10080 -j DNAT --to-destination 172.17.0.2:80

容器网络之 Flannel:每人一亩三分地

容器作为集装箱,可以保证应用在不同的环境中快速迁移,提高迭代的效率。但是如果要形成容器集团军,还需要一个集团军作战的调度平台,这就是 Kubernetes。它可以灵活地将一个容器调度到任何一台机器上,并且当某个应用扛不住的时候,只要在 Kubernetes 上修改容器的副本数,一个应用马上就能变八个,而且都能提供服务。

集团军作战有个重要的问题,就是通信。这里面包含两个问题,第一个是集团军的 A 部队如何实时地知道 B 部队的位置变化,第二个是两个部队之间如何相互通信。

第一个问题位置变化,往往是通过一个称为注册中心的地方统一管理的。这个是应用自己做的。当一个应用启动的时候,将自己所在环境的 IP 地址和端口,注册到注册中心指挥部,这样其他的应用请求它的时候,到指挥部问一下它在哪里就好了

接下来是如何相互通信的问题。NAT 这种模式,在多个主机的场景下,是存在很大问题的。在物理机 A 上的应用 A 看到的 IP 地址是容器 A 的,是172.17.0.2,在物理机 B 上的应用 B 看到的 IP 地址是容器 B 的,不巧也是172.17.0.2,当它们都注册到注册中心的时候,注册中心就是这个图里这样子。

应用 A 要访问应用 B,当应用 A 从注册中心将应用 B 的 IP 地址读出来的时候,就彻底困惑了,这不是自己访问自己吗?

怎么解决这个问题呢?一种办法是不去注册容器内的 IP 地址,而是注册所在物理机的 IP 地址,端口也要是物理机上映射的端口。但是一方面,大部分分布式框架都是容器诞生之前就有了,它们不会适配这种场景;另一方面,让容器内的应用意识到容器外的环境,本来就是非常不好的设计。

于是业界就涌现了大量的方案,Flannel 就是其中之一。

原文链接:https://blog.shipengqi.top/2018/12/12/network-protocol-2/

本文来自《趣谈网络协议(下)》- 熊清亮的博客。

转载请注明原文链接:https://seealso.cn/linux/network-protocol-2