文章目录

  • 系列文章目录
  • 前言
  • Calico通过calixxx和tunl0网卡完成节点间Pod通信
  • 准备两台机器
  • 第一种情况 指定网卡1(白名单配置法)
  • 第二种情况 指定网卡2(白名单配置法)
  • 第三种情况 跳过指定网卡(黑名单配置法)
  • 第四种情况 通过网络连通性判断(指定IP)
  • 第五种情况 通过cidr判断
  • 总结



前言

前面两篇学会了Calico的三个功能:
(1) 给Pod分配虚拟IP, 并完成Pod之间网络通信(同一节点Pod和不同节点Pod)
(2) IPPool给Pod分配固定IP
(3) Calico给Pod分配网络策略

第三篇学会了使用calicoctl操作calico各种资源,即 calicoctl + verb + resource

这是第四篇,来学习一下,不同节点间Pod通信的时候,是 tunl0+calixx 实现的:每个有虚拟IP的Pod 网络直连一个确定的 calixxx 网卡,然后 calixxx 网卡连接自己所在 linux 机器上的 tunl0 网卡,然后 tunl0 网卡连接自己所在 linux 机器上的某个具体网卡,将网络请求发送出去,发送给 目的Pod 所在机器的 tunl0 连接的网卡上,然后目的机器网卡发送给目的机器tunl0,然后目的机器 tunl0 发给目的Pod 的calixxx,然后目的Pod的calixxx 网络直连目的Pod。如此,整个过程完成不同节点上的Pod网络通信。

但是这里有一个问题,无论是源Linux服务器,还是目的Linux机器,如果机器上有多张网卡,那么 tunl0 会绑定哪个网卡呢?这可以通过 calico-node 的 yaml 来配置指定,各种情况下如何配置指定就是本文的重点。

Calico通过calixxx和tunl0网卡完成节点间Pod通信

默认情况下calico使用的是IPIP模式进行通信(IPIP模式和BGP模式两种,当前正在使用哪种看ippool),所有节点之间建立tunl0隧道(本质是所有node上有一个tul0网卡)。每创建一个pod在宿主机上都会产生一个以cali开头的虚拟接口,如下图pod1生成了一个calixxxxx网卡,pod2生成了一个caliyyyyy虚拟网卡。每个pod发出去的数据包会直接转发到cali开头的网卡,这个接口跟pod里的calixxx网卡是veth pair的关系。

这种veth pair最直接的理解就是“网线直连”,一方面,pod1发出去的所有数据包,“闭着眼”转发给calixxxxx;另一方面,calixxxxx从其他地方收到的数据包“闭着眼”转发给pod1。这种网线直连底层是通过 linux route 本机网路路由实现的。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_IP

cali开头的网卡会连接到tunl0上,pod1和pod2通信时,pod1发出的数据包先到达calixxxxx,然后进入vms71的tunl0接口,再然后到达隧道的另一端vms72的tunl0接口,然后转发到caliyyyy,最终到达了pod2。所以tunl0隧道的作用就是封装所有pod之间的流量。如下图:

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_容器_02

现在的问题是tunl0隧道所走的物理线路是哪条呢?默认情况下calico会自动选择,但是如果有多张网卡,比如上图中左边Linux机器有 ens32 和 ens33 两个网卡,则这个机器上 tunl0 要绑定哪个网卡;右边Linux机器有 eth0 和 eth1 两个网卡,则这个机器 tunl0 绑定哪个网卡。我们想手动选择 tunl0 绑定的网卡,下面分成5种情况来看tunl0如何选择物理线路。

准备两台机器

本实验共两台节点,如下。

[root@vms71 ~]# kubectl get nodes
NAME            STATUS   ROLES                  AGE   VERSION
vms71.rhce.cc   Ready    control-plane,master   54m   v1.23.2
vms72.rhce.cc   Ready    <none>                 54m   v1.23.2
[root@vms71 ~]#

为了测试,特地把vms72上的网卡名做了修改,原来的ens32修改名为eth0,原来的ens33修改名为eth1。

vms71上ens32 ----- vms72上eth1   同一网段,本机直达,无需路由
vms71上ens33 ----- vms72上eth0   同一网段,本机直达,无需路由

同一网段的网卡,结合上图,我这里并没写错。

练习时会在vms71上创建pod1,在vms72上创建pod2,所编写的pod.yaml内容如下。

[root@vms71 ~]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  nodeName: vms71.rhce.cc   # 将 pod1 放到 vms71
  terminationGracePeriodSeconds: 0
  containers:
  - image: localhost/centos:test
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 1d"]
    name: pod1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
---
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod2
  name: pod2
spec:
  nodeName: vms72.rhce.cc    # 将 pod2 放到 vms72
  terminationGracePeriodSeconds: 0
  containers:
  - image: localhost/centos:test
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 1d"]
    name: pod2
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
[root@vms71 ~]#

第一种情况 指定网卡1(白名单配置法)

修改calico.yaml的内容,找到IP_AUTODETECTION_METHOD

- name: IP_AUTODETECTION_METHOD
  value: "interface=ens33,eth0"

这个IP_AUTODETECTION_METHOD参数的含义是:指定tunl0隧道所对应的物理接口为ens33或者eth0。在vms71上他会选择ens33,在vms72上会选择eth0。

如果缺省这个 IP_AUTODETECTION_METHOD 参数,tunl0 就会自动选择。开发过程中,如果linux上只有一张网卡,无需指定 IP_AUTODETECTION_METHOD 参数,因为 tunl0 隧道会自动连接这个网卡;如果linux上只有多张网卡,建议指定 IP_AUTODETECTION_METHOD 参数,因为如果 tunl0 隧道对应的本机物理网卡,如果和 route 中配置的网关不通,会导致不同Node之间的Pod无法通信。

不同Node之间的Pod无法通信:
(1) 要么就是缺少route路由配置,需要增加一条route路由,通向另一个node的Block (另一个node的Block是什么可以在另一个node route 中看到)
(2) 要么就是 tunl0 绑定的网卡与到达目的地址的经过的gateway不通,需要在calico-node.yaml 这个ds中手动配置一个和 route规则中gateway网络通的 本机网卡

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_kubernetes_03

安装calico。

[root@vms71 ~]# kubectl apply -f calico.yaml
configmap/calico-config created
	...输出...
[root@vms71 ~]#

创建测试用的pod。

[root@vms71 ~]# kubectl apply -f pod.yaml 
pod/pod1 created
pod/pod2 created
[root@vms71 ~]#
[root@vms71 ~]# kubectl get pods -owide
NAME   READY   STATUS    RESTARTS   AGE        IP             NODE        
pod1   1/1     Running   0          14s   10.244.118.193   vms71.rhce.cc 
pod2   1/1     Running   0          14s   10.244.5.194     vms72.rhce.cc 
[root@vms71 ~]#

整个拓扑图应该如下。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_kubernetes_04

进入到pod1里去,然后通过traceroute访问 pod2的IP即 10.244.5.194。

[root@vms71 ~]# kubectl exec -it pod1 -- bash
[root@pod1 /]#
[root@pod1 /]# traceroute 10.244.5.194   # 请求vms72节点上的pod2
traceroute to 10.244.5.194 (10.244.5.194), 30 hops max, 60 byte packets
 1  192-168-26-71.kubernetes.default.svc.cluster.local (192.168.26.71)  0.049 ms  0.009 ms  0.007 ms
 2  10.244.5.192 (10.244.5.192)  0.477 ms  0.414 ms  0.378 ms  # 先到达vms72节点上的tunl0
 3  10.244.5.194 (10.244.5.194)  0.615 ms  0.577 ms  0.540 ms  # 再到达vms72节点上的pod2
[root@pod1 /]# exit
[root@vms71 ~]#

过程:从这里可以看到在pod1发出去的包,直接到达隧道另一端即vms72上的tunl0,然后再转发到pod2的。

那么vms71和vms72之间的linux网卡之间是怎么走的呢?

先查看vms71的路由。

[root@vms71 ~]# ip route  | grep tunl0
10.244.5.192/26 via 192.168.30.72 dev tunl0 proto bird onlink 
[root@vms71 ~]#

在192.168.30.71机器上,凡是去往网段10.244.5.192/26的数据包都发送到192.168.30.72这个IP地址去,这个192.168.30.72 IP地址 就是 vms72的eth0网卡。所以说,在 192.168.30.72 这个机器上,隧道 tunl0 连接的 eth0 网卡,即 calico-node yaml 配置指定生效。

查看vms72的路由。

[root@vms72 ~]# ip route  | grep tunl0
10.244.118.192/26 via 192.168.30.71 dev tunl0 proto bird onlink 
[root@vms72 ~]#

在192.168.30.72机器上,凡是去往网段10.244.118.192/26的数据包都发送到192.168.30.71这个IP地址,这个192.168.30.71 IP地址 就是 vms71的ens33网卡。所以说,在 192.168.30.71 这个机器上,隧道 tunl0 连接的 ens33 网卡,即 calico-node yaml 配置指定生效。

删除pod1和pod2 (删除两个pod之后,两个机器上的两个calixxx也会自动消失)

[root@vms71 ~]# kubectl delete -f pod.yaml 
pod "pod1" deleted
pod "pod2" deleted
[root@vms71 ~]#

删除calico。

[root@vms71 ~]# kubectl delete -f calico.yaml 
configmap "calico-config" deleted
	...输出...
[root@vms71 ~]#

第二种情况 指定网卡2(白名单配置法)

修改calico.yaml的内容,修改IP_AUTODETECTION_METHOD的值。

- name: IP_AUTODETECTION_METHOD
  value: "interface=ens32,eth1"

这里指定tunl0所使用的物理线路是ens32或者eth1,vms71上有ens32所以选择ens32,vms72上有eth1则选择eth1。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_IP_05

安装calico。

[root@vms71 ~]# kubectl apply -f calico.yaml
configmap/calico-config created
	...输出...
[root@vms71 ~]#

创建pod。

[root@vms71 ~]# kubectl apply -f pod.yaml 
pod/pod1 created
pod/pod2 created
[root@vms71 ~]#
[root@vms71 ~]# kubectl get pods -owide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE 
pod1   1/1     Running   0          3s    10.244.118.193   vms71.rhce.cc 
pod2   1/1     Running   0          3s    10.244.5.194     vms72.rhce.cc  
[root@vms71 ~]#

两个pod的IP保持了之前一样。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_kubernetes_06

进入到pod1里去,然后通过traceroute访问 pod2的IP即 10.244.5.194。

[root@vms71 ~]# kubectl exec -it pod1 -- bash
[root@pod1 /]# traceroute 10.244.5.194   # 请求vms72节点上的pod2
traceroute to 10.244.5.194 (10.244.5.194), 30 hops max, 60 byte packets
 1  192-168-26-71.kubernetes.default.svc.cluster.local (192.168.26.71)  0.050 ms  0.009 ms  0.007 ms
 2  10.244.5.192 (10.244.5.192)  0.493 ms  0.407 ms  0.365 ms  # 先到达vms72节点上的tunl0
 3  10.244.5.194 (10.244.5.194)  0.482 ms  0.468 ms  0.461 ms  # 先到达vms72节点上的pod2
[root@pod1 /]# exit
exit
[root@vms71 ~]#

查看vms71的路由。

[root@vms71 ~]# ip route  | grep tunl0
10.244.5.192/26 via 192.168.26.72 dev tunl0 proto bird onlink 
[root@vms71 ~]#

凡是去往网段10.244.5.192/26的数据包都发送到192.168.26.72(这是vms72的eth1接口,也是隧道tunl0的另一端)。

查看vms72的路由。

[root@vms72 ~]# ip route  | grep tunl0
10.244.118.192/26 via 192.168.26.71 dev tunl0 proto bird onlink 
[root@vms72 ~]#

凡是去往网段10.244.118.192/26的数据包都发送到192.168.26.71(这是vms71的ens32接口,也是隧道tunl0的另一端)。

删除pod1和pod2。

[root@vms71 ~]# kubectl delete -f pod.yaml 
pod "pod1" deleted
pod "pod2" deleted
[root@vms71 ~]#

删除calico。

[root@vms71 ~]# kubectl delete -f calico.yaml 
configmap "calico-config" deleted
	...输出...
[root@vms71 ~]#

小结:第二种情况和第一种情况,一模一样,无非就是换一张网卡绑定 tunl0 。

第三种情况 跳过指定网卡(黑名单配置法)

修改calico.yaml的内容,修改IP_AUTODETECTION_METHOD的值。

- name: IP_AUTODETECTION_METHOD
  value: "skip-interface=eth1,ens32"

tunl0选择物理网卡时跳过eth1或者ens32。在vms71上跳过ens32的话只能选择ens33,在vms72上跳过eth1的话只能选择eth0。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_容器_07

安装calico。

[root@vms71 ~]# kubectl apply -f calico.yaml
configmap/calico-config created
	...输出...
[root@vms71 ~]#

先查看vms71的路由

[root@vms71 ~]# ip route  | grep tunl0
10.244.5.192/26 via 192.168.30.72 dev tunl0 proto bird onlink 
[root@vms71 ~]#

凡是去往网段10.244.5.192/26的数据包都发送到192.168.30.72(这是vms72的eth0接口,也是隧道tunl0的另一端)。

先查看vms72的路由

[root@vms72 ~]# ip route  | grep tunl0
10.244.118.192/26 via 192.168.30.71 dev tunl0 proto bird onlink 
[root@vms72 ~]#

凡是去往网段10.244.118.192/26的数据包都发送到192.168.30.71(这是vms71的ens33接口,也是隧道tunl0的另一端)。

删除calico。

[root@vms71 ~]# kubectl delete -f calico.yaml 
configmap "calico-config" deleted
	...输出...
[root@vms71 ~]#

第四种情况 通过网络连通性判断(指定IP)

修改calico.yaml的内容,修改IP_AUTODETECTION_METHOD的值。

- name: IP_AUTODETECTION_METHOD
  value: "can-reach=192.168.30.1"

tunl0要选择能和192.168.30.1通信的那张网卡,在vms71上ens33网卡和192.168.30.1是同一个网段,能够和192.168.30.1通信,被选中。在vms72上eth0网卡和192.168.30.1是同一网段可以通信,被选中。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_kubernetes_08

创建calico。

[root@vms71 ~]# kubectl apply -f calico.yaml
configmap/calico-config created
	...输出...
[root@vms71 ~]#

先查看vms71的路由

[root@vms71 ~]# ip route  | grep tunl0
10.244.5.192/26 via 192.168.30.72 dev tunl0 proto bird onlink 
[root@vms71 ~]#

凡是去往网段10.244.5.192/26的数据包都发送到192.168.30.72(这是vms72的eth0接口,也是隧道tunl0的另一端)。

先查看vms72的路由

[root@vms72 ~]# ip route  | grep tunl0
10.244.118.192/26 via 192.168.30.71 dev tunl0 proto bird onlink 
[root@vms72 ~]#

凡是去往网段10.244.118.192/26的数据包都发送到192.168.30.71(这是vms71的ens33接口,也是隧道tunl0的另一端)。

删除calico。

[root@vms71 ~]# kubectl delete -f calico.yaml 
configmap "calico-config" deleted
	...输出...
[root@vms71 ~]#

第五种情况 通过cidr判断

修改calico.yaml的内容,修改IP_AUTODETECTION_METHOD的值。

- name: IP_AUTODETECTION_METHOD
  value: "cidr=192.168.26.0/24"

tunl0选择选择192.168.26.0/24网段的网卡,vms71选中的是ens32,vms72上选中的应该是eth1了,因为他们都是属于192.168.26.0/24网段的。

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_容器_09

创建calico。

[root@vms71 ~]# kubectl apply -f calico.yaml
configmap/calico-config created
	...输出...
[root@vms71 ~]#

查看vms71的路由。

[root@vms71 ~]# ip route  | grep tunl0
10.244.5.192/26 via 192.168.26.72 dev tunl0 proto bird onlink 
[root@vms71 ~]#

凡是去往网段10.244.5.192/26的数据包都发送到192.168.26.72(这是vms72的eth1接口,也是隧道tunl0的另一端)。

查看vms72的路由。

[root@vms72 ~]# ip route  | grep tunl0
10.244.118.192/26 via 192.168.26.71 dev tunl0 proto bird onlink 
[root@vms72 ~]#

凡是去往网段10.244.118.192/26的数据包都发送到192.168.26.71(这是vms71的ens32接口,也是隧道tunl0的另一端)。

删除calico。

[root@vms71 ~]# kubectl delete -f calico.yaml 
configmap "calico-config" deleted
	...输出...
[root@vms71 ~]#

总结

Calico网络通信:

  • 同一个节点上的宿主机到Pod通信: 宿主机 -> 目的Pod的calixxx -> 目的Pod
  • 同一个节点上的Pod到宿主机通信: 源Pod -> 源Pod的calixxx -> 宿主机
  • 同一个节点上的Pod通信: 源Pod -> 源Pod的calixxx -> 目的Pod的calixxx -> 目的Pod
  • 不同节点上的宿主机到Pod通信: 源机器上的tunl0绑定的网卡 -> 目的机器上的tunl0绑定的网卡 -> 目的机器上的tunl0 -> 目的Pod的calixxx -> 目的Pod
  • 不同节点上的Pod到宿主机通信: 源Pod -> 源Pod的calixxx -> 源机器上的tunl0 -> 源机器上的tunl0绑定的网卡 -> 目的机器上的tunl0绑定的网卡
  • 不同节点上的Pod通信: 源Pod -> 源Pod的calixxx -> 源机器上的tunl0 -> 源机器上的tunl0绑定的网卡 -> 目的机器上的tunl0绑定的网卡 -> 目的机器上的tunl0 -> 目的Pod的calixxx -> 目的Pod

整个过程中的网络可达性,以最复杂的不同节点上的Pod通信为例:

  • 源Pod -> 源Pod的calixxx :网络直达,无需解释,程序员不涉及
  • 源Pod的calixxx -> 源机器上的tunl0 : calico内部设计,不深究,程序员不涉及
  • 源机器上的tunl0 -> 源机器上的tunl0绑定的网卡:默认linux一个网卡就直接绑定(常见的eth0或ens33),linux多个网卡通过 calico-node yaml 配置指定
  • 源机器上的tunl0绑定的网卡 -> 目的机器上的tunl0绑定的网卡:linux基础,不同机器之间网络通信,route路由+firewalld/iptables防火墙
  • 目的机器上的tunl0绑定的网卡 -> 目的机器上的tunl0:默认linux一个网卡就直接绑定(常见的eth0或ens33),linux多个网卡通过 calico-node yaml 配置指定
  • 目的机器上的tunl0 -> 目的Pod的calixxx : calico内部设计,不深究,程序员不涉及
  • 目的Pod的calixxx -> 目的Pod :网络直达,无需解释,程序员不涉及

小结:对于不同node之间Pod通信,需要每个机器上的 tul0 网卡和 route 路由的支持。底层原因是calico通过ippool给不同的node分配了不同的 block块,每个block块是默认 64 个IP地址,就是 2的6次方个IP地址,就是 /26 ,就是 32-6= 26

Kubernetes_容器网络_Calico_04_Calico不同节点间Pod通信tunl0手动选择网卡_ico_10

不同的Node之间通信 就是不同Block直接通信,需要通过 tunl0 网卡的,并配置 route 路由转发。但是同一Node上的 Pod 之间通信,直接使用 calixxx 网卡,0.0.0.0 本机可达,不需要经过 tunl0 网卡,也不是本文讨论的内容,因为本文中的核心是 tunl0 网卡。