区块链技术

动手排查Kubernetes网络故障之旅

本文主要是介绍动手排查Kubernetes网络故障之旅,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在开发 Kata/远程超主机(remote-hypervisor,也称为 peer-pods)的过程中,我遇到了一个问题,即 Kubernetes 的 pod IP 从工作节点(worker node)不可达。在这篇博客中,我将分享我的 Kubernetes 网络排查经历,希望这能帮助我的读者们。

Kata 远程 hypervisor(peer-pods)方式允许在任何基础设施环境中通过利用环境自带的基础设施管理 API(例如 AWS 或 Microsoft Azure 的 API)创建 Kata 虚拟机,分别在 AWS 或 Azure 上创建 Kata 虚拟机时使用这些 API。CNCF confidential containers 项目中的 cloud-api-adaptor 子项目实现了 Kata 远程 hypervisor 的功能。

如下图所示,在 peer-pods 方法中,pod(Kata)VM 运行在 K8s 工作节点之外,而 pod IP 可以通过 VXLAN 隧道从工作节点访问到。使用隧道确保 pod 网络继续按原样运行,而无需更改 CNI 网络。

使用 Kata 容器时,Kubernetes 中的 pod 在一个虚拟机中运行,因此我们称运行 pod 的虚拟机为 Kata VM。

问题:

Pod IP 10.132.2.46 无法从工作节点VM(IP: 192.168.10.163)访问,它位于Pod VM(IP: 192.168.10.201)上。

以下是我的环境中VM的详情如下——包含工作节点的VM和pod(Kata)VM。OVN-Kubernetes是我们使用的Kubernetes CNI插件。

    +===========================+================+================+  
    |          VM名称           |   IP地址       |    说明        |  
    +===========================+================+================+  
    | ocp-412-ovn-worker-1      | 192.168.10.163 | 工作节点VM     |  
    +---------------------------+----------------+----------------+  
    | podvm-nginx-priv-8b726648 | 192.168.10.201 | Pod VM        |  
    +---------------------------+----------------+----------------+

最简单的解决方法本应是向网络专家求助来解决这个问题。然而,在我的情况下,由于其他紧急的任务,专家们无法直接处理这个问题。此外,这种同级节点的网络拓扑是新的,并涉及多个网络栈——Kubernetes CNI、Kata网络和VXLAN隧道,这使得找出根本原因变得困难且耗时。

于是,我抓住这个机会来提高我的 Kubernetes 网络技术,开始在一些 Linux 网络专家的指导下独立学习。

接下来的几节中,我将带领你了解我的调试过程及找到问题根源的方法。这次调试步骤在排查Kubernetes网络问题时能帮到你一些。

故障排除 — 第一阶段

总的来说,我采取的方法如下,分为两个步骤。

  1. 理解网络拓扑结构
  2. 从拓扑中找出问题的部分

让我们从工作节点虚拟机 ping 这个 IP 地址 10.132.2.46 并跟踪数据包在网络栈中的路径。

运行ping命令来测试10.132.2.46的网络连接
[root@ocp-412-worker-1 core]# ping 10.132.2.46

Linux 会根据路由表来决定将这个数据包(packet)发送到哪里。

[root@ocp-412-worker-1 core]# ip route get 10.132.2.46  
10.132.2.46 dev ovn-k8s-mp0 src 10.132.2.2 uid 0  
(注:这里的输出显示了从10.132.2.2到10.132.2.46的路由信息)

所以,通往 pod IP 的路由是通过 ovn-k8s-mp0 接口

让我们先获取工作节点的网络详情,并查一下ovn-k8s-mp0设备的信息。

[root@ocp-412-ovn-worker-1 core]# ip r  
默认网关 via 192.168.10.1 dev br-ex proto dhcp src 192.168.10.163 metric 48  
10.132.0.0/14 via 10.132.2.1 dev ovn-k8s-mp0  
10.132.2.0/23 dev ovn-k8s-mp0 proto kernel scope link src 10.132.2.2  
169.254.169.0/29 dev br-ex proto kernel scope link src 169.254.169.2  
169.254.169.1 dev br-ex src 192.168.10.163 mtu 1400  
169.254.169.3 via 10.132.2.1 dev ovn-k8s-mp0  
172.30.0.0/16 via 169.254.169.4 dev br-ex mtu 1400  
192.168.10.0/24 dev br-ex proto kernel scope link src 192.168.10.163 metric 48  

[root@ocp-412-ovn-worker-1 core]# ip a  

[省略]  

2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP group default qlen 1000  
    link/ether 52:54:00:f9:70:58 brd ff:ff:ff:ff:ff:ff  
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000  
    link/ether 32:7c:7a:20:6e:5a brd ff:ff:ff:ff:ff:ff  
4: genev_sys_6081: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65000 qdisc noqueue master ovs-system state UNKNOWN group default qlen 1000  
    link/ether 3a:9c:a8:4e:15:0c brd ff:ff:ff:ff:ff:ff  
    inet6 fe80::389c:a8ff:fe4e:150c/64 scope link  
       有效时间:永久 首选时间:永久  
5: br-int: <BROADCAST,MULTICAST> mtu 1400 qdisc noop state DOWN group default qlen 1000  
    link/ether d2:b6:67:15:ef:06 brd ff:ff:ff:ff:ff:ff  
6: ovn-k8s-mp0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UNKNOWN group default qlen 1000  
    link/ether ee:cb:ed:8e:f9:e0 brd ff:ff:ff:ff:ff:ff  
    inet 10.132.2.2/23 brd 10.132.3.255 scope global ovn-k8s-mp0  
       有效时间:永久 首选时间:永久  
    inet6 fe80::eccb:edff:fe8e:f9e0/64 scope link  
       有效时间:永久 首选时间:永久  
8: br-ex: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000  
    link/ether 52:54:00:f9:70:58 brd ff:ff:ff:ff:ff:ff  
    inet 192.168.10.163/24 brd 192.168.10.255 scope global dynamic 无前缀路由 br-ex  
       有效时间:2266秒 首选时间:2266秒  
    inet 169.254.169.2/29 brd 169.254.169.7 scope global br-ex  
       有效时间:永久 首选时间:永久  
    inet6 fe80::17f3:957b:5e8d:a4a6/64 scope link 无前缀路由  
       有效时间:永久 首选时间:永久  

[省略]

如上所示,ovn-k8s-mp0 接口的 IP 为 10.132.2.2/23

我们来看看 ovn-k8s-mp0 接口的细节。

如下所示的输出,这个接口是OVS的一个实体(OVS,开放虚拟交换机)。

    [root@ocp-412-ovn-worker-1 core]# ip -d li sh dev ovn-k8s-mp0  
    6: ovn-k8s-mp0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue 状态:未知 模式:默认 组 default 队列长度  
    以太网链路 ee:cb:ed:8e:f9:e0 广播地址 ff:ff:ff:ff:ff:ff 混杂模式 1 minmtu 68 maxmtu 65535  
    openvswitch 地址生成模式 eui64 发送队列数 1 接收队列数 1 最大gso大小 65536 最大gso段数 65535

ovn-k8s-mp0 是不是 OVS 桥?

注:根据上下文决定是否需要将 "OVS" 解释为 "开放虚拟交换机"。此处保持原样。

从下面的命令输出来看,ovn-k8s-mp0 不是一个 OVS 桥。工作节点上仅存在两个桥,分别是 br-exbr-int

[root@ocp-412-ovn-worker-1 core]# ovs-vsctl list-br  
br-ex  
br-int
# 此命令列出 Open vSwitch 中的桥接器,输出为 br-ex 和 br-int。

因此**ovn-k8s-mp0**是一个OVS端口。我们需要找到拥有此端口的OVS桥接。

从下面的命令输出中可以看出,ovn-k8s-mp0 并不是为桥 br-ex 设计的 OVS 端口之一。

[root@ocp-412-ovn-worker-1 core]# ovs-ofctl dump-ports br-ex ovn-k8s-mp0  
ovs-ofctl: br-ex: 未知端口 `ovn-k8s-mp0` 不存在

从下面的命令输出可以看出,ovn-k8s-mp0 是桥 br-int 上的一个 OVS 端口。

    [root@ocp-412-ovn-worker-1 core]# ovs-ofctl dump-ports br-int ovn-k8s-mp0  
    OFPST_PORT 消息 (xid=0x4): 1 个端口  
    端口 "ovn-k8s-mp0": rx 包=1798208, 字节量=665641420, 丢弃=2, 错误=0, 帧=0, 超长包=0, CRC 错误=0, tx 包=2614471, 字节量=1357528110, 丢弃=0, 错误=0

**简而言之,ovn-k8s-mp0br-int OVS 桥上的一个端口,其 IP 地址为 10.132.2.2/23

现在,咱们来看看这个 pod 的网络配置。

要了解pod的网络详情,首先需要知道pod的网络命名空间。下面的命令通过其IP地址找到pod的网络命名空间。

[root@ocp-412-ovn-worker-1 core]# POD_IP=10.132.2.46; for ns in $(ip netns ls | cut -f 1 -d " "); do ip netns exec $ns ip a | grep -q $POD_IP; status=$?; [ $status -eq 0 ] && echo "pod 命名空间: $ns" ; done  

pod 命名空间: c16c7a01-1bc5-474a-9eb6-15474b5fbf04

一旦确定pod的网络命名空间,就可以找到pod的网络配置详情,如下。

    [root@ocp-412-ovn-worker-1 core]# NS=c16c7a01–1bc5–474a-9eb6–15474b5fbf04  
    [root@ocp-412-ovn-worker-1 core]# ip netns exec $NS ip a  
    1: lo: <LOOPBACK, UP, LOWER_UP> mtu 65536 qdisc noqueue 状态 未知 组 默认 队列长度 1000  
     链路/本地回环 00:00:00:00:00:00 广播 00:00:00:00:00:00  
     inet 127.0.0.1/8 范围 主机 lo  
     有效时间 永久 优先时间 永久  
     inet6 ::1/128 范围 主机  
     有效时间 永久 优先时间 永久  
    2: eth0@if4256: <BROADCAST, MULTICAST, UP, LOWER_UP> mtu 1400 qdisc noqueue 状态 启用 组 默认 队列长度 1000  
     链路/以太网 0a:58:0a:84:02:2e 广播 ff:ff:ff:ff:ff:ff 链接-netns 59e250f6–0491–4ff4-bb22-baa3bca249f6  
     inet 10.132.2.46/23 范围 全局 eth0  
     有效时间 永久 优先时间 永久  
     inet6 fe80::858:aff:fe84:22e/64 范围 链路  
     有效时间 永久 优先时间 永久  
    4257: vxlan1@if4257: <BROADCAST, MULTICAST, UP, LOWER_UP> mtu 1500 qdisc noqueue 状态 未知 组 默认 队列长度 1000  
     链路/以太网 ca:40:81:86:fa:73 广播 ff:ff:ff:ff:ff:ff 链接-netns 59e250f6–0491–4ff4-bb22-baa3bca249f6  
     inet6 fe80::c840:81ff:fe86:fa73/64 范围 链路  
     有效时间 永久 优先时间 永久  

    [root@ocp-412-ovn-worker-1 core]# ip netns exec $NS ip r  
    default 经 10.132.2.1 设备 eth0  
    10.132.2.0/23 设备 eth0 协议 内核 范围 链路 源 10.132.2.46

所以这个主要网络接口就是 eth0@if4256,它是为 pod 所准备的。

我们来看看 eth0,这个设备的详细信息.

如以下输出所示,在pod网络命名空间中的eth0设备实际上是一个虚拟以太网设备(veth设备)。

[root@ocp-412-ovn-worker-1 core]# ip netns exec $NS ip -d li sh dev eth0  
以太网链路 ether 0a:58:0a:84:02:2e 广播地址 ff:ff:ff:ff:ff:ff 链路网络命名空间 59e250f6-0491-4ff4-bb22-baa3bca249f6  
veth 地址生成模式 eui64 发送队列 8 接收队列 8 最大GSO大小 65536 最大GSO段数 65535 最大TSO大小 524280 最大TSO段数 65535 最大GRO大小 65536

众所周知,veth设备通常成对出现;一端位于init(或根)命名空间中,另一端位于pod网络命名空间。

让我们在 init 命名空间里找到 pod 对应的 veth 设备对。

[root@ocp-412-ovn-worker-1 core]# ip a | grep -A1 ^4256  (注释:该命令用于查询网络接口,输出包含4256的行及其下一行)
4256: 8b7266486ea2861@if2: <广播,多播,启用,底层启用> 最大传输单元 1400 队列调度器 noqueue 主ovs-system 状态为启用 group 默认组 (例如:接口4256的详细信息,如其MAC地址等)
link/ether de:fb:3e:87:0f:d6 brd ff:ff:ff:ff:ff:ff link-netns c16c7a01–1bc5–474a-9eb6–15474b5fbf04 (例如:MAC地址为de:fb:3e:87:0f:d6,广播地址为ff:ff:ff:ff:ff:ff,关联的网络命名空间的唯一标识符)

所以,8b7266486ea2861@if2 是 init 命名空间中 pod 的 veth 设备端。**veth**对连接了 init 和 pod 的网络名空间。

让我们查一下 veth 设备端点的详情。

[root@ocp-412-ovn-worker-1 core]# ip -d li sh dev 8b7266486ea2861  
4256: 8b7266486ea2861@if2: <广播,多播,UP,LOWER_UP> mtu 1400 qdisc noqueue master ovs-system 状态UP 模式默认 组默认  
以太网链路 de:fb:3e:87:0f:d6 brd ff:ff:ff:ff:ff:ff 链接网络命名空间 c16c7a01–1bc5–474a-9eb6–15474b5fbf04 侦听模式 1 最小MTU 68 最大MTU 65535  
veth  
openvswitch从设备 地址生成模式为EUI-64 发送队列数量为4 接收队列数量为4 GSO最大大小为65536字节 GSO最大分段数为65535

因此,8b7266486ea2861@if2是一个OVS实体。查看OVS交换机的详细信息可以提供哪个OVS桥接器拥有该端口的详情。

如以下输出所示,桥 br-int 包含端口。

注意,使用 ovs-vsctl 命令是之前命令 ovs-ofctl dump-ports <bridge> <port> 的一个替代命令。这表明不同的命令可以帮助探索网络拓扑。

[root@ocp-412-ovn-worker-1 core]# ovs-vsctl show  

[snap]  

桥 br-int  
    故障模式:安全  
    数据路径类型:系统  

    [snip]  

    端口 "8b7266486ea2861"  
        接口 "8b7266486ea2861"  

[snap]

所以**br-int**拥有包含**veth**端点的端口,该端口位于init命名空间中。同时,也持有**ovn-k8s-mp0**端口。

让我们查一下 pod 的 vxlan 相关信息。

如下所示的输出,vxlan 隧道的远程端点是 IP 192.168.10.201,这是 pod 虚拟机的 IP。

    [root@ocp-412-ovn-worker-1 core]# ip netns exec $NS ip -d li sh dev vxlan1  
    4257: vxlan1@if4257: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue 状态 未知 模式 默认 组 默认 队列长度 1000  
    链路/以太网 ca:40:81:86:fa:73 brd ff:ff:ff:ff:ff:ff 链接-netns 59e250f6–0491–4ff4-bb22-baa3bca249f6 混杂模式 0 最小MTU 68 最大MTU 65535  
    vxlan ID 555005, 远程 192.168.10.201, 源端口 0 0, 目标端口 4789, 禁止学习, ttl 自动设置, 年龄 300, udpcsum, 禁用udp6zerocsumtx, 禁用udp6zerocsumrx, 地址生成模式 eui64, 发送队列大小 1, 接收队列大小 1, GSO 最大大小 65536, GSO 最大段数 65535

一个问题就是如何将包从eth0接口发送到vxlan1接口。

这可以通过在网络命名空间中设置的Linux Traffic Control (TC) 来实现,用来在eth0vxlan1之间镜像流量。这是从Kata容器的设计中了解到的。然而,我认为在排查网络问题时检查Traffic Control (TC)配置是个好习惯。

以下输出结果展示了在我的环境中 pod 网络命名空间中的设备的 TC 筛选规则。

[root@ocp-412-ovn-worker-1 core]# ip netns exec $NS tc filter show dev eth0 root  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0 fh 800: ht 分段器 1  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0 fh 800::800 顺序 2048 key ht 800 bkt 0 终端 flowid not_in_hw  
  匹配 00000000/00000000 at 0  
        动作顺序 1: mirred (Egress Redirect to 设备 vxlan1) 劫持  
        索引 1 引用数 1 绑定数 1  

[root@ocp-412-ovn-worker-1 core]# ip netns exec $NS tc filter show dev vxlan1 root  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0 fh 800: ht 分段器 1  
过滤规则 parent ffff: 协议 all pref 49152 u32 链 0 fh 800::800 顺序 2048 key ht 800 bkt 0 终端 flowid not_in_hw  
  匹配 00000000/00000000 at 0  
        动作顺序 1: mirred (Egress Redirect to 设备 eth0) 劫持  
        索引 2 引用数 1 绑定数 1  

eth0的出站数据流向被重定向到了vxlan1,与此同时,vxlan1的出站数据流向也被重定向到了eth0。

如下图所示,根据这些细节,可以创建一个工作节点的网络拓扑图,用于参考和进一步的技术分析。

现在,我们来关注一下 pod 虚拟机。

注意,pod 设计为虚拟机,使用了一个固定的网络名称空间,如 podns

以下输出显示了 pod 的网络设置

ubuntu@podvm-nginx-priv-8b726648:/home/ubuntu# ip a  
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 状态 UNKNOWN group default qlen 1000  
链路/本地环回 00:00:00:00:00:00 广播 00:00:00:00:00:00  
inet 127.0.0.1/8 scope host lo  
valid_lft forever preferred_lft forever  
inet6 ::1/128 scope host  
valid_lft forever preferred_lft forever  
2: ens2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel 状态 UP group default qlen 1000  
链路/以太网设备 52:54:00:e1:58:67 广播 ff:ff:ff:ff:ff:ff  
inet 192.168.10.201/24 广播 192.168.10.255 scope global dynamic ens2  
valid_lft 2902sec preferred_lft 2902sec  
inet6 fe80::5054:ff:fee1:5867/64 scope link  
valid_lft forever preferred_lft forever  

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip r  
默认路由 via 192.168.10.1 dev ens2 proto dhcp src 192.168.10.201 metric 100  
192.168.10.0/24 dev ens2 proto kernel scope link src 192.168.10.201  
192.168.10.1 dev ens2 proto dhcp scope link src 192.168.10.201 metric 100  

root@podvm-nginx-priv-8b726648:/home/ubuntu# iptables -S  
-P INPUT ACCEPT  
-P FORWARD ACCEPT  
-P OUTPUT ACCEPT  

以下显示了 podns 网络名称空间内的网络配置。

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip netns exec podns ip a  
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 状态 UNKNOWN group default qlen 1000  
链路/本地环回 00:00:00:00:00:00 brd 00:00:00:00:00:00  
inet 127.0.0.1/8 范围主机 lo  
inet6 ::1/128 范围主机  
3: vxlan0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue 状态 UNKNOWN group default qlen 1000  
链路/以太网 7e:e5:f7:e6:f5:1a brd ff:ff:ff:ff:ff:ff 链路-netnsid 0  
inet 10.132.2.46/23 范围全局 vxlan0  
inet6 fe80::7ce5:f7ff:fee6:f51a/64 范围链

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip netns exec podns ip r  
默认路由 via 10.132.2.1 dev vxlan0  
10.132.2.0/23 dev vxlan0 协议内核 范围链 源 10.132.2.46  

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip netns exec podns ip -d li sh vxlan0  
3: vxlan0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue 状态 UNKNOWN 模式默认 group default qlen 1000  
链路/以太网 7e:e5:f7:e6:f5:1a brd ff:ff:ff:ff:ff:ff 链路-netnsid 0 促进度 0 最小mtu 68 最大mtu 65535  
vxlan ID 555005 远程地址 192.168.10.163 源端口 0 0 目标端口 4789 不学习 TTL 自动 老化 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx 地址生成模式 eui64 tx队列数 1 rx队列数 1 gso_max_size 65536 gso_max_segs 65535  

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip netns exec podns iptables -S  
-P INPUT ACCEPT  
-P FORWARD ACCEPT  
-P OUTPUT ACCEPT

这个**vxlan**隧道的设置看起来没问题。它显示了远程端点的IP**192.168.10.163**,这是工作节点VM的IP地址。

在 pod 虚拟机里,也没有任何防火墙规则。

然而,在Pod VM中你不会看到像在工作节点中那样的veth对。这时一个问题是这样的:init和podns网络命名空间之间是如何通信的,而没有了veth对呢?值得注意的是,物理设备位于init(即根)命名空间和podns命名空间中,而vxlan设备位于podns命名空间中。

感谢Stefano Brivio提醒了这个Linux内核的更新提交,它让这一切成为可能。

    commit f01ec1c017dead42092997a2b8684fcab4cbf126  
    Author: Nicolas Dichtel <nicolas.dichtel@6wind.com>  
    Date: Thu Apr 24 10:02:49 2014 +0200  
    vxlan: 添加对 x-netns 的支持  

     此补丁允许在封装或解封装数据包时切换网络命名空间。  
     VXLAN 套接字在 I/O 命名空间中打开,即在接收封装数据包的命名空间中打开。套接字查找将在该命名空间中执行,以找到相应的 VXLAN 隧道。解封装后,数据包将被注入到可能位于不同命名空间中的相应接口中。  

     当其中一个命名空间被删除时,隧道将被销毁。  

     配置示例:

     ip netns add netns1  
     ip netns exec netns1 ip link set lo up  
     ip link add vxlan10 type vxlan id 10 group 239.0.0.10 dev eth0 dstport 0  
     ip link set vxlan10 netns netns1  
     ip netns exec netns1 ip address add 192.168.0.249/24 broadcast 192.168.0.255 dev vxlan10  
     ip netns exec netns1 ip link set vxlan10 up

也有一个StackOverflow上的帖子解释了这个问题。

这些细节让我们对pod VM(即pod虚拟机)的网络拓扑有了一个良好的概览,如图所示。

我们可以在 vxlan0 接口上运行 tcpdump,看看是否收到了来自工作节点(worker node)的 ICMP 请求。

如下所示的输出,收到了互联网控制消息协议(ICMP)请求,但没有回信。

root@podvm-nginx-priv-8b726648:/home/ubuntu# ip netns exec podns tcpdump -i vxlan0 -s0 -n -vv  
tcpdump: listening on vxlan0, link-type EN10MB (Ethernet), capture size 262144 bytes  

[略]  

10.132.2.2 > 10.132.2.46: ICMP 回声请求, id 20, 序号 1, 长度 64  
10:34:17.389643 IP (tos 0x0, ttl 64, id 27606, offset 0, 标志 [DF], 协议 ICMP (1), 长度 84)  
10.132.2.2 > 10.132.2.46: ICMP 回声请求, id 20, 序号 2, 长度 64  
10:34:18.413682 IP (tos 0x0, ttl 64, id 27631, offset 0, 标志 [DF], 协议 ICMP (1), 长度 84)  
10.132.2.2 > 10.132.2.46: ICMP 回声请求, id 20, 序号 3, 长度 64  
10:34:19.002837 IP (tos 0x0, ttl 1, id 28098, offset 0, 标志 [DF], 协议 UDP (17), 长度 69)  

[略]

咱们现在来盘点一下情况。

完成这个练习后,你对工作节点和 pod 虚拟机的网络结构有了一个不错的理解,并且没有发现任何隧道设置问题的迹象。可以看到 pod 虚拟机接收了 ICMP 数据包,也没有软件防火墙阻止这些数据包。接下来该做什么?

继续往下看,看看接下来会发生什么 :-)

(Markdown格式保持不变,直接翻译文本部分。)

故障排除阶段2

我用 wireshark 分析了正常工作的常规 Kata 设置中的 tcpdump 抓包。使用 Wireshark 的 GUI 可以很容易地理解通过 tcpdump 抓取的网络数据包。

在跟踪中没有看到任何ARP请求或响应。工作节点上的ARP表会被填充,且使用的是pod网络命名空间内的eth0设备的MAC地址,而不是pod VM中的vxlan0设备(位于podns命名空间内)的MAC地址。

? (10.132.2.46) 的 MAC 地址是 0a:58:0a:84:02:2e (MAC地址) [以太网],在 ovn-k8s-mp0 上。

0a:58:0a:84:02:2e 是工作节点上 pod 网络命名空间中的 eth0 接口的 MAC 地址,而 7e:e5:f7:e6:f5:1a 是虚拟机上 podns 命名空间中的 vxlan0 接口的 MAC 地址。

这是因为问题在于从工作节点(worker node)无法访问pod IP。ARP条目应该指向podns命名空间中vxlan0设备的MAC地址(比如7e:e5:f7:e6:f5:1a)。

回头想想,我本应该先看看ARP表里的信息。下次遇到类似情况时,我一定会那样做 :)

解答

斯蒂法诺·布里维奥(Stefano Brivio)的一个巧妙的技巧解决了这个问题。在 pod 虚拟机的 vxlan0 接口上使用与工作节点上的 pod 的 eth0 接口相同的 MAC 地址,解决了连接问题。

    ip netns exec podns ip link set vxlan0 down  # 将vxlan0接口设置为关闭状态  
    ip netns exec podns ip link set dev vxlan0 address 0a:58:0a:84:02:2e  # 设置vxlan0接口的MAC地址  
    ip netns exec podns ip link set vxlan0 up  # 将vxlan0接口设置为开启状态

这就是最终的网络拓扑图。

最后来总结一下

调试 Kubernetes 集群中的网络问题并不简单。采用一种明确的方法,借助专家的帮助以及公开可用的资料,可以找到根本原因并解决问题。在这个过程中学习也挺有趣的。

我希望这对你有用。

下面是我排除故障时发现的一些非常有用的参考资料。

  • https://learnk8s.io/kubernetes-network-packets
  • https://programmer.help/blogs/practice-vxlan-under-linux.html
  • https://www.man7.org/linux/man-pages/man7/ovn-architecture.7.html
  • https://developers.redhat.com/articles/2022/04/06/introduction-linux-bridging-commands-and-features#vlan_tunnel_mapping
  • https://www.tkng.io/cni/flannel/
  • https://access.redhat.com/documentation/en-us/openshift_container_platform/3.4/html/cluster_administration/admin-guide-sdn-troubleshooting#debugging-local-networking
这篇关于动手排查Kubernetes网络故障之旅的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!