前言 本文主要分析K8s中网络组件calico的IPIP网络模式。旨在理解IPIP网络模式下产生的calixxxx,tunl0等设备以及跨节点网络通信方式。可能看着有点枯燥,但是请花几分钟时间坚持看完,如果看到后面忘了前面,请反复看两遍,这几分钟时间一定你会花的很值。一、Calico介绍 Calico是Kubernetes生态系统中另一种流行的网络选择。虽然Flannel被公认为是最简单的选择,但Calico以其性能、灵活性而闻名。Calico的功能更为全面,不仅提供主机和Pod之间的网络连接,还涉及网络安全和管理。CalicoCNI插件在CNI框架内封装了Calico的功能。 Calico是一个基于BGP的纯三层的网络方案,与OpenStack、Kubernetes、AWS、GCE等云平台都能够良好地集成。Calico在每个计算节点都利用LinuxKernel实现了一个高效的虚拟路由器vRouter来负责数据转发。每个vRouter都通过BGP1协议把在本节点上运行的容器的路由信息向整个Calico网络广播,并自动设置到达其他节点的路由转发规则。 Calico保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者OverlayNetwork,没有额外的封包解包,能够节约CPU运算,提高网络效率。 此外,Calico基于iptables还提供了丰富的网络策略,实现了K8s的NetworkPolicy策略,提供容器间网络可达性限制的功能。 calico官网:https:www。projectcalico。org二、Calico架构及核心组件 架构图如下: Calico核心组件:Felix:运行在每个需要运行workload的节点上的agent进程。主要负责配置路由及ACLs(访问控制列表)等信息来确保endpoint的连通状态,保证跨主机容器的网络互通;etcd:强一致性、高可用的键值存储,持久存储calico数据的存储管理系统。主要负责网络元数据一致性,确保Calico网络状态的准确性;BGPClient(BIRD):读取Felix设置的内核路由状态,在数据中心分发状态。BGPRouteReflector(BIRD):BGP路由反射器,在较大规模部署时使用。如果仅使用BGPClient形成mesh全网互联就会导致规模限制,因为所有BGPclient节点之间两两互联,需要建立N2个连接,拓扑也会变得复杂。因此使用reflector来负责client之间的连接,防止节点两两相连。三、Calico工作原理 Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。四、Calico的两种网络方式 1)IPIP 把IP层封装到IP层的一个tunnel。它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需IP,而这个ipip则是通过两端的路由做一个tunnel,把两个本来不通的网络通过点对点连接起来。ipip的源代码在内核netipv4ipip。c中可以找到。 2)BGP 边界网关协议(BorderGatewayProtocol,BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。五、IPIP网络模式分析 由于个人环境中使用的是IPIP模式,因此接下来这里分析一下这种模式。kubectlgetpoowidenpaasgrephellodemohelloperfd84bffcb87fxqj11Running09d10。20。105。215node2。perfnonenonedemohellosit6d5c9f44bcncpql11Running09d10。20。42。31node1。sitnonenone 进行Ping测试 这里在demohelloperf这个pod中pingdemohellosit这个pod。rootdemohelloperfd84bffcb87fxqj:ping10。20。42。31PING10。20。42。31(10。20。42。31)56(84)bytesofdata。64bytesfrom10。20。42。31:icmpseq1ttl62time5。60ms64bytesfrom10。20。42。31:icmpseq2ttl62time1。66ms64bytesfrom10。20。42。31:icmpseq3ttl62time1。79msC10。20。42。31pingstatistics3packetstransmitted,3received,0packetloss,time6msrttminavgmaxmdev1。6623。0155。5951。825ms 进入poddemohelloperf中查看这个pod中的路由信息rootdemohelloperfd84bffcb87fxqj:routenKernelIProutingtableDestinationGatewayGenmaskFlagsMetricRefUseIface0。0。0。0169。254。1。10。0。0。0UG000eth0169。254。1。10。0。0。0255。255。255。255UH000eth0 根据路由信息,ping10。20。42。31,会匹配到第一条。 第一条路由的意思是:去往任何网段的数据包都发往网关169。254。1。1,然后从eth0网卡发送出去。 demohelloperf所在的nodenode2。perf宿主机上路由信息如下:routenKernelIProutingtableDestinationGatewayGenmaskFlagsMetricRefUseIface0。0。0。0172。16。36。10。0。0。0UG10000eth010。20。42。0172。16。35。4255。255。255。192UG000tunl010。20。105。1960。0。0。0255。255。255。255UH000cali4bb1efe70a2169。254。169。254172。16。36。2255。255。255。255UGH10000eth0172。16。36。00。0。0。0255。255。255。0U10000eth0172。17。0。00。0。0。0255。255。0。0U000docker0 可以看到一条Destination为10。20。42。0的路由。 意思是:当ping包来到master节点上,会匹配到路由tunl0。该路由的意思是:去往10。20。42。026的网段的数据包都发往网关172。16。35。4。因为demohelloperf的pod在172。16。36。5上,demohellosit的pod在172。16。35。4上。所以数据包就通过设备tunl0发往到node节点上。 demohellosit所在的nodenode1。sit宿主机上路由信息如下:routenKernelIProutingtableDestinationGatewayGenmaskFlagsMetricRefUseIface0。0。0。0172。16。35。10。0。0。0UG10000eth010。20。15。64172。16。36。4255。255。255。192UG000tunl010。20。42。310。0。0。0255。255。255。255UH000cali04736ec14ce10。20。105。192172。16。36。5255。255。255。192UG000tunl0 当node节点网卡收到数据包之后,发现发往的目的IP为10。20。42。31,于是匹配到Dst为10。20。42。31的路由。 该路由的意思是:10。20。42。31是本机直连设备,去往设备的数据包发往cali04736ec14ce。 为什么这么奇怪会有一个名为cali04736ec14ce的设备呢?这是个啥玩意儿呢? 其实这个设备就是vethpair的一端。在创建demohellosit时calico会给demohellosit创建一个vethpair设备。一端是demohellosit的网卡,另一端就是我们看到的cali04736ec14ce。 接着验证一下。我们进入demohellosit的Pod,查看到4号设备后面的编号是:122964rootdemohellosit6d5c9f44bcncpql:ipa1:lo:LOOPBACK,UP,LOWERUPmtu65536qdiscnoqueuestateUNKNOWNgroupdefaultqlen1000linkloopback00:00:00:00:00:00brd00:00:00:00:00:00inet127。0。0。18scopehostlovalidlftforeverpreferredlftforever2:tunl0NONE:NOARPmtu1480qdiscnoopstateDOWNgroupdefaultqlen1000linkipip0。0。0。0brd0。0。0。04:eth0if122964:BROADCAST,MULTICAST,UP,LOWERUPmtu1380qdiscnoqueuestateUPgroupdefaultlinkether9a:7d:b2:26:9b:17brdff:ff:ff:ff:ff:fflinknetnsid0inet10。20。42。3132brd10。20。42。31scopeglobaleth0validlftforeverpreferredlftforever 然后我们登录到demohellosit这个pod所在的宿主机查看ipagrepA5cali04736ec14ce122964:cali04736ec14ceif4:BROADCAST,MULTICAST,UP,LOWERUPmtu1380qdiscnoqueuestateUPgroupdefaultlinketheree:ee:ee:ee:ee:eebrdff:ff:ff:ff:ff:fflinknetnsid16inet6fe80::ecee:eeff:feee:eeee64scopelinkvalidlftforeverpreferredlftforever120918:calidd1cafcd275if4:BROADCAST,MULTICAST,UP,LOWERUPmtu1380qdiscnoqueuestateUPgroupdefaultlinketheree:ee:ee:ee:ee:eebrdff:ff:ff:ff:ff:fflinknetnsid2 发现poddemohellosit中的另一端设备编号和这里在node上看到的cali04736ec14ce编号122964是一样的 所以,node上的路由,发送cali04736ec14ce网卡设备的数据其实就是发送到了demohellosit的这个Pod中去了。到这里Ping包就到了目的地。 注意看demohellosit这个Pod所在的宿主机的路由,有一条Destination为10。20。105。192的路由routenKernelIProutingtableDestinationGatewayGenmaskFlagsMetricRefUseIface。。。0。0。0。0172。16。35。10。0。0。0UG10000eth010。20。105。192172。16。36。5255。255。255。192UG000tunl0。。。 再查看一下demohellosit的Pod中路由信息,和demohelloperf的Pod中是一样的。 所以综合上述例子来看,IPIP的网络模式就是将IP网络封装了一层。特点就是所有Pod的数据流量都从隧道tunl0发送,并且tunl0这里增加了一层传输层的封包操作。六、抓包分析 在demohelloperf这个Pod中Pingdemohellosit这个Pod,接着在demohellosit这个Pod所在的宿主机进行tcpdump。tcpdumpieth0nnwicmpping。captcpdump:listeningoneth0,linktypeEN10MB(Ethernet),capturesize262144bytes 在demohelloperf这个pod中进行pingdemohellosit的操作rootdemohelloperfd84bffcb87fxqj:ping10。20。42。31PING10。20。42。31(10。20。42。31)56(84)bytesofdata。64bytesfrom10。20。42。31:icmpseq1ttl62time5。66ms64bytesfrom10。20。42。31:icmpseq2ttl62time1。68ms64bytesfrom10。20。42。31:icmpseq3ttl62time1。61msC10。20。42。31pingstatistics3packetstransmitted,3received,0packetloss,time6msrttminavgmaxmdev1。6082。9835。6591。892ms 结束抓包后下载icmpping。cap到本地windows进行抓包分析 能看到该数据包一共5层,其中IP(InternetProtocol)所在的网络层有两个,分别是Pod之间的网络和主机之间的网络封装。 红色框选的是两个Pod所在的宿主机,蓝色框选的是两个Pod的IP,src表示发起ping操作的Pod所在的宿主机ip以及发起ping操作的pod的ip,dst表示被ping的pod所在的宿主机ip及被ping的pod的ip。 根据数据包的封装顺序,应该是在demohelloperfpingdemohellosit的ICMP包外面多封装了一层主机之间的数据包。 可以看到每个数据报文共有两个IP网络层,内层是Pod容器之间的IP网络报文,外层是宿主机节点的网络报文(2个node节点)。之所以要这样做是因为tunl0是一个隧道端点设备,在数据到达时要加上一层封装,便于发送到对端隧道设备中。 两层封包的具体内容如下: Pod间的通信经由IPIP的三层隧道转发,相比较VxLAN的二层隧道来说,IPIP隧道的开销较小,但其安全性也更差一些。七、Pod到SVC的访问 查看ServicekubectlgetsvcowidenpaasgrephellodemohelloperfClusterIP10。10。255。18none8080TCP10dappEnvperf,appNamedemohellodemohellositClusterIP10。10。48。254none8080TCP10dappEnvsit,appNamedemohello 在Poddemohellosit的宿主机上抓包tcpdumpieth0nnwsvc。captcpdump:listeningoneth0,linktypeEN10MB(Ethernet),capturesize262144bytes 测试访问,在demohellosit中curldemohelloperf的svc的地址和端口rootdemohelloperfd84bffcb87fxqj:curlIhttp:10。10。48。254:8080actuatorhealthHTTP1。1200ContentType:applicationvnd。springboot。actuator。v3jsonTransferEncoding:chunkedDate:Fri,30Apr202101:42:56GMTrootdemohelloperfd84bffcb87fxqj:curlIhttp:10。10。48。254:8080actuatorhealthHTTP1。1200ContentType:applicationvnd。springboot。actuator。v3jsonTransferEncoding:chunkedDate:Fri,30Apr202101:42:58GMTrootdemohelloperfd84bffcb87fxqj:curlIhttp:10。10。48。254:8080actuatorhealthHTTP1。1200ContentType:applicationvnd。springboot。actuator。v3jsonTransferEncoding:chunkedDate:Fri,30Apr202101:42:58GMT 结束抓包,下载svc。cap文件放到wireshark中打开查看 可以看到wireshark中Src和Dst的结果。任然是和上面pod中访问pod的ip地址一样。这里Src和Dst任然是两个pod的宿主机的内网ip和两个pod自己的ip地址。是用ipip的方式进行通信的。 通过以上例子演示,应该就看明白了IPIP网络模式的通信方式了吧!