TKE 容器网络中的 ARP Overflow 问题探究及其解决之道

做者朱瑜坚,腾讯云后台开发工程师,熟悉 CNI 容器网络相关技术,负责腾讯云 TKE 的容器网络的构建和相关网络组件的开发维护工做,做为主力开发实现了 TKE 下一代容器网络方案。shell

1. 问题背景

1.1 问题描述

最近,某内部客户的 TKE VPC-CNI 模式的独立网卡集群上出现了 pod 间访问不通的状况,问题 pod ping 不通任何其余 pod 和节点。缓存

查看 dmesg 内核日志,有以下报错信息:neighbour: arp_cache: neighbor table overflow!(下图为后续复现的日志截图)网络

而且,这个集群规模较大,约有 1000 个节点,30000 个 pod,基本能够怀疑是因为集群规模较大,致使 ARP 表项过多,从而引发 ARP Overflow 的问题。数据结构

1.2 名词解释

名词 说明
TKE 全称 Tencent Kubernetes Engine, 腾讯云容器服务,是基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务
VPC-CNI 模式 是容器服务 TKE 基于 CNI 和 VPC 弹性网卡实现的容器网络能力
Pod Pod 是 kubernetes 的基本资源管理单位,拥有独立的网络命名空间,1个 Pod 可包含多个容器

2. 问题初步分析

从如上报错信息可知,这个问题的基本缘由在于 ARP 缓存表打满了。这里涉及到内核的 ARP 缓存垃圾回收机制。当 ARP 表项太多且又没有可回收的表项的时候,新表项就会没法插入。性能

这就致使网络包发送时没法找到对应的硬件地址(MAC)。使得网络包不能发送。测试

那么具体什么状况会致使新表项没法插入呢?回答这个问题,咱们须要先深刻了解一下 ARP 缓存老化及垃圾回收机制。spa

3. ARP 缓存老化回收机制

3.1 ARP 缓存表项状态机

如上图,是整个 ARP 表项的生命周期及其状态机。日志

咱们知道,对于 TCP/IP 网络包发送时,网络栈须要对端的 MAC 地址才能让网络包转换成二层的数据结构——帧,从而在网络中传输。而对于不一样广播域的 IP 地址,其对端 MAC 地址为网关,发送端会将网络包发给网关让其转发,而对于同广播域中的 IP 地址,其对端 MAC 地址即与 IP 地址对应。code

而经过 IP 地址找到 MAC 地址就是 ARP 协议的主要工做内容。ARP 协议的工做过程此处再也不赘述,而经过 ARP 协议找到 IP 地址对应的 MAC 地址后,会将该对应关系存储在本机上一段时间,以减小 ARP 协议的通讯频率,加快网络包的发送。该对应关系,即 ARP 缓存表项,其状态机或整个生命周期可描述以下:blog

  1. 初始时,对于任何网络包发送时,内核协议栈须要找到目的 IP 地址对应的对端 MAC 地址,若是这时 ARP 缓存中没有命中,则会新插入一条状态为 Incomplete 的表项。Incomplete 状态会尝试发送 ARP 包,请求某 IP 地址对应的 MAC 地址。
  2. 若收到 ARP 回应的,表项状态就变为 Reachable。
  3. 若尝试了必定次数后没收到响应,表项即变为 Failed。
  4. Reachable 表项在到达超时时间后,会变成 Stale 状态,Stale 状态的表项已不可再使用。
  5. Stale 的表项如有被引用来发包,则表项会变为 Delay 状态。
  6. Delay 状态的表项也不可以使用来发包,但在 Delay 状态到期前,收到 ARP 的本机确认,则从新转为 Reachable 状态。
  7. Delay 状态到期,表项变为 Probe 状态,该状态与 Incomplete 状态相似。
  8. Stale 状态到期后,会被启动的垃圾回收起回收删除。

经过如下命令可查看当前网络命名空间(network namespace) 中 arp 表项及其状态:

ip neigh

如:

本机确认:这是代指本机收到了一个源 mac 地址匹配的网络包,这个网络包表示这次网络通讯的“上一跳”便是该 mac 地址的机器,能收到这个网络包即说明该 mac 地址可达。所以便可把该表项转为 Reachable 状态。经过这一机制,内核可减小 ARP 的通讯需求。

3.2 涉及到的内核参数

如下列出了该机制中主要涉及的内核参数:

参数 含义 默认值
/proc/sys/net/ipv4/neigh/default/base_reachable_time Reachable 状态基础过时时间,每一个表项过时时间是在[1/2base_reachable_time,3/2base_reachable_time]之间 30秒
/proc/sys/net/ipv4/neigh/default/base_reachable_time_ms Reachable 状态基础过时时间,毫秒表示 30秒
/proc/sys/net/ipv4/neigh/default/gc_stale_time Stale 状态过时时间 60秒
/proc/sys/net/ipv4/neigh/default/delay_first_probe_time delay 状态过时到 Probe 的时间 5秒
/proc/sys/net/ipv4/neigh/default/gc_interval gc 启动的周期时间 30秒
/proc/sys/net/ipv4/neigh/default/gc_thresh1 少于这个值,gc 不会启动 2048
/proc/sys/net/ipv4/neigh/default/gc_thresh2 ARP表的最多纪录的软限制,容许超过该数字5秒 4096
/proc/sys/net/ipv4/neigh/default/gc_thresh3 ARP表的最多纪录的硬限制,大于该数目,gc当即启动,并强制回收 8192

其中,gc 相关的内核参数是对全部网卡(interface)生效的。可是各类到期时间的设置是仅对单独网卡(interface)生效的,default 值仅对新增接口设备生效。

3.3 ARP 缓存垃圾回收机制

由其缓存表项的状态机咱们知道,不是全部的表项都会被回收,只有 Stale 状态过时后,Failed 的表项可能会被回收。另外,ARP 缓存表项的垃圾回收是触发式的,须要回收的表项不必定马上会被回收,ARP 缓存表项的垃圾回收有四种启动逻辑:

  1. arp 表项数量 < gc_thresh1,不启动。
  2. gc_thresh1 =< arp 表项数量 <= gc_thresh2,按照 gc_interval 按期启动
  3. gc_thresh2 < arp 表项数量 <= gc_thresh3,5秒后启动
  4. arp 表项数量 > gc_thresh3,当即启动

对于不可回收的表项,垃圾回收即使启动了也不会对其进行回收。所以当不可回收的表项数量大于 gc_thresh3 的时候,垃圾回收也无能为力了。

4. 进一步探究

4.1 垃圾回收阈值是按命名空间级别生效仍是子机级别生效

咱们知道,每一个独立的网络命名空间是有完整的网络协议栈的。那么,ARP 缓存的垃圾回收也是每一个命名空间单独处理的吗?

从涉及的内核参数能够看出,gc 相关的内核参数是对全部接口设备生效的,所以,这里能够推测垃圾回收的阈值也是子机级别生效的,而不是按网络命名空间。

这里作了一个简单的实验来验证:

  1. 在节点 default ns 上的 gc_thresh1, gc_thresh2 和 gc_thresh3 设置成60 。
  2. 在节点上建立了 19 个独立网卡模式的 Pod
  3. 任意选择一个 pod ping 其余的 pod,以此产生 arp 缓存
  4. 用 shell 脚本扫描节点上的全部 pod,计算 arp 表项的和,能够获得:

能够发现, 各命名空间的累计 arp 表项的数目在每次达到 60 以后就会快速降低,也就是达到 60 以后就产生了垃圾回收。重复几回都是相似的结果,所以,这说明了垃圾回收在计算 ARP 表项是否触发阈值时,是计算各命名空间的累计值,也就是按子机级别生效,而非命名空间级别。

4.2 不可回收的 ARP 表项达到 gc_thresh3 时,会发生什么

由前面的介绍咱们知道,垃圾回收机制并不是回收任意 ARP 缓存,所以,当全部可达状态的 ARP 表项打满 ARP 缓存表时,也即达到 gc_thresh3 时,会发生什么行为?能够推测,此时旧的没法回收,新的 ARP 表项也没法插入,新的网络包会没法发送,也即发生了本次文章所描述的问题。

为了验证这一点,继续在以上环境中实验:

  1. 将任意两个 Pod 的基础老化时间 base_reachable_time 调长到 1800秒,以此产生不可回收的 ARP 表项。
  2. 设置 gc_thresh3 为 40,以此更容易触发问题
  3. 选择调整了老化时间的 pod ping 其余的 pod,以此产生 arp 缓存。
  4. 能够发现,当到达阈值的时候,ping 会产生丢包或不通:

查看内核日志 dmesg -T,能够看到文章开头描述的信息:neighbour: arp_cache: neighbor table overflow!

以上实验说明了,不可回收的 ARP 表项打满 ARP 表会让新的表项没法插入,从而网络不通。

4.3 为何相比 TKE 的全局路由模式和单网卡多 IP 模式,独立网卡模式更容易产生这个问题

要回答这个问题,咱们先简单看一下 TKE 各网络模式的原理介绍

全局路由模式

该网络模式下,每一个节点上的容器 IP 是预先分配到节点上的,这些 IP 同属一个子网,且每一个节点就是一个小子网。咱们知道,ARP 协议是为二层通讯服务的,所以,该网络方案中,每一个 Pod 的网络命名空间内的 ARP 表最大可能保存了节点上全部其余 Pod 的 ARP 表项,最后节点的 ARP 表项的数量最大即为 节点子网 IP 数的平方,如节点的子网大小是128,则其 ARP 表项最大可能为 127 的平方,约 16000。

共享网卡模式

该网络模式下,每一个节点会绑定辅助弹性网卡,节点上的 Pod 共享使用该辅助网卡,每一个 Pod 内不会作网络包的路由,只会有一条 ARP 表项,实际的路由控制在节点的 default 命名空间内完成。所以,该网络模式下,ARP 缓存表几乎是共享的,又由于网卡只能属于 1 个子网,所以每一个节点的 Pod ARP 缓存表只能存储一个子网的 IP-MAC 映射关系,至多数量为各网卡所在子网内 IP 的数量和,如子网是 /22,即含有约 1000 个 ip, 那么 arp 表项也大概有 1000,因为节点网卡配额通常不超过 10,所以该节点的最大 ARP 表项通常不超过 10000。

下一代网络方案——独立网卡模式

独立网卡模式是 TKE 团队推出的下一代“零损耗”容器网络方案,其基本原理以下图所示:

即母机虚拟出的弹性网卡,直接置于容器中,使容器得到与 CVM 子机同样的网络通讯能力和网络管理能力,大大提高了容器网络的数据面能力,真正作到“零损耗”。

目前,独立网卡网络方案已在 TKE 产品中开放白名单测试,欢迎内外部客户体验试用。

以上网络方案中,每一个 Pod 都会独占一个网卡,也会拥有独立的命名空间和独立的 ARP 缓存表。而每一个网卡均可以属于不一样的子网。所以,在独立网卡模式里,ARP 缓存表项数量至多为同可用区的子网 IP 数量之和。这一数量量级是能够很轻易上万的,很容易就突破了默认的 ARP 缓存设置。也就触发了这个问题。

5. 解决方案

从以上的分析能够看出,这个问题,调大垃圾回收的阈值,能够比较好的解决问题。所以,临时的解决方案,就是调大 ARP 缓存表的垃圾回收阈值:

echo 8192 > /proc/sys/net/ipv4/neigh/default/gc_thresh1echo 16384 > /proc/sys/net/ipv4/neigh/default/gc_thresh2echo 32768 > /proc/sys/net/ipv4/neigh/default/gc_thresh3

6. 总结

ARP 缓存打满以后,Pod 就会网络不通。初看起来很简单,可是其背后的 ARP 缓存老化和垃圾回收机制也是比较复杂的。查询了不少资料,可是都对“垃圾回收阈值是对各命名空间的 ARP 表项累积值生效仍是单独生效”,“垃圾回收会回收哪些表项”,“表项打满后的具体行为如何”等问题说不清、道不明。所以,笔者尝试经过几个小实验验证了具体的行为模式。相比直接阅读晦涩的内核源码,实验法也许也是一个研究问题和理解机制的捷径了。但愿可以帮助到各位读者。

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!

相关文章
相关标签/搜索