k8s网络

Author Avatar
Sean Yu 12月 29, 2020
  • 在其它设备中阅读本文章

k8s网络模型

Kubernetes网络模型设计的一个基础原则是: 每个Pod都拥有一个独 立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管它们是否运行在同一个Node(宿主机)中,都要求它们 可以直接通过对方的IP进行访问。

于Kubernetes的网络模型假设Pod之间访问时使用的是对方Pod的实际地址,所以一个Pod内部的应用程序看到的自己的IP地址和端口与 集群内其他Pod看到的一样。它们都是Pod实际分配的IP地址。将IP地址 和端口在Pod内部和外部都保持一致,也就不需要使用NAT来进行地址转换了。

为每个Pod都设置一个IP地址的模型还有另外一层含义,那就是同 一个Pod内的不同容器会共享同一个网络命名空间,也就是同一个Linux 网络协议栈。这就意味着同一个Pod内的容器可以通过localhost来连接对 方的端口。这种关系和同一个VM内的进程之间的关系是一样的,看起 来Pod内容器之间的隔离性减小了,而且Pod内不同容器之间的端口是共享的。

Docker网络基础

网络命名空间

为了支持网络协议栈的多个实例,Linux在网络栈中引入了网络命 名空间,这些独立的协议栈被隔离到不同的命名空间中。处于不同命名 空间中的网络栈是完全隔离的,彼此之间无法通信。

image.png

Veth设备对

Veth设备对是为了在不同的网络命名空间之间通信,利用它可 以直接将两个网络命名空间连接起来。由于要连接两个网络命名空间, 所以V eth设备都是成对出现的,很像一对以太网卡,并且中间有一根直 连的网线。既然是一对网卡,那么我们将其中一端称为另一端的peer。

image.png

网桥

网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间的报文能够互相转发。网桥能够解析收发的报文,读取目 标MAC地址的信息,和自己记录的MAC表结合,来决策报文的转发目标网络接口。

网桥是在内核中虚拟出来的,可以将主机上真实的物理网卡(如eth0,eth1),或虚拟的网卡(tap0,tap1,vnet0,vnet1)桥接上来。桥接上来的网卡就相当于网桥上的端口。 端口收到的数据包都提交给这个虚拟的”网桥“,让其进行转发。

有点像一个虚拟的二层交换机。但是网桥可以通过自身IP和nat协议,把数据包送到网络协议栈的上层(网络层)。

iptables和Netfilter

在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂 接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一 些操作,例如过滤、修改、丢弃等。整个挂接点技术叫作Netfilter和 iptables。

Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中; 而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter 的各种规则表。二者互相配合来实现整个Linux网络协议栈中灵活的数 据包处理机制。

Netfilter可以挂接的规则点有5个

image.png

规则表Table

这些挂接点能挂接的规则也分不同的类型(也就是规则表 Table),我们可以在不同类型的Table中加入我们的规则。目前主要支 持的Table类型有:RAW、MANGLE、NAT和FILTER。

上述4个Table(规则链)的优先级是RAW最高,FILTER最低。

image.png

处理规则

每个规则的特性都分为以下几部分。

  • 表类型(准备干什么事情)。
  • 什么挂接点(什么时候起作用)。
  • 匹配的参数是什么(针对什么样的数据包)。
  • 匹配后有什么动作(匹配后具体的操作是什么)。

其实主要就是做防火墙,k8s中也会做原地址和目的地址的改变。

路由

Linux系统包含一个完整的路由功能。当IP层在处理数据发送或者 转发时,会使用路由表来决定发往哪里。

路由功能由IP层维护的一张路由表来实现。当主机收到数据报文 时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报 文时,IP层首先会检查报文的IP地址是否与主机自身的地址相同。如果 数据报文中的IP地址是主机自身的地址,那么报文将被发送到传输层相 应的协议中。如果报文中的IP地址不是主机自身的地址,并且主机配置 了路由功能,那么报文将被转发,否则,报文将被丢弃。

docker0

在bridge模式下,Docker Daemon第1次启动时会创建一个虚拟的网 桥,默认的名称是docker0,然后按照RPC1918的模型在私有网络空间中 给这个网桥分配一个子网。针对由Docker创建的每一个容器,都会创建 一个虚拟的以太网设备(V eth设备对),其中一端关联到网桥上,另一 端使用Linux的网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址段内给eth0接口分配一个IP地址。

image.png

Kubernetes的网络实现

容器到容器的通信

同一个Pod内的容器可以用localhost地址访问彼此的端口。

image.png

Pod之间的通信

同一个Node内Pod之间的通信

image.png

不同Node上Pod之间的通信

image.png

cni

Flannel

先说overlay网络吧

lan

lan就是局域网,共享一个arp广播域

vlan

VLAN(Virtual LAN),翻译成中文是“虚拟局域网”。把物理的lan切分成两个不能在二层互通的广播域。

image.png

基于IP地址的VLAN

基于子网的VLAN,则是通过所连计算机的IP地址,来决定端口所属VLAN的。不像基于MAC地址的VLAN,即使计算机因为交换了网卡或是其他原因导致MAC地址改变,只要它的IP地址不变,就仍可以加入原先设定的VLAN。

因此,与基于MAC地址的VLAN相比,能够更为简便地改变网络结构。IP地址是OSI参照模型中第三层的信息,所以我们可以理解为基于子网的VLAN是一种在OSI的第三层设定访问链接的方法。

汇聚链接

汇聚链接(Trunk Link)指的是能够转发多个不同VLAN的通信的端口。

image.png

进行VLAN间通信时,即使通信双方都连接在同一台交换机上,也必须经过:“发送方——交换机——路由器——交换机——接收方”这样一个流程。

image.png

使用三层交换机进行VLAN间路由(VLAN内通信)

image.png

vlan具体看 https://zhuanlan.zhihu.com/p/35616289

overlay

Overlay 网络是建立在另一个计算机网络之上的虚拟网络,所以它不能独立出现,Overlay 底层依赖的网络就是 Underlay 网络,这两个概念也经常成对出现。

Underlay 网络是专门用来承载用户 IP 流量的基础架构层,它与 Overlay 网络之间的关系有点类似物理机和虚拟机。Underlay 网络和物理机都是真正存在的实体,它们分别对应着真实存在的网络设备和计算设备,而 Overlay 网络和虚拟机都是依托在下层实体使用软件虚拟出来的层级。

image.png

vxlan

本节摘自 https://cizixs.com/2017/09/25/vxlan-protocol-introduction/
https://draveness.me/whys-the-design-overlay-network/

VXLAN 全称是 Virtual eXtensible Local Area Network,虚拟可扩展的局域网。它是一种 overlay 技术,通过三层的网络来搭建虚拟的二层网络。

VXLAN 到底要解决哪些问题。

  • 虚拟化(虚拟机和容器)的兴起使得一个数据中心会有成千上万的机器需要通信,而传统的 VLAN 技术只能支持 4096 个网络上限,已经满足不了不断扩展的数据中心规模
  • 越来越多的数据中心(尤其是公有云服务)需要提供多租户的功能,不同用户之间需要独立地分配 ip 和 MAC 地址,如何保证这个功能的扩展性和正确性也是一个待解决的问题
  • 云计算业务对业务灵活性要求很高,虚拟机可能会大规模迁移,并保证网络一直可用,也就是大二层的概念。解决这个问题同时保证二层的广播域不会过分扩大,也是云计算网络的要求

vxlan 这类隧道网络的一个特点是对原有的网络架构影响小,原来的网络不需要做任何改动,在原来网络基础上架设一层新的网络。

vxlan 自然会引入一些新的概念,这部分就讲讲它们。下面这张图 是 vxlan 的工作模型,它创建在原来的 IP 网络(三层)上,只要是三层可达(能够通过 IP 互相通信)的网络就能部署 vxlan。在每个端点上都有一个 vtep 负责 vxlan 协议报文的封包和解包,也就是在虚拟报文上封装 vtep 通信的报文头部。物理网络上可以创建多个 vxlan 网络,这些 vxlan 网络可以认为是一个隧道,不同节点的虚拟机能够通过隧道直连。每个 vxlan 网络由唯一的 VNI 标识,不同的 vxlan 可以不相互影响。

image.png

  • VTEP(VXLAN Tunnel Endpoints):vxlan 网络的边缘设备,用来进行 vxlan 报文的处理(封包和解包)。vtep 可以是网络设备(比如交换机),也可以是一台机器(比如虚拟化集群中的宿主机)
  • VNI(VXLAN Network Identifier):VNI 是每个 vxlan 的标识,是个 24 位整数,一共有 2^24 = 16,777,216(一千多万),一般每个 VNI 对应一个租户,也就是说使用 vxlan 搭建的公有云可以理论上可以支撑千万级别的租户
  • Tunnel:隧道是一个逻辑上的概念,在 vxlan 模型中并没有具体的物理实体想对应。隧道可以看做是一种虚拟通道,vxlan 通信双方(图中的虚拟机)认为自己是在直接通信,并不知道底层网络的存在。从整体来说,每个 vxlan 网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道。

image.png

vxlan 网络通信过程

问题:vxlan 网络怎么感知彼此的存在并选择正确的路径传输报文?

先看看一个完整的 vxlan 报文需要哪些信息。

image.png

  • 内层报文:通信的虚拟机双方要么直接使用 IP 地址,要么通过 DNS 等方式已经获取了对方的 IP 地址,因此网络层地址已经知道。同一个网络的虚拟机需要通信,还需要知道对方虚拟机的 MAC 地址,vxlan 需要一个机制来实现传统网络 ARP 的功能
  • vxlan 头部:只需要知道 VNI,这一般是直接配置在 vtep 上的,要么是提前规划写死的,要么是根据内部报文自动生成的,也不需要担心
  • UDP 头部:最重要的是源地址和目的地址的端口,源地址端口是系统生成并管理的,目的端口也是写死的,比如 IANA 规定的 4789 端口,这部分也不需要担心
  • IP 头部:IP 头部关心的是 vtep 双方的 IP 地址,源地址可以很简单确定,目的地址是虚拟机所在地址宿主机 vtep 的 IP 地址,这个也需要由某种方式来确定
  • MAC 头部:如果 vtep 的 IP 地址确定了,MAC 地址可以通过经典的 ARP 方式来获取,毕竟 vtep 网络在同一个三层,经典网络架构那一套就能直接用了

总结一下,一个 vxlan 报文需要确定两个地址信息:目的虚拟机的 MAC 地址和目的 vtep 的 IP 地址,如果 VNI 也是动态感知的,那么 vtep 就需要一个三元组:

内部 MAC <--> VNI <--> VTEP IP

根据实现的不同,一般分为两种方式:多播和控制中心。多播的概念是同个 vxlan 网络的 vtep 加入到同一个多播网络,如果需要知道以上信息,就在组内发送多播来查询;控制中心的概念是在某个集中式的地方保存了所有虚拟机的上述信息,自动化告知 vtep 它需要的信息。

多播:

多播的概念和工作原理不是这里的重点,所以就不介绍了。简单来说,每个多播组对应一个多播 IP 地址,往这个多播 IP 地址发送的报文会发给多播组的所有主机。

为什么要使用多播?因为 vxlan 的底层网络是三层的,广播地址无法穿越三层网络,要给 vxlan 网络所有 vtep 发送报文只能通过多播。

下图是在多播模式下,vxlan 的报文工作流程,位于左下方的 机器 A 要通过 vxlan 网络发送报文给右下方的机器 B。

image.png

vtep 建立的时候会通过配置加入到多播组(具体做法取决于实现),图中的多播组 IP 地址是 239.1.1.1。

  • 机器 A 只知道对方的 IP 地址,不知道 MAC 地址,因此会发送 ARP 报文进行查询,内部的 ARP 报文很普通,目标地址为全 1 的广播地址
  • vtep 收到 ARP 报文,发现虚拟机目的 MAC 为广播地址,封装上 vxlan 协议头部之后(外层 IP 为多播组 IP,MAC 地址为多播组的 MAC 地址),发送给多播组 239.1.1.1,支持多播的底层网络设备(交换机和路由器)会把报文发送给组内所有的成员【这里会封装成一个udp的包在三层上传输】
  • vtep 接收到 vxlan 封装的 ARP 请求,去掉 vxlan 头部,并通过报文学习到发送方 <虚拟机 MAC - VNI - Vtep IP> 三元组保存起来,把原来的 ARP 报文广播给主机
  • 主机接收到 ARP 请求报文,如果 ARP 报文请求的是自己的 MAC 地址,就返回 ARP 应答
  • vtep-2 此时已经知道发送放的虚拟机和 vtep 信息,把 ARP 应答添加上 vxlan 头部(外部 IP 地址为 vtep-1 的 IP 地址,VNI 是原来报文的 VNI)之后通过单播发送出去
  • vtep-1 接收到报文,并学习到报文中的三元组,记录下来。然后 vtep 进行解包,知道内部的 IP 和 MAC 地址,并转发给目的虚拟机
  • 虚拟机拿到 ARP 应答报文,就知道了到目的虚拟机的 MAC 地址

在数据包的传输过程中,通信的双方都不知道底层网络做的这些转换,它们认为两者可以通过二层的网络互相访问,但是实际上经过了三层 IP 网络的中转,通过 VTEP 之间建立的隧道实现了连通。

在这个过程中,只有一次多播,因为 vtep 有自动学习的能力,后续的报文都是通过单播直接发送的。可以看到,多播报文非常浪费,每次的多播其实只有一个报文是有效的,如果某个多播组的 vtep 数量很多,这个浪费是非常大的。但是多播组也有它的实现起来比较简单,不需要中心化的控制,只有底层网络支持多播,只有配置好多播组就能自动发现了。

单播报文的发送过程就是上述应答报文的逻辑,应该也非常容易理解了。还有一种通信方式,那就是不同 VNI 网络之间的通信,这个需要用到 vxlan 网关(可以是物理网络设备,也可以是软件),它接收到一个 vxlan 网络报文之后解压,根据特定的逻辑添加上另外一个 vxlan 头部转发出去。

因为并不是所有的网络设备都支持多播,再加上多播方式带来的报文浪费,在实际生产中这种方式很少用到。

分布式控制中心

从多播的流程可以看出来,其实 vtep 发送报文最关键的就是知道对方虚拟机的 MAC 地址和虚拟机所在主机的 vtep IP 地址。如果能够事先知道这两个信息,直接告诉 vtep,那么就不需要多播了。

在虚拟机和容器的场景中,当虚拟机或者容器启动还没有进行网络通讯时,我们就可以知道它的 IP 和 MAC(可能是用某种方式获取,也有可能是事先控制这两个地址),分布式控制中心保存了这些信息。除此之外,控制中心还保存了每个 vxlan 网络有哪些 vtep,这些 vtep 的地址是多少。有了这些信息,vtep 就能发送报文时直接查询并添加头部,不需要多播去满网络地问了。

一般情况下,在每个 vtep 所在的节点都会有一个 agent,它会和控制中心通信,获取 vtep 需要的信息以某种方式告诉 vtep。具体的做法取决于具体的实现,每种实现可能会更新不同的信息给 vtep,比如 HER(Head End Replication)只是把多播组替换成多个单播报文,也就是把多播组所有的 VTEP IP 地址告诉 vtep,这样查询的时候不是发送多播,而是给组内每个 vtep 发送一个单播报文;有些实现只是告诉 vtep 目的虚拟机的 MAC 地址信息;有些实现告诉 MAC 地址对应的 vtep IP 地址。

此外,什么时候告诉 vtep 这些信息也是有区别的。一般有两种方式:常见的是一旦知道了虚拟机的三元组信息就告诉 vtep(即使某个 vtep 用不到这个信息,因为它管理的虚拟机不会和这个地址通信),一般这时候第一次通信还没有发生;另外一种方式是在第一次通信时,当 vtep 需要这些信息的时候以某种方式通知 agent,然后 agent 这时候才告诉 vtep 信息。

因为vxlan的存在,使得即使二层不互通的两个数据中心,也可以通过这种在三层设备上伪装成二层的通信,这样不同的数据中心就形成了一个大二层。大二层的存在使得即使虚拟机在不同的数据中心中迁移,也可以不改变IP地址。

Flannel

说了这么多终于到Flannel了,Flannel正式基于vxlan的overlay网络。

flannel会在每一个宿主机上运行名为flanneld代理,其负责为宿主机预先分配一个子网,并为Pod分配IP地址。Flannel使用Kubernetes或etcd来存储网络配置、分配的子网和主机公共IP等信息。数据包则通过VXLAN、UDP或host-gw这些类型的后端机制进行转发。

image.png

从图中可以看出,node内部仍然是docker0/cni0那一套,外部就是vxlan了,这种方式使不同的pod可以处于同一个二层网络下互通。flannel在这里就充当了vtep的角色。

calico

calico 是容器网络的又一种解决方案,和其他虚拟网络最大的不同是,它没有采用 overlay 网络做报文的转发,提供了纯 3 层的网络模型。三层通信模型表示每个容器都通过 IP 直接通信,中间通过路由转发找到对方。在这个过程中,容器所在的节点类似于传统的路由器,提供了路由查找的功能。

要想路由工作能够正常,每个虚拟路由器(容器所在的主机节点)必须有某种方法知道整个集群的路由信息,calico 采用的是 BGP 路由协议,全称是 Border Gateway Protocol。

calico 把主机作为容器的默认网关来使用,所有的报文发到主机(通过veth pair),然后主机根据路由表进行转发。和经典的网络架构不同的是,calico 并没有给默认网络配置一个 IP 地址(这样每个网络都会额外消耗一个 IP 资源,而且主机上也会增加对应的 IP 地址和路由信息),而是通过 arp proxy 和修改容器路由表来实现。

+--------------------+              +--------------------+ 
|   +------------+   |              |   +------------+   | 
|   |            |   |              |   |            |   | 
|   |    ConA    |   |              |   |    ConB    |   | 
|   |            |   |              |   |            |   | 
|   +-----+------+   |              |   +-----+------+   | 
|         |veth      |              |         |veth      | 
|       wl-A         |              |       wl-B         | 
|         |          |              |         |          |
+-------node-A-------+              +-------node-B-------+ 
        |    |                               |    |
        |    | type1.  in the same lan       |    |
        |    +-------------------------------+    |
        |                                         |
        |      type2. in different network        |
        |             +-------------+             |
        |             |             |             |
        +-------------+   Routers   |-------------+
                      |             |
                      +-------------+

从ConA中发送给ConB的报文被nodeA的wl-A接收,根据nodeA上的路由规则,经过各种iptables规则后,转发到nodeB。

如果nodeA和nodeB在同一个二层网段,下一条地址直接就是node-B,经过二层交换机即可到达。
如果nodeA和nodeB在不同的网段,报文被路由到下一跳,经过三层交换或路由器,一步步跳转到node-B。

bgp协议异常繁琐,先略过吧,找时间补上 https://blog.51cto.com/xuanbo/465596

同时calico也提供ipip隧道的模式,也就是说,pod发出的ping包,经过veth找到calico agent后,会经过calico的tunl0会根据etcd提供的元数据,再封装上一层IP报文,发向对端的calico,在进行解包。 参照 https://blog.csdn.net/u010771890/article/details/103224004

https://www.cnblogs.com/goldsunshine/p/10701242.html

貌似因为calico没有选择overlay网络,只是在三层通过IP隧道或者路由打通网络,因此不能做到虚拟机迁移无痛不改ip。但是大多数k8s的pod都是动态改ip的,etcd中心化的存储帮助calico/flannel找到对端,因此这里二层可达的问题显现的不明显。

同时calico因为需要维护路由表并强关联iptables,因此对ip的维护消耗也比较大,可能出现大量IP冲击到路由表里,导致路由效率降低等问题。

参考 https://www.talkwithtrend.com/Question/433909