前言
一、Calico介绍
此外,Calico 基于 iptables 还提供了丰富的网络策略,实现了 K8s 的 Network Policy策略,提供容器间网络可达性限制的功能。
calico官网:https://www.projectcalico.org/
二、Calico架构及核心组件
架构图如下:
-
Felix:运行在每个需要运行 workload 的节点上的 agent 进程。主要负责配置路由及 ACLs(访问控制列表)等信息来确保 endpoint 的连通状态,保证跨主机容器的网络互通;
-
etcd:强一致性、高可用的键值存储,持久存储calico数据的存储管理系统。主要负责网络元数据一致性,确保Calico网络状态的准确性;
-
BGP Client(BIRD):读取Felix设置的内核路由状态,在数据中心分发状态。
-
BGP Route Reflector(BIRD):BGP路由反射器,在较大规模部署时使用。如果仅使用BGP Client形成mesh全网互联就会导致规模限制,因为所有BGP client节点之间两两互联,需要建立N^2个连接,拓扑也会变得复杂。因此使用reflector来负责client之间的连接,防止节点两两相连。
三、Calico工作原理
Calico 把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以 Calico 方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。
四、Calico的两种网络方式
五、IPIP网络模式分析
由于个人环境中使用的是IPIP模式,因此接下来这里分析一下这种模式。
# kubectl get po -o wide -n paas | grep hello
demo-hello-perf-d84bffcb8-7fxqj 1/1 Running 0 9d 10.20.105.215 node2.perf <none> <none>
demo-hello-sit-6d5c9f44bc-ncpql 1/1 Running 0 9d 10.20.42.31 node1.sit <none> <none>
进行 Ping 测试
这里在 demo-hello-perf 这个 pod 中 ping demo-hello-sit 这个 pod。
root@demo-hello-perf-d84bffcb8-7fxqj:/# ping 10.20.42.31
PING 10.20.42.31 (10.20.42.31) 56(84) bytes of data.
64 bytes from 10.20.42.31: icmp_seq=1 ttl=62 time=5.60 ms
64 bytes from 10.20.42.31: icmp_seq=2 ttl=62 time=1.66 ms
64 bytes from 10.20.42.31: icmp_seq=3 ttl=62 time=1.79 ms
^C
--- 10.20.42.31 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 1.662/3.015/5.595/1.825 ms
进入 pod demo-hello-perf 中查看这个 pod 中的路由信息
root@demo-hello-perf-d84bffcb8-7fxqj:/# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
根据路由信息,ping 10.20.42.31
,会匹配到第一条。第一条路由的意思是:去往任何网段的数据包都发往网关169.254.1.1,然后从eth0网卡发送出去。 demo-hello-perf 所在的 node node2.perf 宿主机上路由信息如下: # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.36.1 0.0.0.0 UG 100 0 0 eth0
10.20.42.0 172.16.35.4 255.255.255.192 UG 0 0 0 tunl0
10.20.105.196 0.0.0.0 255.255.255.255 UH 0 0 0 cali4bb1efe70a2
169.254.169.254 172.16.36.2 255.255.255.255 UGH 100 0 0 eth0
172.16.36.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
可以看到一条 Destination 为
10.20.42.0
的路由。意思是:当 ping 包来到 master 节点上,会匹配到路由 tunl0。该路由的意思是:去往 10.20.42.0/26 的网段的数据包都发往网关172.16.35.4。因为demo-hello-perf的pod在172.16.36.5上,demo-hello-sit的pod在172.16.35.4上。所以数据包就通过设备tunl0发往到node节点上。 demo-hello-sit 所在的 node node1.sit 宿主机上路由信息如下:
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.16.35.1 0.0.0.0 UG 100 0 0 eth0
10.20.15.64 172.16.36.4 255.255.255.192 UG 0 0 0 tunl0
10.20.42.31 0.0.0.0 255.255.255.255 UH 0 0 0 cali04736ec14ce
10.20.105.192 172.16.36.5 255.255.255.192 UG 0 0 0 tunl0
当 node 节点网卡收到数据包之后,发现发往的目的 IP 为 10.20.42.31,于是匹配到 Dst 为 10.20.42.31 的路由。 该路由的意思是:10.20.42.31 是本机直连设备,去往设备的数据包发往cali04736ec14ce。
为什么这么奇怪会有一个名为 cali04736ec14ce 的设备呢?这是个啥玩意儿呢? 其实这个设备就是 veth pair 的一端。在创建 demo-hello-sit
时 calico 会给demo-hello-sit
创建一个 veth pair 设备。一端是demo-hello-sit
的网卡,另一端就是我们看到的 cali04736ec14ce。接着验证一下。我们进入 demo-hello-sit 的 Pod,查看到 4 号设备后面的编号是:122964 root@demo-hello-sit--6d5c9f44bc-ncpql:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if122964: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
link/ether 9a:7d:b2:26:9b:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.20.42.31/32 brd 10.20.42.31 scope global eth0
valid_lft forever preferred_lft forever
然后我们登录到 demo-hello-sit 这个pod所在的宿主机查看
# ip a | grep -A 5 "cali04736ec14ce"
122964: cali04736ec14ce@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 16
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
120918: calidd1cafcd275@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1380 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2
发现 pod demo-hello-sit中 的另一端设备编号和这里在 node 上看到的 cali04736ec14ce 编号 122964 是一样的
所以,node上的路由,发送 cali04736ec14ce 网卡设备的数据其实就是发送到了demo-hello-sit 的这个 Pod 中去了。到这里 Ping 包就到了目的地。 注意看 demo-hello-sit 这个 Pod 所在的宿主机的路由,有一条 Destination 为10.20.105.192 的路由 # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
...
0.0.0.0 172.16.35.1 0.0.0.0 UG 100 0 0 eth0
10.20.105.192 172.16.36.5 255.255.255.192 UG 0 0 0 tunl0
...
再查看一下demo-hello-sit 的 Pod 中路由信息,和 demo-hello-perf 的 Pod 中是一样的。 所以综合上述例子来看,IPIP 的网络模式就是将IP网络封装了一层。特点就是所有 Pod的数据流量都从隧道 tunl0 发送,并且 tunl0 这里增加了一层传输层的封包操作。 六、抓包分析
在 demo-hello-perf 这个 Pod 中 Ping demo-hello-sit 这个 Pod,接着在 demo-hello-sit 这个 Pod 所在的宿主机进行 tcpdump。 # tcpdump -i eth0 -nn -w icmp_ping.cap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
在 demo-hello-perf 这个 pod 中进行 ping demo-hello-sit 的操作
root@demo-hello-perf-d84bffcb8-7fxqj:/# ping 10.20.42.31
PING 10.20.42.31 (10.20.42.31) 56(84) bytes of data.
64 bytes from 10.20.42.31: icmp_seq=1 ttl=62 time=5.66 ms
64 bytes from 10.20.42.31: icmp_seq=2 ttl=62 time=1.68 ms
64 bytes from 10.20.42.31: icmp_seq=3 ttl=62 time=1.61 ms
^C
--- 10.20.42.31 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 1.608/2.983/5.659/1.892 ms
结束抓包后下载 icmp_ping.cap 到本地 windows 进行抓包分析 能看到该数据包一共5层,其中IP(Internet Protocol)所在的网络层有两个,分别是Pod 之间的网络和主机之间的网络封装。
红色框选的是两个 Pod 所在的宿主机,蓝色框选的是两个 Pod 的 IP,src 表示发起 ping 操作的 Pod 所在的宿主机 ip 以及发起 ping 操作的 pod 的ip,dst 表示被ping的pod 所在的宿主机 ip 及被 ping 的 pod 的 ip。 根据数据包的封装顺序,应该是在 demo-hello-perf ping demo-hello-sit 的 ICMP 包外面多封装了一层主机之间的数据包。
可以看到每个数据报文共有两个IP网络层,内层是 Pod 容器之间的 IP 网络报文,外层是宿主机节点的网络报文(2个node节点)。之所以要这样做是因为 tunl0 是一个隧道端点设备,在数据到达时要加上一层封装,便于发送到对端隧道设备中。 两层封包的具体内容如下:
Pod 间的通信经由 IPIP 的三层隧道转发,相比较 VxLAN 的二层隧道来说,IPIP 隧道的开销较小,但其安全性也更差一些。
七、Pod 到 SVC 的访问
查看 Service
# kubectl get svc -o wide -n paas | grep hello
demo-hello-perf ClusterIP 10.10.255.18 <none> 8080/TCP 10d appEnv=perf,appName=demo-hello
demo-hello-sit ClusterIP 10.10.48.254 <none> 8080/TCP 10d appEnv=sit,appName=demo-hello
在 Pod demo-hello-sit 的宿主机上抓包
# tcpdump -i eth0 -nn -w svc.cap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
测试访问,在demo-hello-sit中curl demo-hello-perf的svc的地址和端口
root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Fri, 30 Apr 2021 01:42:56 GMT
root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Fri, 30 Apr 2021 01:42:58 GMT
root@demo-hello-perf-d84bffcb8-7fxqj:/# curl -I http://10.10.48.254:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Fri, 30 Apr 2021 01:42:58 GMT
结束抓包,下载svc.cap文件放到wireshark中打开查看
可以看到wireshark中Src和Dst的结果。任然是和上面pod中访问pod的ip地址一样。这里Src和Dst任然是两个pod的宿主机的内网ip和两个pod自己的ip地址。是用ipip的方式进行通信的。 通过以上例子演示,应该就看明白了IPIP网络模式的通信方式了吧! 来源:本文转自公众号运维开发技术,点击查看原文。
史上最简明的 Tcpdump 入门指南,看这一篇就够了
Linux Shell 脚本的 10 个有用的“面试问题和解答”
原文始发于微信公众号(高效运维):K8s 高性能网络组件详解:Calico 的 IPIP 网络模式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论