Think Deep,Work Lean

K8s底层网络原理及故障排查

Posted on By zack

前言

网络底层经常为了要连接两个不同的namespace连通,经常的做法就是使用veth pair。如在虚机对应到宿主机上,容器对于宿主机等。或是自己创建一个namespace,要与它的网络连通等。

关于veth pair

首先要理解底层原理

  1. 安装ethtool
    yum install ethtool
    
  2. 找到相应的容器
    [root@node1 ~]# kubectl -n kube-system get po coredns-79c6f6447f-6f9xp -o yaml | grep -i id
    ...
      - containerID: docker://b79eb8591d3e671c0a97354e1d778a16a4380dffbd2cd58bac5c7ffb4b77917a
     imageID: docker-pullable://coredns/coredns@sha256:263d03f2b889a75a0b91e035c2a14d45d7c1559c53444c5f7abf3a76014b779d
    ...
    
  3. 去容器所在的node
    [root@node1 ~]# kubectl -n kube-system get po coredns-79c6f6447f-6f9xp -o wide
    NAME                       READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
    coredns-79c6f6447f-6f9xp   1/1     Running   0          41h   10.233.74.65   node4   <none>           <none>
    
  4. 查看容器的pid
    [root@node4 ~]# docker inspect b79eb8591d3e671c0a97354e1d778a16a4380dffbd2cd58bac5c7ffb4b77917a | grep -i pid
             "Pid": 20880,
    
  5. 确认下这个网卡是veth
    [root@node4 ~]# nsenter -n -t 20880 ip -d link show eth0
    4: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP mode DEFAULT group default
     link/ether b6:0b:be:96:5c:86 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
     veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    
  6. 查看该 eth0的对端
    [root@node4 ~]# nsenter -n -t 20880 ethtool -S eth0
    NIC statistics:
      peer_ifindex: 8
    
  7. 在宿主机上查看序号为8 的网卡
    [root@node4 ~]# ip link | less
    8: cali9a8d0827254@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP mode DEFAULT group default
     link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
    
  8. 因此,去往这个pod的路由,其实就是把数据发到这个网卡。这个ip就是Pod的IP
    [root@node4 ~]# ip r | grep cali9a8d0827254
    10.233.74.65 dev cali9a8d0827254 scope link
    

了解了这个,那么对于ping pod IP不通的网络问题的排查方法,自然而然就清晰了。

无法ping通pod ip的故障排查

思路:

  1. 首先在客户端长ping

  2. 在pod所在的主机上抓包
    tcpdump -i any arp or icmp -nn
    
  3. 如果包进入到了这个host上,接着在抓pod上的包

实战

  1. 首先看 nginx controller pod的日志,看到有如下报错:
    127.0.0.1 - [127.0.0.1] - - [22/Jun/2020:06:10:51 +0000] “GET / HTTP/1.1” 499 0 “-” “curl/7.61.1” 297 1.676 [test-productpage-9080] 10.233.75.9:9080 0 1.676 - ec4ba69a-2e7f-9278-b4af-5fc2bee8e387
    127.0.0.1 - [127.0.0.1] - - [22/Jun/2020:06:11:25 +0000] “GET / HTTP/1.1" 503 91 “-” “curl/7.61.1" 297 30.047 [test-productpage-9080] 10.233.75.9:9080 91 30.048 503 7ac4e901-967b-415c-9be5-0a619219e13a
    127.0.0.1 - [127.0.0.1] - - [22/Jun/2020:06:11:50 +0000] “GET / HTTP/1.1” 499 0 “-” “curl/7.58.0” 297 1.399 [test-productpage-9080] 10.233.75.9:9080 0 1.396 - f1a23193-6cb1-452f-a215-f43e353f7ac
    
  2. 看返回码是 499 503,503 容易理解是后端挂了或是网络不通。499 上网 google 后是说客户端手动终止导致的 http 终断。

  3. 这个时候要查下从 controller pod 来 ping 这个 ip,发现确实 ping 不通。

  4. 查了下这个 ip 是目标 pod ip。

  5. ipvsadm -Sn grep 看了下它的 service ip, 然后从这个 pod 里面 ping 这个 service ip 发现能通,但其实没有什么用,不要受它的干扰,致于为什么能通,原因还不是很清楚。
  6. 从源宿主机上 ping 这个 pod ip 也不通,此时就是不正常的。注意一般来讲 node 能 ping 通所有的 pod 的IP。

  7. 从目标宿主机上 ping 这个 pod 的 ip 能 ping 通。注意所有的 pod 所在的 宿主机都能 ping 通它上面的 pod

  8. 在源宿主机上一直 ping 目标 ip,现在包的走向应该是 源宿主机 → 目标宿主机 → 目标 pod,现在的问题是源宿主机到目标宿主机能通,目标宿主机到目标 pod 能通,但是源宿主机到目标 pod 不通。此时需要抓包来看:
    #在目标宿主机上:
    # tcpdump -qenc 4 -i any arp or icmp  # 此时能收到包
    # ip r get 10.233.71.54
    10.233.71.54 dev califefebb442cd src 192.168.0.7 uid 0  # 找到网卡
    # tcpdump -qenc 4 -i califefebb442cd icmp or arp  # 抓下这张网卡的包,发现丢包了。
    # iptables -t filter -F  # 清空下iptabels filter 表中的所有规则,发现通了,但是过一会儿又全部加上了。
    
  9. 此时问题就明朗了,说明是可能是有网络策略。查下 ws project 下的 network policy,发现确实有,把它关闭就好了。

  10. 为什么有网络策略问题呢?
    原因是 ingress contrller 是在默认的 system-workspace 下面,而test 这个 Project 是在 demo 的 workspace 下面,网络策略开启后,只允许出不允许进,因此从 ingress controller 这个 pod 上面无法 ping 通到该 pod。
    #其实在目标主机上把包过滤掉了,可以直接 iptables 看包,可以看到
    root@node3:~# watch -n 0.5 -d "iptables -t filter -vS | grep califefebb442cd"
    

    注意: 如果一直 Ping 着包是通的,然后把网络隔离打开后,Ping 包不会断,因为已经建立着的连接不会跟你断掉,但是断开一下,然后再 ping 就会把你的包拒绝。