一条被遗忘的容器路由

前段时间我们 K8s 集群遇到一个网络问题,一直没空整理,现在终于可以理一理了。

环境描述

K8s version: v1.20
网络方案:Canal + macvlan
Pod CIDR: 192.168.0.0/16

canal+macvlan.png

发现问题

集群里有一个 node 节点,访问不了本节点启用 macvlan 网络的容器。

- 本节点其它容器可以访问这部分容器
- 其它节点可以访问这部分容器
- 其它节点上的容器也可以访问这部分容器

例如,这个 node 节点有一个启用 macvlan 网络的容器 ,flannel 网卡(eth0) ip192.168.8.42,在宿主节点上 ping 192.168.8.42 无法 ping 通。

  • 在宿主节点访问
# ping 192.168.8.42
PING 192.168.8.42 (192.168.8.42) 56(84) bytes of data.
^C
--- 192.168.8.42 ping statistics ---
112842 packets transmitted, 0 received, 100% packet loss, time 115549205ms
  • 在节点的其它容器访问
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
3: eth0@if17780: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether 46:ba:74:99:47:7f brd ff:ff:ff:ff:ff:ff
    inet 192.168.8.43/32 brd 192.168.8.43 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping 192.168.8.42
PING 192.168.8.42 (192.168.8.42): 56 data bytes
64 bytes from 192.168.8.42: seq=0 ttl=63 time=0.118 ms
64 bytes from 192.168.8.42: seq=1 ttl=63 time=0.042 ms
  • 在其它节点的容器访问
# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
3: eth0@if375779: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 2e:35:84:1a:e7:08 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.17.245/32 brd 192.168.17.245 scope global eth0
       valid_lft forever preferred_lft forever
[root@rancher-dg-tn7 ~]# ping 192.168.8.42
PING 192.168.8.42 (192.168.8.42) 56(84) bytes of data.
64 bytes from 192.168.8.42: icmp_seq=1 ttl=62 time=0.257 ms
64 bytes from 192.168.8.42: icmp_seq=2 ttl=62 time=0.164 ms

这场景挺奇怪的,容器间能互通,其它节点也能通,宿主节点却不能访问。
初步判断跟网络插件关系不大,不然就是整个节点的容器都不能访问了,网络问题就是让人头大。

排查思路

  1. 先来看看节点上的路由,会不会是节点路由表上没有到容器的路由明细。
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.xx.xx.1       0.0.0.0         UG    0      0        0 ens192
10.xx.xx.0       0.0.0.0         255.255.255.0   U     0      0        0 ens192
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
...
...
192.168.8.40    0.0.0.0         255.255.255.255 UH    0      0        0 cali8f6e7372819
192.168.8.42    0.0.0.0         255.255.255.255 UH    0      0        0 califda2d1a1007

这么一看,节点上的路由没什么问题,节点访问 192.168.8.42 这个容器时数据包直接通过 califda2d1a1007 这个虚拟设备进入容器。

接着尝试对这个设备进行抓包,验证一下内核数据包是不是进入容器。

# tcpdump -i califda2d1a1007 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on califda2d1a1007, link-type EN10MB (Ethernet), capture size 262144 bytes
14:18:06.368393 IP 172.17.0.1 > 192.168.8.42: ICMP echo request, id 3225, seq 1400, length 64
14:18:07.392402 IP 172.17.0.1 > 192.168.8.42: ICMP echo request, id 3225, seq 1401, length 64
^C

通过这个抓包可以发现两个问题:

  • 数据包确实通过 califda2d1a1007 设备进入容器了,但容器没有数据包响应。
  • 发起 icmp request 的源 IP 为什么是 172.17.0.1 ? 这可是节点 docker0IP 啊。
# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
  1. 分析容器 net namespace

进入容器 net namespace,查看网络结构,如前面贴出来的我们的网络架构图,这个容器有两张网卡,一张是 eth0(flannel),另外一张是 eth1(macvlan)

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
3: eth0@if17779: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 02:96:6c:66:6e:51 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.8.42/32 brd 192.168.8.42 scope global eth0
       valid_lft forever preferred_lft forever
4: eth1@if17778: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether aa:b3:23:7f:75:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.xx.xx.203/24 brd 10.xx.xx.255 scope global eth1
       valid_lft forever preferred_lft forever

接着对容器的 eth0 网络进行抓包,可以看到跟在节点上对 califda2d1a1007 抓包的结果一致。

# tcpdump -nn -i eth0 icmp  
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:53:01.472399 IP 172.17.0.1 > 192.168.8.42: ICMP echo request, id 3225, seq 3446, length 64
14:53:02.496402 IP 172.17.0.1 > 192.168.8.42: ICMP echo request, id 3225, seq 3447, length 64
^C

再看看容器的路由,默认路由是 macvlan 的网卡 eth1Pod CIDR 的路由指向 eth0, 172.23.0.0 是集群 Service CIDR,也是指向 eth0,这都是正确的。唯独没有指向 172.17.0.1 这个节点 docker0 的路由,这也能就是前面抓包我们看到的结果,解析了为什么在宿主 node 节点 ping 不通这个容器。

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.xx.xx.1       0.0.0.0         UG    0      0        0 eth1
10.xx.xx.0       169.254.1.1     255.255.255.0   UG    0      0        0 eth0
10.xx.xx.0       0.0.0.0         255.255.255.0   U     0      0        0 eth1
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0
172.23.0.0      169.254.1.1     255.255.128.0   UG    0      0        0 eth0
192.168.0.0     169.254.1.1     255.255.0.0     UG    0      0        0 eth0

尝试给这个容器增加一条指向宿主节点 docker0 的路由,果然网络就通了。

route add -net 172.17.0.0 netmask 255.255.0.0 eth0
# route del -net 172.17.0.0 netmask 255.255.0.0

分析原因

现在知道了宿主节点访问不到这个容器是因为缺少路由,但同时也带来了新的问题。
为什么宿主节点访问本节点容器的时候使用的 IPdocker0IP,而不是节点本身的物理 IP

回头看一下宿主节点本身的网络信息:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eno1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 34:73:79:14:ef:88 brd ff:ff:ff:ff:ff:ff
3: eno2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 34:73:79:14:ef:89 brd ff:ff:ff:ff:ff:ff
4: ens2f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ens192 state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b6 brd ff:ff:ff:ff:ff:ff
5: ens2f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ens224 state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b7 brd ff:ff:ff:ff:ff:ff
6: ens3f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ens192 state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b6 brd ff:ff:ff:ff:ff:ff
7: ens3f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ens224 state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b7 brd ff:ff:ff:ff:ff:ff
10: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:f8:95:c1:b7 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
11: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b6 brd ff:ff:ff:ff:ff:ff
    inet 10.xx.xx.38/24 brd 10.xx.xx.255 scope global ens192
       valid_lft forever preferred_lft forever
12: ens224: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 04:3f:72:bb:a1:b7 brd ff:ff:ff:ff:ff:ff
  • ens192 是节点真实 IP 的网卡,flannel 网络跨节点通讯也是这个这个网卡,它是一个 activebackup 类型的 teamd
  • ens224 也是一个 teamd,用于 macvlan 网络通讯。
  • ens3f0 这些是真正的物理网卡。

看宿主节点网络信息发现一个比较有趣的现象,docker0 前面的数字是要比 ens192/ens224 这些 teamd 网卡要小的。网卡前面的这个数字,在 Linux 上是 ifindex,在路径 /sys/class/net/docker0/ifindex 可以查到。到这里,我们猜想宿主节点访问本节点容器时使用 docker0IP 跟这个 ifindex 相关:ifindex 越小,它的优先级更高。

为了验证这个想法,我们想过去修改 ifindex,使得 ens192ifindexdocker0 的更小。但找不到修改方法,最后查阅了一些内核文档,得知这个 ifindex 是由一个内核函数 dev_new_index() 分配的,它的分配顺序依赖硬件检测、驱动注册、服务启动的先后。

既然修改 ifindex 这个思路不可行,那我们可以反向验证,找一个 “正常” 节点,在宿主节点上可以访问本节点的所有容器,包括启用了 macvlan 的容器。

接着,然后重启节点的网络服务。

  • 根据 dev_new_index() 的原理,ens192/ens224 这两个 teamd 必然会分配一个新的 ifindex 且大于 docker0

  • 重启节点 networking 服务前

# cat /sys/class/net/ens192/ifindex
10
# cat /sys/class/net/ens224/ifindex 
11
# cat /sys/class/net/docker0/ifindex
12
  • 重启节点 networking 服务后
# systemctl restart network
# cat /sys/class/net/ens192/ifindex
17774
# cat /sys/class/net/ens224/ifindex 
17775
# cat /sys/class/net/docker0/ifindex
12
  • 如实验前设想,重启 networking 服务后,宿主节点开始无法访问本节点已启用 macvlan 网络的容器,其它不受影响。

解决方案

在我们的容器网络规划里,docker 自带的这个 bridge 网络我们并没有用到,所以我们的路由规则也没有加上这个指向。可以说,在这种隐藏的场景下,这是一条被遗忘的路由。

总结一下前面的思路,这个问题是由于节点运行一段时间之后重启过网络服务引起的,解决这个问题,有几个方案:

  1. 添加临时路由

    • 直接在容器上 route add 添加一条路由即可,这个方案容器一旦重启就会失效。
  2. 重启宿主节点

    • 重启宿主节点可以让节点网络信息重新初始化,包括网卡的 ifindex,但这个方案也是不保险,一旦有需要重启 networking 服务,就还是会复现这个场景。
  3. macvlan controller 增加路由规则,然后重启容器。

    • 这个方案较为完整,既然是因为缺少路由引起的,那在控制器上把路由规则完善即可,若不希望重启容器,还可以给容器添加临时路由。容器重启后也会获取到控制器下发的最新路由。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • 本文主要是介绍docker默认的网络行为,包含创建的默认网络类型以及如何创建用户自定义网络,也会介绍如何在单一主机...
    凤落溪凰落地阅读 38,154评论 1 4
  • docker网络类型 none:不为容器配置任何网络功能,--net=none container:与另一个运行中...
    唯爱熊阅读 594评论 0 3
  • Docker网络模式 在讨论Kubernetes网络之前,让我们先来看一下Docker网络。Docker采用插件化...
    程序员札记阅读 7,890评论 0 14
  • [TOC] Docker容器平台选型调研 编排选型 Swarm Swarm可以从一个Dockerfile来构建镜像...
    AllenWu阅读 2,514评论 0 7
  • 深入浅出Docker学习笔记 Docker引擎 Docker引擎:用来运行和管理容器的核心文件模块化(基于开放容器...
    yuq329阅读 814评论 0 4