wwqdrh

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

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

VXLAN。和三层外面再套三层的GRE不同,VXLAN则是从二层外面就套了一个VXLAN的头,这里面包含的VXLAN ID为24位,也够用了。在VXLAN头外面还封装了UDP、IP,以及外层的MAC头。VXLAN作为扩展性协议,也需要一个地方对VXLAN的包进行封装和解封装,实现这个功能的点称为VTEP(VXLAN Tunnel Endpoint)。VTEP相当于虚拟机网络的管家。每台物理机上都可以有一个VTEP。每个虚拟机启动的时候,都需要向这个VTEP管家注册,每个VTEP都知道自己上面注册了多少个虚拟机。当虚拟机要跨VTEP进行通信的时候,需要通过VTEP代理进行,由VTEP进行包的封装和解封装。和GRE端到端的隧道不同,VXLAN不是点对点的,而是支持通过组播的来定位目标机器的,而非一定是这一端发出,另一端接收。

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

虚拟化

虚拟化就是用来解决单机不灵活,资源利用率不高的问题

qemu-kvm,能让你在一台巨大的物理机里面,掏出一台台小的机器。这套软件就能解决上面的问题:一点就能创建,一点就能销毁。你想要多大就有多大,每次创建的系统还都是新的。软件模拟硬件,CPU、内存、网络、硬盘,使得虚拟机感觉自己在使用独立的设备,但是真正使用的时候,当然还是使用物理的设备。

虚拟网卡

对于qemu-kvm来说,这是通过Linux上的一种TUN/TAP技术来实现的。可以像其他应用打开文件一样,打开一个称为TUN/TAP的Char Dev(字符设备文件)。

  • 打开了这个字符设备文件之后,在物理机上就能看到一张虚拟TAP网卡。
  • 虚拟机中的所有应用,所有的网络包都往这里发。网络包会到虚拟化软件这里。它会将网络包转换成为文件流,写入字符设备,就像写一个文件一样。
  • 内核中TUN/TAP字符设备驱动会收到这个写入的文件流,交给TUN/TAP的虚拟网卡驱动。
  • 这个驱动将文件流再次转成网络包,交给TCP/IP协议栈,最终从虚拟TAP网卡发出来,成为标准的网络包。

需要注意的问题

共享和互通:尽管每个虚拟机都会有一个或者多个虚拟网卡,但是物理机上可能只有有限的网卡。那这么多虚拟网卡如何共享同一个出口?互通分两个方面,一个是如果同一台机器上的两个虚拟机,属于同一个用户的话,这两个如何相互通信?另一个是如果不同物理机上的两个虚拟机,属于同一个用户的话,这两个如何相互通信?

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

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

如果需要访问外部网络,在桥接模式下,物理网卡也连接到虚拟交换机上。这时虚拟机和本机的ip在同一个网桥下。在每台机器上都创建网桥br0,虚拟机的网卡都连到br0上,物理网卡也连到br0上,所有的br0都通过物理网卡出来连接到物理交换机上。(在这种方式下,不但解决了同一台机器的互通问题,也解决了跨物理机的互通问题,因为都在一个二层网络里面,彼此用相同的网段访问就可以了。但是当规模很大的时候,会存在问题。最大的问题是广播。一个数据中心的物理机已经很多了,广播已经非常严重,需要通过VLAN进行划分。如果使用了虚拟机,假设一台物理机里面创建10台虚拟机,全部在一个二层网络里面,那广播就会很严重,所以除非是你的桌面虚拟机或者数据中心规模非常小,才可以使用这种相对简单的方式。)

要访问外部网络还可以使用NAT,虚拟机的网络是虚拟机的,物理机的网络是物理机的,两个不相同。虚拟机要想访问物理机的时候,需要将地址NAT成为物理机的地址。如果使用NAT模式,会创建一个DHCP服务,为虚拟机动态分配ip地址(桥接模式不需要是因为桥接将网络打平了,虚拟机的IP地址应该由物理网络的DHCP服务器分配。)。虚拟机要想访问互联网,需要通过br0连到路由器上,然后通过路由器将请求NAT成为物理网络的地址,转发到物理网络。

隔离:分两个方面,一个是安全隔离,两个虚拟机可能属于两个用户,那怎么保证一个用户的数据不被另一个用户窃听?一个是流量隔离,两个虚拟机,如果有一个疯狂下片,会不会导致另外一个上不了网?

brctl创建的网桥也是支持VLAN功能的,可以设置两个虚拟机的tag,这样在这个虚拟网桥上,两个虚拟机是不互通的。

跨主机的隔离,vconfig,可以基于物理网卡eth0创建带VLAN的虚拟网卡,所有从这个虚拟网卡出去的包,都带这个VLAN,如果这样,跨物理机的互通和隔离就可以通过这个网卡来实现。首先为每个用户分配不同的VLAN(VLAN ID只有4096个,另外配置也不够灵活),例如有一个用户VLAN 10,一个用户VLAN 20。在一台物理机上,基于物理网卡,为每个用户用vconfig创建一个带VLAN的网卡。不同的用户使用不同的虚拟网桥,带VLAN的虚拟网卡也连接到虚拟网桥上。这样是否能保证两个用户的隔离性呢?不同的用户由于网桥不通,不能相互通信,一旦出了网桥,由于VLAN不同,也不会将包转发到另一个网桥上。另外,出了物理机,也是带着VLAN ID的。只要物理交换机也是支持VLAN的,到达另一台物理机的时候,VLAN ID依然在,它只会将包转发给相同VLAN的网卡和网桥,所以跨物理机,不同的VLAN也不会相互通信。

灵活:虚拟机和物理不同,会经常创建、删除,从一个机器漂移到另一台机器,有的互通、有的不通等等,灵活性比物理网络要好得多,需要能够灵活配置。

通过软件定义网络SDN提高灵活性

软件定义网络(SDN)

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

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

由于OpenvSwitch本身就是支持VLAN的,所有的虚拟机都可以放在一个网桥br0上,通过不同的用户配置不同的tag,就能够实现隔离。例如用户A的虚拟机都在br0上,用户B的虚拟机都在br1上,有了OpenvSwitch,就可以都放在br0上,只是设置了不同的tag。

也可以将物理网络和虚拟网络放在不同的网桥上,进行解耦

容器化

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

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

容器里面有张网卡,容器外有张网卡,容器外的网卡连到docker0网桥,通过这个网桥,容器直接实现相互访问。

在Linux下,可以创建一对veth pair的网卡,从一边发送包,另一边就能收到。用于替代虚拟化中的虚拟网卡

Docker默认使用NAT模式。NAT模式分为SNAT和DNAT,如果是容器内部访问外部,就需要通过SNAT。

flannel

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

基于NAT的容器网络模型在微服务架构下有两个问题,一个是IP重叠,一个是端口冲突,需要通过Overlay网络的机制保持跨节点的连通性。

Flannel是跨节点容器网络方案之一,它提供的Overlay方案主要有两种方式,一种是UDP在用户态封装,一种是VXLAN在内核态封装,而VXLAN的性能更好一些。

Flannel有一个非常好的模式,就是给不同的物理机设置不同网段,这一点和虚拟机的Overlay的模式完全不一样。

网络模型需要扁平

Flannel使用UDP实现Overlay网络的方案。

在每台物理机上,都会跑一个flanneld进程,这个进程打开一个/dev/net/tun字符设备的时候,就出现了这个网卡。物理机A上的flanneld会将网络包封装在UDP包里面,然后外层加上物理机A和物理机B的IP地址,发送给物理机B上的flanneld。(为什么是UDP呢?因为不想在flanneld之间建立两两连接,而UDP没有连接的概念,任何一台机器都能发给另一台。)。物理机B上的flanneld收到包之后,解开UDP的包,将里面的网络包拿出来,从物理机B的flannel.1网卡发出去。在物理机B上,有路由规则类似172.17.9.0/24 dev docker0 proto kernel scope link src 172.17.9.1, 将包发给docker0,docker0将包转给容器B。通信成功。

calico

Calico网络的大概思路,即不走Overlay网络,不引入另外的网络性能损耗,而是将转发全部用三层网络的路由转发来实现。首先,如果全部走三层的路由规则,没必要每台机器都用一个docker0,从而浪费了一个IP地址,而是可以直接用路由转发到veth pair在物理机这一端的网卡。同样,在容器内,路由规则也可以这样设定:把容器外面的veth pair网卡算作默认网关,下一跳就是外面的物理机。

  • 路由配置组件Felix: 每台物理机上有一个agent,当创建和删除容器的时候,自动配置或者删除一条指向这个容器的路由。这个agent在Calico中称为Felix
  • 路由广播组件BGP Speaker: 每个Node上运行一个软件BIRD,作为BGP的客户端,或者叫作BGP Speaker,将“如何到达我这个Node,访问我这个Node上的容器”的路由信息广播出去。所有Node上的BGP Speaker 都互相建立连接,就形成了全互连的情况,这样每当路由有所变化的时候,所有节点就都能够收到了。
  • BGP Route Reflector: 用BIRD实现的。有了它,BGP Speaker就不用全互连了,而是都直连它,它负责将全网的路由信息广播出去。(为了避免瓶颈,肯定不能让一个BGP Router Reflector管理所有的路由分发,而是应该有多个BGP Router Reflector,每个BGP Router Reflector管一部分)
  • 安全配置组件: 灵活配置网络策略Network Policy,可以灵活配置两个容器通或者不通(也是基于iptables)

IPIP模式: 为了解决跨网段(由于在不同网段,中间有多个路由器),让物理机之间知道如何走,在物理机A和物理机B之间打一个隧道,这个隧道有两个端点,在端点上进行封装,将容器的IP作为乘客协议放在隧道里面,而物理主机的IP放在外面作为承载协议。这样不管外层的IP通过传统的物理网络,走多少跳到达目标物理机,从隧道两端看起来,物理机A的下一跳就是物理机B,这样前面的逻辑才能成立。

这和原来模式的区别在于,下一跳不再是同一个网段的物理机B了,IP为192.168.200.101,并且不是从eth0跳,而是建立一个隧道的端点tun0,从这里才是下一跳。