kubernetes网络原理

一、Docker网络原理

1、网络命名空间

为了支持网络协议栈的多个实例,Linux在网络栈中引入了网络命名空间,这些独立的协议栈被隔离到不同的命名空间中。处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信。通过对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。Docker正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。

在Linux的网络命名空间中可以有自己独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能。

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

2、Veth设备对

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

3、网桥

Linux可以支持多个不同的网络,它们之间能够相互通信,如何将这些网络连接起来并实现各网络中主机的相互通信呢?可以用网桥。网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间的报文能够相互转发。网桥能够解析收发的报文,读取目标MAC地址的信息,将其与自己记录的MAC表结合,来决策报文的转发目标网络接口。为了实现这些功能,网桥会学习源MAC地址(二层网桥转发的依据就是MAC地址)。在转发报文时,网桥只需向特定的网口进行转发,来避免不必要的网络交互。如果它遇到一个自己从未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,将报文广播给所有的网络接口(报文来源的网络接口除外)。

在实际的网络中,网络拓扑不可能永久不变。设备如果被移动到另一个端口上,却没有发送任何数据,网桥设备就无法感知这个变化,网桥还是向原来的端口转发数据包,在这种情况下数据会丢失。所以网桥还要对学习到的MAC地址表加上超时时间(默认为5min)。如果网桥收到了对应端口MAC地址回发的包,则重置超时时间,否则过了超时时间,就认为设备已经不在那个端口上了,它会重新广播发送。

在Linux的内部网络栈里实现的网桥设备,作用和上面的描述相同。Linux主机过去一般只有一个网卡,现在多网卡的机器越来越多,而且有很多虚拟设备存在,所以Linux网桥提供了在这些设备之间相互转发数据的二层设备。

Linux内核支持网口的桥接(目前只支持以太网接口)。但是与单纯的交换机不同,交换机只是一个二层设备,对于接收到的报文,要么转发,要么丢弃。运行着Linux内核的机器本身就是一台主机,有可能是网络报文的目的地,其收到的报文除了转发和丢弃,还可能被送到网络协议栈的上层(网络层),从而被自己(这台主机本身的协议栈)消化,所以我们既可以把网桥看作一个二层设备,也可以把它看作一个三层设备。

4、iptables和Netfilter

我们知道,Linux网络协议栈非常高效,同时比较复杂。如果我们希望在数据的处理。过程中对关心的数据进行一些操作,则该怎么做呢?Linux提供了一套机制来为用户实现自定义的数据包处理。

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

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

5、路由

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

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

路由表中的数据一般是以条目形式存在的。一个典型的路由表条目通常包含以下主要的条目项。

(1)目的IP地址:此字段表示目标的IP地址。这个IP地址可以是某主机的地址,也可以是一个网络地址。如果这个条目包含的是一个主机地址,那么它的主机ID将被标记为非零;如果这个条目包含的是一个网络地址,那么它的主机ID将被标记为零。

(2)下一个路由器的IP地址:这里采用“下一个”的说法,是因为下一个路由器并不总是最终的目的路由器,它很可能是一个中间路由器。条目给出的下一个路由器的地址用来转发在相应接口接收到的IP数据报文。

(3)标志:这个字段提供了另一组重要信息,例如,目的IP地址是一个主机地址还是一个网络地址。此外,从标志中可以得知下一个路由器是一个真实路由器还是一个直接相连的接口。

(4)网络接口规范:为一些数据报文的网络接口规范,该规范将与报文一起被转发。

在通过路由表转发时,如果任何条目的第1个字段完全匹配目的IP地址(主机)或部分匹配条目的IP地址(网络),那么它将指示下一个路由器的IP地址。这是一个重要的信息,因为这些信息直接告诉主机(具备路由功能的)数据包应该被转发到哪个路由器。而条目中的所有其他字段将提供更多的辅助信息来为路由转发做决定。

如果没有找到一个完全匹配的IP,就接着搜索相匹配的网络ID。如果找到,那么该数据报文会被转发到指定的路由器上。可以看出,网络上的所有主机都通过这个路由表中的单个(这个)条目进行管理。

如果上述两个条件都不匹配,那么该数据报文将被转发到一个默认的路由器上。

如果上述步骤都失败,默认的路由器也不存在,那么该数据报文最终无法被转发。任何无法投递的数据报文都将产生一个ICMP主机不可达或ICMP网络不可达的错误,并将此错误返回给生成此数据报文的应用程序。

6、Docker网络实现

标准的Docker支持以下4类网络模式。

◎ host模式:使用--net=host指定。

◎ container模式:使用--net=container:NAME_or_ID指定。

◎ none模式:使用--net=none指定。

◎ bridge模式:使用--net=bridge指定,为默认设置。

在Kubernetes管理模式下通常只会使用bridge模式,所以只介绍Docker在bridge模式下是如何支持网络的。

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

 

其中ip1是网桥的IP地址,Docker Daemon会在几个备选地址段里给它选一个地址,通常是以172开头的一个地址,这个地址和主机的IP地址是不重叠的。ip2是Docker在启动容器时在这个地址段选择的一个没有使用的IP地址,它被分配给容器,相应的MAC地址也根据这个IP地址,在02:42:ac:11:00:00和02:42:ac:11:ff:ff的范围内生成,这样做可以确保不会有ARP冲突。

启动后,Docker还将Veth设备对的名称映射到eth0网络接口。ip3就是主机的网卡地址。

在一般情况下,ip1、ip2和ip3是不同的IP段,所以在默认不做任何特殊配置的情况下,在外部是看不到ip1和ip2的。

这样做的结果就是,在同一台机器内的容器之间可以相互通信,不同主机上的容器不能相互通信,实际上它们甚至有可能在相同的网络地址范围内(不同主机上的docker0的地址段可能是一样的)。

为了让它们跨节点相互通信,就必须在主机的地址上分配端口,然后通过这个端口将网络流量路由或代理到目标容器上。这样做显然意味着一定要在容器之间小心谨慎地协调好端口的分配情况,或者使用动态端口的分配技术。在不同应用之间协调好端口分配情况是十分困难的事情,特别是集群水平扩展时。而动态端口分配也会大大增加复杂度,例如:每个应用程序都只能将端口看作一个符号(因为是动态分配的,所以无法提前设置)。而且API Server要在分配完后,将动态端口插入配置的合适位置,服务也必须能相互找到对方等。这些都是Docker的网络模型在跨主机访问时面临的问题。

二、Kubernetes的网络实现

在实际的业务场景中,业务组件之间的关系十分复杂,特别是随着微服务理念逐步深入人心,应用部署的粒度更加细小和灵活。为了支持业务应用组件的通信,Kubernetes网络的设计主要致力于解决以下问题。

(1)容器到容器之间的直接通信。

(2)抽象的Pod到Pod之间的通信。

(3)Pod到Service之间的通信。

(4)集群内部与外部组件之间的通信。

1、容器到容器的通信

同一个Pod内的容器(Pod内的容器是不会跨宿主机的)共享同一个网络命名空间,共享同一个Linux协议栈。所以对于网络的各类操作,就和它们在同一台机器上一样,它们甚至可以用localhost地址访问彼此的端口。

 

2、Pod之间的通信

  • 同一个Node上Pod之间的通信

  •  

    可以看出,Pod1和Pod2都是通过Veth连接到同一个docker0网桥的,它们的IP地址IP1、IP2都是从docker0的网段上动态获取的,和网桥本身的IP3属于同一个网段。

    另外,在Pod1、Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会被默认发送到docker0网桥上,由docker0网桥直接中转。

    综上所述,由于它们都关联在同一个docker0网桥上,地址段相同,所以它们之间是能直接通信的。

  • 不同Node上Pod之间的通信

    Pod的地址是与docker0在同一个网段的,我们知道docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通信,就必须想办法通过主机的这个IP地址进行寻址和通信。

    另一方面,这些动态分配且藏在docker0后的“私有”IP地址也是可以找到的。Kubernetes会记录所有正在运行的Pod的IP分配信息,并将这些信息保存在etcd中(作为Service的Endpoint)。这些私有IP信息对于Pod到Pod的通信也是十分重要的,因为我们的网络模型要求Pod到Pod使用私有IP进行通信。所以首先要知道这些IP是什么。

    之前提到,Kubernetes的网络对Pod的地址是平面的和直达的,所以这些Pod的IP规划也很重要,不能有冲突。只要没有冲突,我们就可以想办法在整个Kubernetes的集群中找到它。

    综上所述,要想支持不同Node上Pod之间的通信,就要满足两个条件:

    (1)在整个Kubernetes集群中对Pod的IP分配进行规划,不能有冲突;

    (2)找到一种办法,将Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以相互访问。

    根据条件1的要求,我们需要在部署Kubernetes时对docker0的IP地址进行规划,保证每个Node上的docker0地址都没有冲突。我们可以在规划后手工配置到每个Node上,或者做一个分配规则,由安装的程序自己去分配占用。例如,Kubernetes的网络增强开源软件Flannel就能够管理资源池的分配。

    根据条件2的要求,Pod中的数据在发出时,需要有一个机制能够知道对方Pod的IP地址挂在哪个具体的Node上。也就是说,先要找到Node对应宿主机的IP地址,将数据发送到这个宿主机的网卡,然后在宿主机上将相应的数据转发到具体的docker0上。一旦数据到达宿主机Node,那个Node内部的docker0便知道如何将数据发送到Pod了。如图7.10所示,IP1对应的是Pod1,IP2对应的是Pod2,Pod1在访问Pod2时,首先要将数据从源Node的eth0发送出去,找到并到达Node2的eth0,即先是从IP3到IP4的传送,之后才是从IP4到IP2的传送。

  •  

三、总结

  1. Kubernetes在创建Pod时会首先创建出一个pause的基础容器,后续容器的网络模式都跟随这个pause。所以有了同一个Pod内部所有容器共享一个网络空间。

  2. 同一个Pod内部通信类似于之前的同一台物理上的服务之间通信一样。可以通过lo回环网卡进行通信。同一个Pod内的容器不能有端口冲突。

  3. 同一个Node内的Pod之间的通信类似于局域网中内通信。数据包里填上源IP地址、源Mac地址、目标Ip地址发到局域网询问目标Mac地址。

  4. 不同Node之间的Pod通信类似于局域网间的通信。数据发往默认路由,进行路由。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值