问题:今天同事安装了一套k8s环境, 结果发现calico网络互相访问不通,同台机器上的pod之间的calico ip都ping不通
分析解决:
calico支持三种网络模式,可通过修过calico.yaml进行配置:
- overlay之ipip
- overlay之vxlan
- underlay之BGP
overlay — ipip
traffic flow
overlay — vxlan
traffic flow
underlay — BGP
traffic flow
本环境用的是CrossSubnet且都在同网段,用的是BGP
下面是node信息:
1 2 3 4 5 |
[root@localhost ~]# kubectl get no -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME yks1 Ready control-plane,ingress,master,node,system 22h v1.20.15 10.16.245.46 <none> NFSChina Server 4.0 (G196) 4.19.113-14.nfs4.x86_64 docker://20.10.8 yks2 Ready control-plane,ingress,master,node,system 22h v1.20.15 10.16.245.47 <none> NFSChina Server 4.0 (G196) 4.19.113-14.nfs4.x86_64 docker://20.10.8 yks3 Ready ingress,node,system 22h v1.20.15 10.16.245.65 <none> NFSChina Server 4.0 (G196) 4.19.113-14.nfs4.x86_64 docker://20.10.8 |
查看yks2上的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@yks3 ~]# ip r default via 10.16.245.1 dev ens3 proto dhcp metric 100 10.16.245.0/24 dev ens3 proto kernel scope link src 10.16.245.65 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 172.23.27.0/24 via 10.16.245.47 dev ens3 proto bird 172.23.39.0/24 via 10.16.245.34 dev ens3 proto bird blackhole 172.23.119.0/24 proto bird 172.23.119.19 dev cali112980e2900 scope link 172.23.119.20 dev cali7ad105f6052 scope link 172.23.119.21 dev cali850bd215908 scope link 172.23.119.22 dev cali014e534f978 scope link 172.23.119.23 dev cali90505cacde3 scope link 172.23.119.24 dev cali1e38a050268 scope link 172.23.119.25 dev cali55015378d77 scope link 172.23.119.26 dev caliad0c2e2531c scope link 172.23.255.0/24 via 10.16.245.46 dev ens3 proto bird |
可以看到, 172.23.27.0/24 via 10.16.245.47 dev ens3 proto bird 这条路由,172.23.27.0/24这个地址是直接到yks2主机上的
1 2 |
yks nfs-server-fc7d676c9-8dkq5 1/1 Running 0 21h 172.23.119.4 yks3 kube-system coredns-6d7c6648db-4stmp 1/1 Running 0 23h 172.23.27.2 yks2 |
在yks2上ping coredns:
1 2 |
[root@yks1 ~]# ping 172.23.27.2 PING 172.23.27.2 (172.23.27.2) 56(84) bytes of data. |
上主机yks2上抓包:
首先抓宿主机网卡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@yks2 ~]# tcpdump -i ens3 icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes 15:42:16.286242 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 20, length 64 15:42:17.310198 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 21, length 64 15:42:18.334192 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 22, length 64 15:42:19.358197 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 23, length 64 15:42:20.382236 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 24, length 64 15:42:21.410206 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 25, length 64 15:42:22.430279 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 26, length 64 15:42:23.454333 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 27, length 64 15:42:24.478138 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 28, length 64 15:42:25.502182 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 29, length 64 15:42:26.526196 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 30, length 64 15:42:27.550195 IP yks1.cluster.local > 172.23.27.2: ICMP echo request, id 29208, seq 31, length 64 ^C 12 packets captured 13 packets received by filter 0 packets dropped by kernels |
可以接收到包
看下yks2的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@yks2 ~]# ip r default via 10.16.245.1 dev ens3 proto dhcp metric 100 10.16.245.0/24 dev ens3 proto kernel scope link src 10.16.245.47 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 172.18.0.0/16 dev br-34de7dda2079 proto kernel scope link src 172.18.0.1 blackhole 172.23.27.0/24 proto bird 172.23.27.1 dev calid894dc87e68 scope link 172.23.27.2 dev calif945d263abc scope link 172.23.27.3 dev califc5d5492c2d scope link 172.23.27.5 dev cali420874a7614 scope link 172.23.27.7 dev calif2ef5efbec8 scope link 172.23.27.8 dev calif508601809c scope link 172.23.27.9 dev calida55188c614 scope link 172.23.27.10 dev cali3e6ad545e92 scope link 172.23.27.11 dev cali2c5cdc71fac scope link 172.23.27.12 dev cali1be0e00985c scope link 172.23.27.13 dev calif00b7a22fc0 scope link 172.23.27.14 dev cali56eb38c7099 scope link 172.23.39.0/24 via 10.16.245.34 dev ens3 proto bird 172.23.119.0/24 via 10.16.245.65 dev ens3 proto bird 172.23.255.0/24 via 10.16.245.46 dev ens3 proto bird |
172.23.27.2 dev calif945d263abc scope link 是calif945d263abc网上接收的
抓calif945d263abc:
1 2 3 4 5 6 7 |
[root@yks2 ~]# tcpdump -i calif945d263abc icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on calif945d263abc, link-type EN10MB (Ethernet), capture size 262144 bytes ^C 0 packets captured 0 packets received by filter 0 packets dropped by kernel |
没有收到请求
可以看到,是宿主机网卡到calixxx网络出现了问题
查看yks2的iptables规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@yks2 ~]# iptables-save ... ... *filter :INPUT ACCEPT [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] :DOCKER - [0:0] :DOCKER-ISOLATION-STAGE-1 - [0:0] :DOCKER-USER - [0:0] :DOCKER-ISOLATION-STAGE-2 - [0:0] :KUBE-FIREWALL - [0:0] :KUBE-KUBELET-CANARY - [0:0] :KUBE-PROXY-CANARY - [0:0] :KUBE-EXTERNAL-SERVICES - [0:0] :KUBE-SERVICES - [0:0] :KUBE-FORWARD - [0:0] -A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES -A INPUT -j KUBE-FIREWALL -A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD -A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION-STAGE-1 ... ... |
有条:FORWARD DROP [0:0]规则
可以从对应版本的docker源码中找到线索
通过源码可以看出来,当内核参数 net.ipv4.ip_forward 的值不为1时,docker daemon会默认把iptables的filter表的FORWARD链默认设置为DROP。
解决方法
1. 外围的解决方法
我们自己可以在外围这么修改systemd的service脚本
1 2 3 4 5 6 |
vi /lib/systemd/system/docker.service 。。。 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID ExecStartPost=/usr/sbin/iptables -P FORWARD ACCEPT 。。。 |
其实主要是加最后那句 ExecStartPost=/usr/sbin/iptables -P FORWARD ACCEPT
2. 推荐的解决方法
先设置如下两个关键的内核参数并让其生效:
1 2 3 4 5 6 |
cat <<EOF > /etc/sysctl.d/docker.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system |
之后再安装docker,然后启动即可。这里的关键是,安装或者首次启动docker前,linux操作系统需要设置内核参数 net.ipv4.ip_forward = 1 才可以。
附:calico组网的原理如下:
Calico 是一个纯三层的数据中心网络方案,而且无缝集成像 OpenStack 这种 Iaas 云架构,能够提供可控的 VM、容器、裸机之间的 IP 通信。为什么说它是纯三层呢?因为所有的数据包都是通过路由的形式找到对应的主机和容器的,然后通过 BGP 协议来将所有路由同步到所有的机器或数据中心,从而完成整个网络的互联。
简单来说,Calico 在主机上创建了一堆的 veth pair,其中一端在主机上,另一端在容器的网络命名空间里,然后在容器和主机中分别设置几条路由,来完成网络的互联。
任意选择 k8s 集群中的一个节点作为实验节点,进入容器 A,查看容器 A 的 IP 地址:
1 2 3 4 5 6 7 8 9 10 11 |
bash-5.0# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 4: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1480 qdisc noqueue state UP link/ether 92:dc:35:6a:c7:0f brd ff:ff:ff:ff:ff:ff inet 172.23.119.24/32 brd 172.23.119.24 scope global eth0 valid_lft forever preferred_lft forever |
这里容器获取的是 /32 位主机地址,表示将容器 A 作为一个单点的局域网。
容器A的路由:
1 2 3 |
bash-5.0# ip route default via 169.254.1.1 dev eth0 169.254.1.1 dev eth0 scope link |
从路由表可以知道 169.254.1.1 是容器的默认网关,当一个数据包的目的地址不是本机时,就会查询路由表,从路由表中查到网关后,它首先会通过 ARP
获得网关的 MAC 地址,然后在发出的网络数据包中将目标 MAC 改为网关的 MAC,而网关的 IP 地址不会出现在任何网络包头中。也就是说,没有人在乎这个 IP 地址究竟是什么,只要能找到对应的 MAC 地址,能响应 ARP 就行了。可以通过 ip neigh 命令查看一下本地的 ARP 缓存:
1 2 3 |
bash-5.0# ip neigh 10.16.245.65 dev eth0 lladdr ee:ee:ee:ee:ee:ee used 0/0/0 probes 0 STALE 169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee ref 1 used 0/0/0 probes 1 REACHABLE |
这个 MAC 地址应该是 Calico 硬塞进去的,而且还能响应 ARP。但它究竟是怎么实现的呢?
我们先来回想一下正常情况,内核会对外发送 ARP 请求,询问整个二层网络中谁拥有 169.254.1.1 这个 IP 地址,拥有这个 IP 地址的设备会将自己的 MAC
地址返回给对方。但现在的情况比较尴尬,容器和主机都没有这个 IP 地址,甚至连主机上的端口 calicba2f87f6bb,MAC 地址也是一个无用的 ee:ee:ee:ee:ee:ee。按道理容器和主机网络根本就无法通信才对呀!所以 Calico 是怎么做到的呢?实际上Calico 利用了网卡的代理 ARP 功能。代理 ARP 是 ARP 协议的一个变种,当 ARP 请求目标跨网段时,网关设备收到此 ARP 请求,会用自己的 MAC 地址返回给请求者,这便是代理 ARP(Proxy ARP)
1。Calico 通过一个巧妙的方法将 workload 的所有流量引导到一个特殊的网关 169.254.1.1,从而引流到主机的 calixxx 网络设备上,最终将二三层流量全部转换成三层流量来转发。
2。在主机上通过开启代理 ARP 功能来实现 ARP 应答,使得 ARP 广播被抑制在主机上,抑制了广播风暴,也不会有 ARP 表膨胀的问题。
参考:https://icloudnative.io/posts/poke-calicos-lies/
https://singhwang.github.io/2019/10/02/docker_trouble_shooting_001/
https://blog.csdn.net/fengcai_ke/article/details/125716634
0 Comments