Kubernetes 核心原理 之二

来源:互联网 发布:天涯论坛和知乎 编辑:程序博客网 时间:2024/06/08 02:39

kubernetes 网络模型

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

kubernetes的网络模型假设Pod之间访问时使用的是对方的Pod的实际地址,所以一个Pod内部的应用程序看到的自己的IP地址和端口号与集群内部内其他Pod看到的是一样,他们都是Pod实际分配的地址(从docker0分配的)。一个Pod一个IP的模型还有另外一层含义,那就是同一个Pod内的容器会共享一个网络命名空间,也就是同一个网络协议栈,这意味着同一个Pod里面的容器可以通过localhost来连接对方的端口。

IP-per-Pod模型是一个简单的兼容性好的模型。从该模型网络的端口分配、域名解析、服务发现、负载均衡、应用配置和迁移到呢个角度来看、Pod都能被当作一台独立的”虚拟机”或物理机。
kubernetes对集群的网络有如下要求:

  1. 所有的容器都可以在不用NAT的方式下同别的容器通信;
  2. 所有容器节点都可以在不用NAT的方式下同所有容器通信;
  3. 容器的地址和别人看到的地址是同一个地址

Docker的网络基础

kubernetes的网络依赖于Docker,Docker的网络又离不开Linux操作系统内核特性的支持。
Docker使用到的与Linux网络有关的主要技术主要包含如下几种:

网络命名空间

通过对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。让处于不同命名空间的网络进行通信,Veth设备对的一个重要作用就是打开互相看不到的协议栈之间的壁垒。

Veth 设备对

引入veth设备对是为了在不同的网络命名空间之间进行通信,利用它可以直接将两个网络命名空间连接起来,Veth设备都是成对出现的,很像一对以太网卡,并且中间有一条直连的网线。

网桥

网桥是一个二层网络设备,可以解析收发的报文,读取目标MAC的地址信息,和自己的MAC地址结合,来决策报文的转发端口。
Linux内核通过一个虚拟的网桥设备来实现桥接的,这个虚拟设备可以绑定若干以太网接口设备,从而将他们桥接起来。

docker自动完成了对网桥的创建和维护,常用的操作:
* brctl addr xxx ; 增加一个网桥
* 网桥的物理网卡作为一个端口,由于在链路层工作,就不需要IP地址了,这样上面的IP地址

Iptables/Netfilter

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

这些挂载点挂接的规则也分不同的类型(也就是规则表),目前支持的Table类型为:RAW,MANGLE,NAT,FILTER;当Linux协议栈的数据处理运行到挂载点时,会依次调用挂接点上的所有挂接函数,直到数据包的处理结果是明确地接受或者拒绝。

路由

路由功能由IP层维护的一张路由表来实现,路由表中的数据一般以条目形式存在,主要有: 目的IP地址、下一个路由器的IP地址、标志、网络接口规范、

Linux路由表至少要包括两个表,一个是LOCAL,另一个是MAIN,在LOCAL表中会包含所有的本地设备地址,它是在配置网络设配时自动创建的。LOCAL表用于
供Linux协议栈识别本地地址,以及进行本地各个不同网络接口之间数据的转发。MAIN表用于各类网络地址的转发,它的建立既可以使用静态配置完成,也可以使用动态路由协议完成。动态路由发现协议一般用组播功能来通过发送路由发现数据,动态的交换和获取网络的路由信息,并更新到路由表中。

Docker的网络实现

Docker 支持四种模式:

  • host模式
  • container 模式
  • none模式
  • bridge模式

kubernetes的网络实现

kubernetes的网络设计主要致力于解决以下场景:
* 容器到容器之间的通信
* 抽象的Pod到Pod之间的通信
* Pod到Service之间的通信
* 集群外部与内部组件之间的通信

容器到容器之间的通信

在同一个Pod内的容器(pod内的容器是不会跨越主机的)共享同一个网络命名空间,共享同一个Linux协议栈。

Pod之间的通信

同一个Node内的Pod之间的通信

每一个Pod都有一个真实的全局IP地址,同一个Node内的不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要使用使用其他发现机制。


Pod1和Pod2是通过Veth连接在docker0网桥上的,它们的IP地址IP1,IP2都是从docker0的网段上动态获取的,他们和网桥本身的IP3是同一个网段的。
在Pod1、Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是所有非本地地址的网络数据,都会默认发送到docker0网桥,由docker0网桥直接中转。

不同Node上的Pod之间进行通信

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

另一方方面,这些动态分配并且隐藏在docker0之后的所谓”私有IP地址”也是可以找到的。Kubernetes会记录所有正在运行的Pod的IP分配信息,并且将这些信息保存在etcd中(作为service的Endpoint),这些私有IP对Pod到Pod之间的通信是非常重要的。

综上,要想支持不同的Node上的Pod之间的通信,就要达到两个条件:
1. 在整个kubernetes集群中对Pod的IP进行规划,不能有冲突
2. 找到一种方法,将Pod的IP和所在Node关联起来,通过这个关联让Pod可以相互访问。

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

根据2的要求,Pod中的数据在发出时,需要有一个机制能够知道对方那个Pod的IP地址挂在那个具体的Node上,也就是说先要找到Node对应宿主机的IP地址,将数据发送到这个宿主机的网卡上,然后在宿主机上将相应的数据发送到具体的docker0上。一旦数据到达宿主机Node,则那个Node内部的docker0便知道如何将数据发送到pod。

跨Node的Pod通信模型如下:

开源的网络组件

Flannel

Flannel能实现以下两点:
* 它能协助kubernetes,给每个Node上的Docker容器分配不冲突的IP地址。
* 它能在这些IP之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动的传递到目标容器内。


flannel首先创建一个名为flannel0的网桥,并且这个网桥的一端连接docker0网桥,另外一端连接一个叫做flanneld的服务进程。

flanneld进程首先连上etcd,利用etcd来管理可分配的IP地址段资源,同时监控etcd里面每个Pod的实际地址,并在内存中建立了一个Pod节点路由表,然后下连docker0和物理网络,并适用内存中的Pod节点路由表,将docker0发送给它的数据包包装起来,利用物理网络的连接将数据包投递到目标flanneld上,从而完成了Pod到Pod之间的直接的地址通信。

直接路由

docker0网桥上的IP地址在Node上是看不见的,从一个Node到另外一个Node内的docker0是不通的。
我们可以通过部署MulitiLayer Switch(MLS)来实现这一点,在MSL中配置每个Docker0子网到Node地址的路由选项,通过MLS将docker0的IP寻址定向到对应的Node上。