12.凤凰架构:构建可靠的大型分布式系统 --- 容器间网络

第12章 容器间网络
	"虚拟化网络"是一项内容十分丰富,研究历史十分悠久的计算机技术,是计算机科学中一门独立的分支,完全不依附于虚拟化容器的存在。网络运营商
提及的"网络功能虚拟化"(Network Function Virtualization,NFV),网络设备商和网络管理软件提及的"软件定义网络"(Software Defined Networking,
SDN)等都属于虚拟化网络的范畴。
	
	下面讨论的是狭义的,它特指"如何基于Linux系统的网络虚拟化技术来实现容器间网络通信",更通俗一点,就是只关注那些为了使互相隔离的Linux网络名称
空间可互相通信而设计出来的虚拟化网络设施。

12.1 Linux网络虚拟化
		在Linux目前提供的八种名称空间里,网络名称空间无疑是隔离内容最多的一种,它为名称空间内的所有进程提供了全套的网络设施,包括独立的设备界面、
	路由表、ARP表、IP地址表、iptables/ebtables规则、协议栈,等等。虚拟化容器是以Linux名称空间的隔离性为基础来实现的,要解决隔离的容器之间、
	容器与宿主之间,乃至扩物理网络的不同容器间通信的问题的责任,很自然也落在了Linux网络虚拟化技术的肩上。

	12.1.1 网络通信模型
		Linux网络协议栈:
			1.Socket
				应用程序是通过socket编程接口与内核空间的网络协议栈通信的。
			2.TCP/UDP
				这一层还有 流控制传输协议(SCTP)、数据报拥塞控制协议(DCCP)等。
			3.IP
				还有 因特网组管理协议(IGMP)、大量的路由协议(EGP、NHRP、OSPF、IGRP)等。
			4.Device
				网络设备是网络访问层中面向系统一侧的接口,这里说的设备与物理硬件设备不是一个概念,Device只是一种面向操作系统开放的接口,其背后可能
			代表真实的物理硬件,也可能只是某段具有特定功能的程序代码,譬如即使不存在物理网卡,也依然可以存在回环设备(Lookback Device)。许多网络
			抓包工具,如tcpdump,Wireshark便是在此工作,前面介绍的微服务流量控制时曾提到的网络流量整形,通常也是在这里完成的。Device的主要作用
			是抽象出统一的界面,让程序代码去选择或影响收发包出入口,譬如决定数据应该从哪块网卡设备发送出去。
			5.Driver
				网卡驱动程序是网络访问层中面向硬件一侧的接口,它会通过DMA将主存中待发送的数据包复制到驱动内部的缓存之中。

	12.1.2 干预网络通信
		网络协议栈的处理是一套相对固定和封闭的流程,整套处理过程,除了在网络设备层能看到一点点程序已设备的形式介入处理的空间外,其他过程就似乎没有
	什么课供程序插手的地方了。从Linux Kernel 2.4 版本开始,内核开放了一套通用的、可供代码干预数据在协议栈中流转的过滤器框架。名为Netfilter的
	框架,它围绕网络层(IP层)埋下了5个钩子(Hook),每当有数据包流到网络层,经过这些钩子的时候,就会自动触发由内核模块注册在这里的回调函数。5个钩子
	的名字如下:
		1.PREROUTING
			来自设备的数据包进入协议栈后立即触发此钩子。PREROUTING钩子在进入IP路由之前触发,这意味着只要接收到数据包,无论是否真的发往本机,
		都会触发此钩子。一般用于目标网络地址转换(DNAT)。

		2.INPUT
			报文经过IP路由后,如果确定发往本机,就会触发此钩子,一般用于加工发往本地进程的数据包。

		3.FORWARD
			报文经过IP路由后,如果确定不是发往本机,就会触发此钩子,一般用于处理转发到其他机器的数据包。

		4.OUTPUT
			从本机程序发出的数据包,在经过IP路由前,将会触发此钩子,一般用于加工本地进程的输出数据包。

		5.POSTROUTING
			从本机网卡发出的数据包,无论是本机的程序所发出的,还是由本机转发给其他机器的,都会触发此钩子,一般用于源网络地址换换(SNAT)


		Netfilter允许在同一个钩子处注册多个回调函数,因此向钩子注册回调函数时必须提供明确的优先级,以便触发时能按照优先级从高到低进行激活。由于
	回调函数会存在多个,看起来像挂在同一个钩子上的链条,因此钩子触发的回调函数集合被称为"回调链"(Chained Callback),这个名字也导致后续基于
	Netfileter设计的 Xtables 系工具,如 iptables 均有使用到"链"(Chain)的概念。虽然现在看Netfilter只是一个简单的事件回调机制,然而却是
	Linux网络大厦的核心基石,Linux系统提供的许多网络能力,如数据包过滤、封包处理(设置标志位、修改ttl等)、地址伪装、网络地址转换、透明代理、
	访问控制、基于协议类型的连接追踪、带宽限速,等等,都是在Netfilter基础之上实现的。

		以Netfilter为基础的应用有很多,其中使用最广泛的无疑要数Xtables系列工具了,譬如iptables,ebtables,arptables,ip6tables等。
	iptables被称为"Linux自带的防火墙",然后实际上iptables实际能做的事情远超防火墙的范畴。严谨的说,iptables比较贴切的定位应是能够代替
	Netfilter多数常规功能的ip包过滤工具。Netfilter的钩子回调虽然强大,但仍然要通过程序编码才能使用,而设计iptables的目的便是以配置去实现
	原本用Netfilter编码才能做的事情。iptables先把用户常用的管理意图总结成具体的行为预先准备好,然后在满足条件的时候自动激活行为。

		iptables预置的行为:
			1.DROP
			2.REJECT
			3.QUEUE
			4.RETURN
			5.ACCEPT
			6.JUMP
			7.REDIRECT
			8.MASQUEREAD
			9.LOG

		这些行为本来能够被挂在到 Netfilter钩子的回调链上,但iptables进行了一层额外抽象,不是把行为与链直接挂钩,而是根据这些底层操作的目的,
	先总结为更高层次的规则。举例子,假设挂在规则的目的是实现网络地址转换,那就应该对符合某种特征的流量、在某个钩子上进行MASQUEREAD行为,这样
	具有相同目的的规则,就应该放到一起才便于管理,由此形成"规则表"的概念。iptables内置了五张不可扩展的规则表,如下:
		1.raw表
			用于去除数据包上的连接追踪机制。
		2.mangle表
			用于修改数据包的报文头信息,如服务类型(ToS)、生存周期(TTL)以及为数据包设置Mark标记,典型的应用就是服务质量管理(QoS)。
		3.nat表
			用于修改数据包的源或者目的地址等信息,典型的应用就是网络地址转换。
		4.filter表
			用于对数据包进行过滤,控制到达某条链上的数据包是否继续放行、直接丢弃或者拒绝(ACCEPT,DROP,REJECT),典型的应用就是防火墙。
		5.security表
			用于在数据包上应用SELinux,比较少用。

		以上5张表是具有优先级的:raw -> mangle -> nat -> filter -> security。在iptables中新增规则时,需要按照规则的意图指定要存入哪张
	表中,如果没有指定,将默认存入filter表。

		从名字上就可以看出预置的5条链直接源于Netfilter的钩子,它们与5张规则表的对应关系是固定的,用户不能增加自定义的表,或者修改已有表与链
	的关系,但可以增加自定义的链,新增的自定义链与Netfilter的钩子没有天然的对应关系,换言之就是不会被自动触发,只有显示使用JUMP行为,从默认的
	5条链中跳转过去才能被执行。

		iptables不仅仅是Linux系统自带的一个网络工具,它在容器间通信也扮演者相当重要的角色。譬如k8s用来管理Service的Endpoints的核心组件
	kube-proxy,就依赖iptables来完成ClusterIP到Pod的通信(也可以采用IPVS,IPVS同样是基于Netfilter的)这种通信的本质就是一种NAT访问。

	12.1.3 虚拟化网络设备
		1.网卡:tun/tap、veth
			目前主流的虚拟网卡方案有 tun/tap 和 veth 两种,在时间上 tun/tap 出现的更早,它是一组通用的虚拟驱动程序包,里面包含两个设备,
		分别是用于 网络数据包处理的虚拟网卡驱动,以及用于内核空间与用户空间交换的字符设备(Charcter Device,这里具体是指 /dev/net/tun)
		驱动。

			tun和tap是两个相对独立的虚拟网络设备,其中tap模拟以太网设备,操作二层数据包(以太帧),tun则模拟了网络层设备,操作三层数据包(IP报文)。
		使用tun/tap 设备的目的是实现把来自协议栈的数据包先交由某个打开了 /dev/net/tun 字符设备的用户进程处理后,再把数据包重新发回链路中。可以
		通俗的理解为,它一端连着网络协议栈,另外一端连着 用户程序,而普通的网卡驱动则是一端连着网络协议栈,另外一端连着物理网卡。只要协议栈中的
		数据包能被用户态程序截获并加工处理,程序员就有足够的舞台空间去玩出各种花样,譬如数据压缩、流量加密、透明代理等功能都能够以此为基础实现,最
		典型的如VPN。

			应用程序通过tun0设备对外发送数据包后,tun0设备如果发现另外一端的字符设备已经被程序打开(这就是一端连着网络协议栈,另外一端连着用户
		态程序),变会把数据包通过字符设备发送给程序,收到数据包后,会修改后再将其封装成新报文,譬如数据包原本是发送给A地址的,把整个数据包
		进行加密,然后作为报文体,封装到另外一个发送给B地址的新数据包中。这种将一个数据包套进另外一个数据包的处理方式被称为"隧道"(Tunneling),
		隧道技术是在物理网络中构筑逻辑网络的经典做法。而其中提到的加密,也有标准的协议可遵循,如IPSec协议。

			使用tun/tap设备传输需要经过两次协议栈,有一定的性能损耗,如果条件允许,容器对容器的直接通信并不会把tun/tap 作为首选方案,一般是基于
		veth来实现的。但是tun/tap没有veth那样要求设备成对出现、数据要原样传输的限制,数据包到用户态后,程序员就有完全掌控的权利,要修改哪些,要
		发送到什么地方,都可以通过编码实现,因此tun/tap比veth有更广泛的适用范围。

			veth 是另外一种主流的虚拟网卡方案,在Linux Kernel 2.6 中,Linux开始支持网络名称空间隔离的同时,也提供了专门的虚拟以太网(Virtual
		 Ethernet,简写veth)让两个隔离的网络名称空间之间可以互相通信。直接把veth比喻成虚拟网卡其实不准确,如果要和物理设备类比,它应该相当于由
		 交叉网线连接的一对物理网卡。

		 	交叉线序、直连线序:
		 		交叉网线是指一头是 T568A标准,一头是T568B标准的网线。直连网线则是两头采用同一种标准的网线。

		 		网卡对网卡这样的同类设备需要使用交叉线序的网线来连接,网卡到交换机、路由器就采用直连线序的网线,不过现在的网卡大多带有线序翻转的
		 	功能,直连线也可以网卡对网卡的连通了。

		 	veth实际上不是一个设备,而是一对设备,因此被称作veth pair。要使用veth,必须在两个独立的网络名称空间中进行才有意义,因为veth pair
		 是一端连着协议栈,另外一端彼此相连的,在veth设备的其中一端输入数据,这些数据就会在另外一端原样不变的流出。

		 	由于两个容器之间采用veth通信不需要反复经过多次协议栈,这让veth比tap/tun有更好的性能,也让veth pair的实现变得十分简单,内核中只用
		 几十行代码实现一个数据复制函数就完成了veth的主体功能。veth以模拟网卡直连的方式很好的解决了两个容器之间的通信问题,然后对多个容器之间的
		 通信,如果仍然单纯只用veth pair的话,事情就会变得非常麻烦,让每个容器都为与它通信的其他容器建立一对专用的veth pair并不实际,这时候就
		 需要一台虚拟化的交换机来解决多容器之间的通信问题了。

		2.交换机:Linux Bridge
			Linux Bridge 便是Linux系统下的虚拟化交换机,虽然它以"网桥(Bridge)"而不是交换机(Switch)为名,但是在使用过程中,你会发现Linux
		Bridge 的目的看起来像是交换机,功能像

你可能感兴趣的:(架构,后端)