Kubernetes 节点将conntrack_max值与节点上的 RAM 大小成比例地设置。高负载应用(尤其是在小型节点上)很容易超过conntrack_max,并导致连接复位和超时。
理论
conntrack 是建立在 Netlifier 框架之上的功能。它对于高性能的 Kubernetes 复杂网络至关重要,其中节点需要跟踪数千个 Pod 和服务之间的连接信息。
- 在 Kubernetes 中, 默认值可以在 prometheus 指标中找到node_nf_conntrack_entries_limit(需要node_exporter)
- linux系统中可以通过以下指令查看【当然如果未配置过的话,默认值会以该公式「CONNTRACK_MAX = 内存 (bytes) / 16384 / (多少位 / 32)」计算出默认值】:
sysctl net.netfilter.nf_conntrack_max
conntrack_max值与节点的内存成正比,通常聚合代理类服务会需要持续跟踪大量连接【消耗大量的conntrack entries】。
问题现象
如注意到服务存在各种连接问题和极高的错误率,如下:
java
原因:org.apache.http.NoHttpResponseException:xxxxx 无法响应
nodejs
Error: timeout of 15000ms exceeded/socket hang up/error: Error: ESOCKETTIMEDOUT
解决方案
立即修复
知道conntrack_max只与节点上 RAM 大小成正比 - 我们可以选择将应用程序安排在具有更多 RAM 的节点上,同时还限制了每个节点运行的聚合服务 Pod 数量。这可以解决眼前的问题。
Kubernetes 解决方案
Kubernetes 版本 1.15 或更高版本有一个修复pr: https://github.com/kubernetes/kubernetes/pull/74840
监控和警报
使用以下 prometheus 查询规则来监控和提醒高连接利用率:
1 | (node_nf_conntrack_entries / on (pod) node_nf_conntrack_entries_limit / on (pod) group_right kube_pod_info) > 0.9 |
init-container 或 DaemonSet
在服务在专用节点上运行的情况下,可以允许应用程序通过 sysctl 命令操作节点的conntrack_max。
但这种方法有许多缺点:
- 应用程序不知道节点的默认conntrack_max,因此如果要增加节点的 RAM,下面的脚本可能会减少conntrack_max。虽然可以开发更复杂的脚本,但应用程序管理基础结构并不合适,因为它具有完全不同的主要用途。
- 当 Pod 部署在节点上时,使用 init-scripts 的多个应用程序可能会导致 contrack_max 值发生变化。在由于发布或水平 Pod 自动缩放程序更改而频繁重新创建 Pod 的情况下,调试起来可能很棘手。
- conntrock_max的增加可能会导致节点上RAM使用量的增加,并可能导致延迟的增加,因为每个连接要处理的内容更多。
Init-container
1 | # Init-container which will run on pod startup |
DaemonSet
1 | # Daemon-set which will run on node-startup on node matching on nodekey: nodevalue. |
推荐方案:修改 conntrack 限制(见下文 nf_conntrack 部分)
nf_conntrack
基本知识
nf_conntrack是Linux内核连接跟踪的模块,常用在iptables中,比如
1 | -A INPUT -m state --state RELATED,ESTABLISHED -j RETURN |
可以通过cat /proc/net/nf_conntrack来查看当前跟踪的连接信息,这些信息以哈希形式(用链地址法处理冲突)存在内存中,并且每条记录大约占300B空间。
与nf_conntrack相关的内核参数有三个:
nf_conntrack_max:连接跟踪表的大小,默认262144nf_conntrack_buckets:哈希表的大小,(nf_conntrack_max/nf_conntrack_buckets就是每条哈希记录链表的长度),默认65536nf_conntrack_tcp_timeout_established:tcp会话的超时时间(秒),默认是432000 (5天)
查看nf_conntrack相关参数
使用Prometheus指令
使用以下两个 prometheus 指标【node_nf_conntrack_entries 和 node_nf_conntrack_entries_limit】来了解当前已使用的conntrace数量,发现已使用量接近极限:
1 | # This query displays % of utilisation of conntrack table space |
查看当前已用的连接跟踪数量(系统内):
1 | cat /proc/sys/net/netfilter/nf_conntrack_count |
或者
1 | sysctl net.netfilter.nf_conntrack_count |
查看最大连接跟踪表容量(上限)(系统内):
1 | cat /proc/sys/net/netfilter/nf_conntrack_max |
或者
1 | sysctl net.netfilter.nf_conntrack_max |
说明:
- nf_conntrack_count:当前跟踪的连接数(实时用量)。
- nf_conntrack_max:最大允许的连接数(阈值)。
如果想实时监控,可以用 watch 命令:watch cat /proc/sys/net/netfilter/nf_conntrack_count,这样可以方便地观测 nf_conntrack 的使用情况。
推荐配置
1 | 建议根据内存计算该值`CONNTRACK_MAX = 内存 (bytes) / 16384 / (x / 32)`,并满足`nf_conntrack_max = (4 * nf_conntrack_buckets)` |
如何修改 conntrack 限制
在Linux系统中,要增加节点的conntrack(连接跟踪)限制,可以按照以下步骤进行操作:
打开终端并使用 root 或具有 sudo 权限的用户登录。
编辑
/etc/sysctl.conf文件【也可以编辑个新文件:/etc/sysctl.d/xxx.conf】1
sudo vi /etc/sysctl.conf
在文件中添加以下行,以增加 conntrack 限制:
1
net.netfilter.nf_conntrack_max = 新的连接跟踪限制值
将 "新的连接跟踪限制值" 替换为你希望设置的实际限制值。
保存并关闭文件。
在终端中执行以下命令,使更改生效:
1
sudo sysctl -p
这样就完成了节点的conntrack限制的增加。
注:这些更改将在下次系统启动时保持生效。
问题【注意】
/proc/sys/net/netfilter/nf_conntrack_max文件的存在,依赖于 nf_conntrack 内核模块被加载。- 在未加载模块的情况下,内核不会创建此文件,因此
cat时会提示No such file or directory。 - 很多精简型Linux系统(如K3s常用的最小化发行版)默认并不会主动加载nf_conntrack模块,只有在K3s或Kubernetes等组件需要用到时,才会由其启动脚本加载。
解决方法一:使用/etc/modules-load.d/方式
创建文件
/etc/modules-load.d/nf_conntrack.conf内容如下:
1
nf_conntrack
这样,每次系统启动时,nf_conntrack模块会自动被加载。
解决方法二:在rc.local里加
在/etc/rc.local(需赋可执行权限)加入:
1 | modprobe nf_conntrack |
验证
- 重启服务器后,先确认
lsmod | grep nf_conntrack有输出,或者cat /proc/sys/net/netfilter/nf_conntrack_max文件已存在。 - 再确认
sysctl net.netfilter.nf_conntrack_max为你设置的值。
关键是让nf_conntrack模块在sysctl应用参数前被加载。
推荐使用/etc/modules-load.d/nf_conntrack.conf,这种方式最干净、最标准。
引自官方 kube-prometheus runbooks
意义
当前节点链接数量已接近极限。
问题
当达到限制时,一些连接将被被丢弃,从而降低服务质量。
排查
检查节点上的当前连接值。检查哪些应用生成了大量连接。
解决
将一些 Pod 迁移到另一个节点。或直接在节点上增加 conntrack 限制,记住要使其在节点重新启动后持续存在。
