文章目录
- 前言
- 容器间网络
- Pod间网络
- 同节点Pod通信
- 不同节点Pod通信
- Pod与Service间网络
前言
本文介绍了IPVS模式结合calico网络插件,在不同用例场景下流量是如何在kubernetes集群中流转的。
kubernetes的代理模式选择了IPVS模式,它可以将到达cluster ip的流量转发到真实的服务上。
而calico使用的是vxlan模式,帮助实现Pod跨节点通信。
本文主要简述了以下三种场景的网络通信:
- 容器间网络
- Pod间网络
- Pod与Service间网络
容器间网络
在同一个Pod中,容器间是可以直接通过localhost:port
访问的。之所以能够这样操作,是因为Pod是由一组同一网络命名空间下的docker容器组成。网络命名空间为这组容器提供一个独立的网络栈,route,iptables规则以及网络设备对于这些容器都是全新的,不受其他网络命名空间空间影响。而容器,本质上是一组受到资源限制,彼此间相互隔离的进程。那么我们可以把Pod看作一台服务器,容器间的通信就是这台服务器中的两个应用在通信。
Pod间网络
Pod间的网络可分为同节点Pod通信和不同节点Pod通信。同节点的Pod通信实现起来比较简单,既可以通过Linux Bridge把同节点的Pod关联起来,也可以通过route将流量转发到指定Pod。而不同节点的Pod通信相对要复杂一些,需要借助于vxlan或IPIP等技术来实现。
同节点Pod通信
如上图所示,pod1的eth0与root命名空间的veth0相连,pod2的eth0与root 命名空间的veth1相连,每个pod都会分配一个IP。
路由表内容如下:
$ route
Destination Gateway Genmask Flags Metric Ref Use Iface
default host-192-168-18 0.0.0.0 UG 0 0 0 eth0
169.254.169.254 host-192-168-18 255.255.255.255 UGH 0 0 0 eth0
172.16.44.195 0.0.0.0 255.255.255.255 UH 0 0 0 veth0
172.16.44.197 0.0.0.0 255.255.255.255 UH 0 0 0 veth1
当我们在pod1中向pod2发送请求时,
1、 请求报文直接从eth0发送到veth0。
2、接着报文从veth0网卡进入协议栈,通过查询路由表可知,报文会被转发到veth1,
3、将报文发送到pod2中,出现在eth0网卡。
在每个pod内都设置了相同的网卡及路由,因此pod只和本地的eth0通讯。而pod内的路由表如下:
$ route
Destination Gateway Genmask Flags Metric Ref Use Iface
default gateway 0.0.0.0 UG 0 0 0 eth0
gateway 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
pod内部的报文会匹配到默认路由,然后从eth0发出。
不同节点Pod通信
如上图所示,pod1和pod2分属node1和node2两个节点,这两个pod的IP属于同一个CIDR。而在本文中,这两个pod的通信是基于vxlan技术实现的,因此在两个节点上会有vxlan的接口。
pod1发送请求到pod2大致流程如下:
1、pod1中的报文经pod内部的eth0直接发送到calia7dc8ff0932;
2、报文经calia7dc8ff0932进入协议栈,查询路由后,目的IP匹配到网关IP(172.16.154.192),然后转发到vxlan.calico接口;
路由表如下:
$ route
Destination Gateway Genmask Flags Metric Ref Use Iface
default host-192-168-18 0.0.0.0 UG 0 0 0 eth0
169.254.169.254 host-192-168-18 255.255.255.255 UGH 0 0 0 eth0
172.16.154.192 172.16.154.192 255.255.255.192 UG 0 0 0 vxlan.calico
172.16.235.192 0.0.0.0 255.255.255.192 U 0 0 0 *
172.16.235.193 0.0.0.0 255.255.255.255 UH 0 0 0 calia7dc8ff0932
192.168.186.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
3、vxlan.calico是vxlan类型接口,可以将进入的报文封装成UDP报文,端口号4789,然后再次将报文送入协议栈;
通过转发表可以获取到对端vxlan.calico所在节点的IP。
$ bridge fdb
33:33:00:00:00:01 dev eth0 self permanent
01:00:5e:00:00:01 dev eth0 self permanent
33:33:ff:38:55:04 dev eth0 self permanent
33:33:00:00:02:02 dev eth0 self permanent
66:0f:78:83:d5:e1 dev vxlan.calico dst 192.168.186.202 self permanent
33:33:00:00:00:01 dev calia7dc8ff0932 self permanent
01:00:5e:00:00:01 dev calia7dc8ff0932 self permanent
33:33:ff:ee:ee:ee dev calia7dc8ff0932 self permanent
至于目的MAC地址,我们可以通过ip neigh
命令查看。因为报文要发送到对端vxlan.calico上,可以通过以下信息获取对应MAC地址。
$ ip neigh
172.16.154.192 dev vxlan.calico lladdr 66:0f:78:83:d5:e1 PERMANENT
4、UDP报文经node1的eth0网卡发出,经物理网络到达node2;
5、node2监听端口为4789的报文,将报文转发到vxlan.calico;
6、报文经过vxlan.calico网卡进行解封,获取真实报文,然后根据路由表,将报文发送到cali5132a054307进入pod2。
Pod与Service间网络
在kubernetes中,pod并不是一直稳定存在,一旦重新创建pod,那么pod IP也会随之改变。因此kubernetes提出service
的概念,通过service为后端服务提供一个稳定的虚拟IP,发送到此虚拟IP的请求会被转发到后端真实服务上。而service有三种代理模式,分别是userspace模式、iptables模式和IPVS模式,这里笔者采用的是IPVS模式。
IPVS作为Linux内核的一部分,基于netfilter实现了传输层的负载均衡。基于TCP或UPD的请求,将请求转发到真实服务上。
采用IPVS模式,kubernetes会创建dummy网卡。每当kubernetes创建service时,会将该服务的vip绑定到这张网卡。
pod到service代理的服务请求流程大致如下:
1、目的IP为Service IP的请求通过pod内的eth0设备发出到达对端veth;
2、在宿主机端无法找到Service IP的路由,根据默认路由进行转发。
3、在经过iptables的postrouting链时,IPVS会根据指定的负载均衡算法选择一个Pod,并进行DNAT,将目的IP改为Pod IP。iptables还会利用Linux内核的conntrack工具记录所选的Pod。
4、请求被正确修改后发送到指定的Pod;
5、Pod处理请求后,响应数据通过veth pair到达宿主机,经过iptables时根据conntrack记录的信息将响应方的IP修改为Service IP。
6、根据路由将响应数据转发到指定Pod。