该案例是一名微信好友找到我,写出来时让读者以排查角度来加深 K8S 组件作用印象。
部署完成后,发现 calico-kube-controllers
日志报错:
$ kubectl -n calico-system get pod
NAME REDAY STATUS RESTARTS AGE
calico-kube-controllers-596576f79-j2pkgRunning 0/1 Running 3 (32s ago) 13m
calico-node-gqz7n 1/1 Running 0 34m
calico-node-tg9gr 1/1 Running 0 34m
calico-node-tg9gr 1/1 Running 0 34m
calico-typha-b8cccb6bb-nr4m5 1/1 Running 0 48m
calico-typha-b8cccb6bb-v9db21 1/1 Running 0 48m
csi-node-driver-6wkcb 2/2 Running 0 13m
csi-node-driver-gdc67 2/2 Running 0 13m
csi-node-driver-kb896 2/2 Running 0 13m
$ kubectl logs calico-kube-controllers-596576f7
2024-06-12 05:53:08.531 [INF0][1] main.go 107: Loaded configuratiocyWorkers:1, NodeWorkers:1, Kubeconfig:"", DatastoreType:"kubernetes"}
...
2024-06-12 05:53:38.561 [ERROR][1] client.go 295: Error getting cluster information config ClusterInformations="default" error=Get "https://10.96.0.1:443/apis/crd.projectcalico.org/v1/clusterinformations/default": dial tcp 10.96.0.1:443: io timeout
2024-06-12 05:53:38.561 [INF0][1] main.go 138: Failed to initializal datastore error=Get "https://10.96.0.1:443/apis/crd.projectcalico.org/v1/clusterinformations.default": dial tcp 10.96.0.1:443: i/o timeout
确认系统防火墙关了,然后 kube-proxy 也部署了,问题根因是 kuberentes Service https://10.96.0.1:443
不通,让在每台机器上直接 curl 来看看 Service 工作否:
$ curl -vk https://10.96.0.1
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}
有 http 内容返回,说明节点上 Service 的 DNAT 是正常没问题的,问题是 Pod 内访问不了 Service,也就是 DNAT 后的流量有问题 PodIP -> kube-apiserver:6443
不通,所以故障节点上直接绕过 Service 看下:
# 192.168.21.11 是一个 master 节点
$ tcpdump -nn -i any host 192.168.21.11
# 开启抓包后,进入容器的 network namespace 里 curl
$ crictl inspect 或者 docker inspect xx 获取 calico-kube-controllers /pause 容器的 Pid
$ nsenter --net --target <pid> curl -k https://192.168.21.11.6443
然后抓包显示
....
16:40:19.165180 calia9589d79281 In IP 192.168.236.10.59894 > 192.168.21.11.6443: Flags ...
16:40:19.165190 ens192 Out IP 192.168.236.10.59894 > 192.168.21.11.6443: Flags ...
16:40:23.357175 calia9589d79281 In IP 192.168.236.10.59894 > 192.168.21.11.6443: Flags ...
16:40:23.357195 ens192 In IP 192.168.236.10.59894 > 192.168.21.11.6443: Flags ...
从抓包看就是节点上没做 SNAT,包到 master 机器上后,目标 master 机器上回包走默认路由到网关,网关发出去,最后没回来。就让他看 nat 表的 MASQUERADE:
# 他发的截图,这里我只手打下关键信息并折叠方便阅读
$ iptables -w -t nat -S | grep MASQ
...
-A cali-POSTROUTING -o tunl0 -m comment --comment "cali:SXwvdsbh4Mw7wOln" \
-m addrtype !--src-type LOcAL --limit-iface-out -m addrtype LOCAL -j MASQUERADE --randomm-fully
-A cali-nat-outgoing -m comment --comment "cali:flqWnvo8yg4ULQLam" -m set --match-set cali40masg-ipam-pools src \
-m set ! --match-set cali40all-ipam-pools dst -j MASQUERADE --randomm-fully
从 iptables 规则看,calico 使用 ipset 匹配(因为可以多个 IPPool 地址池而不增加 iptables 规则)来做 SNAT 判断条件的,查看下这俩 ipset:
$ ipset list cali40masg-ipam-pools
Name: cali40masq-ipam-pools
Type: hash: net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x793b8a70
size in memory: 504
References: 1
Number of entries: 1
Members:
192.168.0.0/16
$ ipset list cali40all-ipam-pools
Name: cali40all-ipam-pools
Type: hash:net
Revision:7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0xaa98b84c
size in memory: 504
References: 1
Number of entries: 1Members:
192.168.0.0/16
上面 iptables 配合 ipset 字面意思就是:来源IP 192.168.0.0/16
访问 目标 IP 不是 192.168.0.0/16
的才做 SNAT,而目标 IP master 的 192.168.21.11
匹配到了所以没做 SNAT。
让他 /pause
网络里去 curl -I https://223.5.5.5
看看是不是通的,回答是通的,因为没匹配上面规则而做了 SNAT 而正常。
这里根因就是他把 calico 的 ippool 和宿主机重合了,宿主机网段是 192.168.21.0/24
,calico 默认网段是 192.168.0.0/16
。让他把 kubectl edit ippool default-ipv4-ippool
修改 cidr 后删下 Pod 就正常了。