目录

 

参考资料

一、什么是容器网络

1.1 网络命名空间

1.2 veth设备对

二、K8S中节点间通信

2.1 不同Node中的Pod之间通信

三、xx实现概述


 

参考资料

K8s网络模型 - 腾讯云开发者社区-腾讯云

扁平-K8s网络模型漫谈 - 腾讯云开发者社区-腾讯云

036.集群网络-K8S网络模型及Linux基础网络 - 腾讯云开发者社区-腾讯云

 

一、什么是容器网络

下面介绍一些会用到的基础概念

1.1 网络命名空间

为了支持网络协议栈的多个实例,Linux在网络栈中引入了网络命名空间,这些独立的协议栈被隔离到不同的命名空间中。

处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信。通过对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。Docker正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。在Linux的网络命名空间中可以有自己独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能。

为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。进程创建的套接字必须属于某个命名空间,套接字的操作也必须在命名空间中进行。同样,网络设备也必须属于某个命名空间。因为网络设备属于公共资源,所以可以通过修改属性实现在命名空间之间移动。

  • 网络命名空间的实现

Linux的网络协议栈相对复杂,为了支持独立的协议栈,相关的这些全局变量都必须被修改为协议栈私有。最好的办法就是让这些全局变量成为一个Net Namespace变量的成员,然后为协议栈的函数调用加入一个Namespace参数。这就是Linux实现网络命名空间的核心。

同时,为了保证对已经开发的应用程序及内核代码的兼容性,内核代码隐式地使用了命名空间中的变量。程序如果没有对命名空间有特殊需求,就不需要编写额外的代码,网络命名空间对应用程序而言是透明的。

在建立了新的网络命名空间,并将某个进程关联到这个网络命名空间后,就出现了类似于下图所示的内核数据结构,所有网站栈变量都被放入了网络命名空间的数据结构中。这个网络命名空间是其进程组私有的,和其他进程组不冲突。

16c2d6347e75a0404dd4ca1942c45ff1.png

在新生成的私有命名空间中只有回环设备(名为“lo”且是停止状态),其他设备默认都不存在,若需要其他设备,则要手工建立。

从网络角度,每个namespace提供了一份独立的网络协议栈(网络设备接口、IPV4、IPV6、IP路由、防火墙规则、sockets等)。一个设备(Linux Device)只能位于一个namespace中,不同namespace中的设备可以利用veth pair进行桥接。

cfbd88efeceefed89b68c3ed30e5238a.png

实际在操作系统中,ifconfig查询网卡时都能看到环回地址127.0.0.1,只要操作系统在运行,这个地址就一直是通的,网卡状态为UP。

5b050094afc540138713a5afa8c15077.png

 

1.2 veth设备对

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

在Veth设备的一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作。Veth设备对的示意图如下:

a33f7f9348477eb00b8d8bea8f0df3f5.png

 

查看没有多余配置的操作系统中的veth设备对,结果如下

895e4cc4928f449780c7361bf5ee3006.png

 

1.3 网桥

Linux可以支持多个不同的网络,它们之间能够相互通信,可通过网桥将这些网络连接起来并实现各网络中主机的相互通信。

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

为了实现转发功能,网桥学习源MAC地址(二层网桥转发的依据就是MAC地址)。在转发报文时,网桥只需要向特定的网口进行转发,来避免不必要的网络交互。如果接受到未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,就将报文广播给所有的网络接口(报文来源的网络接口除外)。

  • Linux网桥的实现

Linux内核是通过一个虚拟的网桥设备(Net Device)来实现桥接的。这个虚拟设备可以绑定若干个以太网接口设备,从而将它们桥接起来。如下图所示,这种Net Device网桥和普通的设备不同,最明显的一个特性是还可以有一个IP地址。

b2e84d99156eb8e39ddcdb5af8384b1b.png

如上图所示,网桥设备br0绑定了eth0和eth1。对于网络协议栈的上层来说,只看得到br0就行。因为桥接是在数据链路层实现的,上层不需要关心桥接的细节,所以协议栈上层需要发送的报文被送到br0,网桥设备的处理代码判断报文该被转发到eth0还是eth1,或者两者皆转发;反之,从eth0或从eth1接收到的报文被提交给网桥的处理代码,在这里会判断报文应该被转发、丢弃还是被提交到协议栈上层。而有时eth0、eth1也可能会作为报文的源地址或目的地址,直接参与报文的发送与接收,从而绕过网桥。

 为网桥增加网口,在Linux中,一个网口其实就是一个物理网卡。

80c42348de4238f0fee75ed5819e0d27.png

如下的操作系统,配置了1个virbr0的网桥设备。 8ad522d0624d42d8ae720f68bd41ea61.png

 

1.4 iptables和Netfilter

Linux提供了一套机制来为用户实现自定义的数据包处理过程。在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一些操作,例如过滤、修改、丢弃等。

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

  • iptables规则

iptables内置三张表:filter、nat和mangle。

  • filter:实现防火墙功能;
  • nat:实现NAT功能;
  • mangle:实现流量整形。

iptables三张表,或三条链(chain),也是三种策略(policy)。这类策略,由不同规则(rule)串联而成。

提示:更多iptables知识参考:https://wiki.archlinux.org/index.php/Iptables_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)。

规则示例:iptables - A INPUT - i ethO -p icmp -j ACCEPT

解释:允许所有从 ethO 端口进入且协议是 ICMP 的报文可以接受(可以进入下一个流程)。

规则的本质是对进入的IP报文进行说明,如:符合什么样的条件(比如本条命令的条件是“允许所有从eth0端口进入且协议是 ICPM 的报文”)、做什么样的处理(比如本条命令的处理是“接受”,可以进入下一个流程)。

 

实际查看环境上默认的iptables规则,能够看到三张表/链里面的默认配置,即类似于ACL和Route,分别负责消息报文进入、转发和发出。 

f1213b11364447fe80fb5bf3a7765f2a.png

iptables可以定义多种策略/规则,所有规则最终会传递到内核netfilter模块,netfilter模块会根据这些规则做相应的处理。

netfilter的处理方式是:从报文进入本机( linux host 或 vm)的那一刻起,到报文离开本机的那一刻止,中间这段时间(或者是发自本机的报文,从报文准备发送的那一刻,到报文离开本机的那一刻止,中间这段时间),netfilter 会在ABCDE时刻点插入处理模块,这些处理模块根据相应的策略/规则对报文进行处理。

对于 nat 、 filter 、 mangle 三张表也可以这么理解:仅仅是为了达到不同的目的(功能)而实现的三个模块而已 。

Netfilter可以挂接的规则点有5个,如下图所示:

1d4135ac7feb41aa6caefc0724160bfe.png

在这些时刻点中,iptables三张表(模块)并不是所有的时刻都全部进行处理。在同一个时刻点,也可以有多个模块进行处理,对于不同模块谁先处理,谁后处理,可参考如下顺序。

a7756d6405201beaa0975c8a1a7c7254.png

解释:

  1. PREROUTING:报文进入网络接口尚未进入路由之前的时刻;
  2. INPUT:路由判断是本机接收的报文,准备从内核空间进入到用户空间的时刻;
  3. FORWARD:路由判断不是本机接收的报文,需要路由转发,路由转发的那个时刻;
  4. OUTPUT:本机报文需要发出去 经过路由判断选择好端口以后,准备发送的那一刻 ;
  5. POSTROUTING:FORWARD/OUTPUT 已经完成,报文即将出网络接口的那一刻 。

三张表所能对应的时刻点如下:

表名

时刻点

mangle

PREROUTING, INPUT, FORWARD, OUTPUT

nat

PREROUTING, OUTPUT, POSTROUTING

filter

INPUT, FORWARD, OUTPUT

 

1.5 路由

Linux系统包含一个完整的路由功能。当IP层在处理数据发送或者转发时,会使用路由表来决定发往哪里。在通常情况下,如果主机与目的主机直接相连,那么主机可以直接发送IP报文到目的主机,这个过程比较简单。例如,通过点对点的链接或网络共享。如果主机与目的主机没有直接相连,那么主机会将IP报文发送给默认的路由器,然后由路由器来决定往哪里发送IP报文。

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

  • 路由表的创建

Linux的路由表至少包括两个表(当启用策略路由时,还会有其他表):一个是LOCAL,另一个是MAIN。

在LOCAL表中会包含所有的本地设备地址。LOCAL路由表是在配置网络设备地址时自动创建的。LOCAL表用于供Linux协议栈识别本地地址,以及进行本地各个不同网络接口之间的数据转发。

[root@k8smaster01 ~]# ip route show table local type local #查看LOCAL表的内容

14d20763cbab42a086a2244b21fcdcdc.png

 MAIN表用于各类网络IP地址的转发。它的建立既可以使用静态配置生成,也可以使用动态路由发现协议生成。动态路由发现协议一般使用组播功能来通过发送路由发现数据,动态地交换和获取网络的路由信息,并更新到路由表中。

Linux下支持路由发现协议的开源软件有许多,常用的有Quagga、Zebra等。

 手动查看操作系统的路由表配置。

86034075eb9942e69d79079954009aa9.png

 

二、K8S中节点间通信

2.1 不同Node中的Pod之间通信

在这里引用K8s网络模型 - 腾讯云开发者社区-腾讯云,清晰地讲述了K8S不同节点上的pod如何通信,从而引入了一个重要的实现问题,那就节点间的CNI都是如何实现的?

k8s网络模型需要每个pod必须通过ip地址可以进行访问,每个pod的ip地址总是对网络中的其他pod可见,并且每个pod看待自己的ip与别的pod看待的是一样的(虽然他没规定如何实现),下面我们看不同Node间Pod如何交互

k8s中每个集群中的每个Node都会被分配了一个CIDR块(无类别域间路由选择,把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给该Node上的Pod分配IP地址。(保证pod的ip不会冲突) 另外还需要把pod的ip与所在的nodeip关联起来()

842528ad2b6f62077ce5f851381bbd70.gif

  • 如上图Node1(vm1)上的Pod1与Node2(vm2)上Pod4之间进行交互。
  • 首先pod1通过自己的以太网设备eth0把数据包发送到关联到root命名空间的veth0上,然后数据包被Node1上的网桥设备cbr0接受到,网桥查找转发表发现找不到pod4的Mac地址,则会把包转发到默认路由(root命名空间的eth0设备),然后数据包经过eth0就离开了Node1,被发送到网络。
  • 数据包到达Node2后,首先会被root命名空间的eth0设备,然后通过网桥cbr0把数据路由到虚拟设备veth1,最终数据表会被流转到与veth1配对的另外一端(pod4的eth0)

每个Node都知道如何把数据包转发到其内部运行的Pod,当一个数据包到达Node后,其内部数据流就和Node内Pod之间的流转类似了。

对于如何来配置网络,k8s在网络这块自身并没有实现网络规划的具体逻辑,而是制定了一套CNI(Container Network Interface)接口规范,开放给社区来实现。

例如AWS,亚马逊为k8s维护了一个容器网络插件,使用CNI插件来让亚马逊VPC(虚拟私有云)环境中的Node与Node直接进行交互。

 

 

三、Flannel实现概述

 pod在不同主机的通信依赖于CNI插件,CoreOS的Flannel是k8s中实现CNI规范较为出名的一种实现。可以参考如下的一段描述进行理解。
overlay模式与bridge模式的区别就是路由转发给tun口,tun口是overlay隧道的入口,所有机器只需要一条路由表,而在两个主机的tun口之间以flannel为例,会借助分布式数据库记录容器IP与宿主机IP映射,而每个节点上会运行一个agent,监听tun口的封包和拆包,比如Node1的容器发给Node2的容器,当数据包到达Node1的tun口之后,flannel会查询Node2容器的宿主机IP,在tun处将192.168.1.101:8472封装在原始数据包之外(并将包的校验和为0),然后借着eth0物理网络到达Node2的eth0口,Node2的flannel监听到校验和为0的包知道这是一个overlay包将其拆包,看到内层IP根据路由发给cni再广播到容器

 

在Flannel的GitHub页面有如下的一张原理图,可参考文章Flannel入门介绍 - 腾讯云开发者社区-腾讯云

d3c5b15dcc7bd9b48a3734a3e975588c.png

 

四、Calico实现概述

 这里我们再以Calico为例的做简单了解,从Calico架构图中可以看到每个node节点的自身依然采用容器网络模式,Calico在每个节点都利用Linux 内核实现了一个高效的虚拟路由器vRouter来负责数据转发。每个虚拟路由器将路由信息广播到网络中,并添加路由转发规则。同时基于iptables还提供了丰富的网络策略,实现k8s的Network Policy策略,提供容器间网络可达性限制的功能。

简单理解就是通过在主机上启动虚拟路由器(calico node),将每个主机作为路由器使用实现互联互通的网络拓扑。

Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network,没有额外的封包解包,能够节约CPU运算,提高网络效率。

aaa0a39743c4c0ddc34c8d5d416054f3.png

 

五、OCP中的容器网络(待补充)

 

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐