在讨论Kubernetes网络之前,让我们先来看一下Docker网络
一 docker的网络模式
安装完docker的服务器上使用 docker network ls命令列出网络
1) Host
我们在使用docker run创建Docker容器时,可以用 --net=host选项指定容器的网络模式, 容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口,如下图,将宿主机的ip 172.20.128.0的地址和docker0网桥地址也展示出来。
2) Bridge
默认情况,Docker使用bridge网络模式, 此模式会为每一个容器分配Network Namespace、设置IP等,并将并将一个主机上的Docker容器连接到一个名称为docker0的虚拟网桥上,如下图所示:
这样主机上的所有容器就通过虚拟网桥设备连在了一个二层网络中,docker会从定义的网段(可自定义)分配一个ip给容器,如下图为172.17.0.2,网关指向docker0的地址。
3) None
在none模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。使用–net =none模式启动容器:
4) Container
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。使用–net =container模式启动容器:
docker run -itd --name docker-bridge --network=bridge jl890314/centos:7.7
docker run -itd --name docker-container --network=container:07c7efbb26bf jl890314/centos:7.7
二 k8s的网络模式
Kubernetes与Docker网络有些不同。Kubernetes网络需要解决下面的4个问题:
集群内:
容器与容器之间的通信
Pod和Pod之间的通信
Pod和服务之间的通信
集群外:
外部应用与服务之间的通信
因此,Kubernetes假设Pod之间能够进行通讯,这些Pod可能部署在不同的宿主机上。每一个Pod都拥有自己的IP地址,因此能够将Pod看作为物理主机或者虚拟机,从而能实现端口设置、命名、服务发现、负载均衡、应用配置和迁移。为了满足上述需求,则需要通过集群网络来实现。
在本文主要分析容器与容器之间,以及Pod和Pod之间的通信;Pod和服务之间,以及外部应用与服务之间的通信请参考Kubernetes的Service和Ingress。
1) 同一个pod中容器之间的通讯
这种场景对于Kubernetes来说没有任何问题,根据Kubernetes的架构设计。Kubernetes创建Pod时,首先会创建一个pause容器,为Pod指派一个唯一的IP地址。然后,以pause的网络命名空间为基础,创建同一个Pod内的其它容器(--net=container:xxx)。因此,同一个Pod内的所有容器就会共享同一个网络命名空间,在同一个Pod之间的容器可以直接使用localhost进行通信
2) 不同pod中容器之间的通讯
对于此场景,情况现对比较复杂一些,这就需要解决Pod间的通信问题。在Kubernetes通过flannel、calic等网络插件解决Pod间的通信问题。 本文以flannel为例说明在Kubernetes中网络模型,flannel是kubernetes默认提供网络插件,规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网。为了跟踪各个子网的分配情况,flannel使用etcd来存储虚拟IP和主机IP之间的映射,各个节点上运行的flanneld守护进程负责监视etcd中的信息并完成报文路由
flannel在每个Node上启动了一个flanneld的服务,在flanneld启动后,将从etcd中读取配置信息,并请求获取子网的租约。所有Node上的flanneld都依赖etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。flanneld一旦获取子网租约、配置后端后,会将一些信息写入/run/flannel/subnet.env文件,如下图的两个不同的node节点上的信息,
三 flannel的数据传输过程
1) 源容器向目标容器发送数据,数据首先发送给docker0网桥.
2) docker0网桥接受到数据后,将其转交给flannel.1虚拟网卡处理
3) flannel.1接受到数据后,对数据进行封装,并发给宿主机的eth0
flannel.1收到数据后,flannelid会将数据包封装成二层以太包。
Ethernet Header的信息:
From:{源容器flannel.1虚拟网卡的MAC地址}
To:{目标容器flannel.1虚拟网卡的MAC地址}
4) 对在flannel路由节点封装后的数据,进行再封装后,转发给目标容器Node的eth0
由于目前的数据包只是vxlan tunnel上的数据包,因此还不能在物理网络上进行传输。因此,需要将上述数据包再次进行封装,才能源容器节点传输到目标容器节点,这项工作在由linux内核来完成。
Ethernet Header的信息:
From:{源容器Node节点网卡的MAC地址}
To:{目录容器Node节点网卡的MAC地址}
IP Header的信息:
From:{源容器Node节点网卡的IP地址}
To:{目录容器Node节点网卡的IP地址}
通过此次封装,就可以通过物理网络发送数据包。
在目标容器宿主机中的数据传递过程:
5) 目标容器宿主机的eth0接收到数据后,对数据包进行拆封,并转发给flannel.1虚拟网卡;
6) flannel.1 虚拟网卡接受到数据,将数据发送给docker0网桥;
7) 最后,数据到达目标容器,完成容器之间的数据通信。
四 flannel网络的几种模式
1 VxLAN:
Linux内核自3.7.0版本起支持VxLAN, flannel的此种后端意味着使用内核中的VxLAN模块封装报文,这也是flannel较为推荐使用的方式 VxLAN,全称Virtual extensible Local Area Network(虚拟可扩展局域网),是VLAN扩展方案草案,采用的是MAC in UDP封装方式,是NVo3(Network Virtualization over Layer3)中的一种网络虚拟化技术,如图所示。具体实现方式为:将虚拟网络的数据帧添加到VxLAN首部后,封装在物理网络的UDP报文中,然后以传统网络的通信方式传送该UDP报文,待其到达目的主机后,去掉物理网络报文的头部信息以及VxLAN首部,然后将报文交付给目的终端,如图所示的拓扑结构中,跨节点的Pod间通信即为如此。不过,整个过程中通信双方对物理网络无所感知
2 host-gw:
Host GateWay,它通过在节点上创建到达目标容器地址的路由直接完成报文转发,因此这种方式要求各节点本身必须在同一个二层网络中,故该方式不太适用于较大的网络规模(大二层网络除外)。host-gw有着较好的转发性能,且易于设定,推荐对报文转发性能要求较高的场景使用
host-gw后端通过添加必要的路由信息使用节点的二层网络直接发送Pod的通信报文,其工作方式类似于VxLAN后端中direct routing的功能,但不包括其VxLAN的隧道转发能力。其工作模型示意图如图所示。
3 UDP:
使用普通UDP报文封装完成隧道转发,其性能较前两种方式要低很多,仅应该在不支持前两种方式的环境中使用。