Kubernetes 节点将conntrack_max值与节点上的 RAM 大小成比例地设置。高负载应用(尤其是在小型节点上)很容易超过conntrack_max,并导致连接复位和超时。

理论

conntrack 是建立在 Netlifier 框架之上的功能。它对于高性能的 Kubernetes 复杂网络至关重要,其中节点需要跟踪数千个 Pod 和服务之间的连接信息。

  • 在 Kubernetes 中, 默认值可以在 prometheus 指标中找到node_nf_conntrack_entries_limit(需要node_exporter
  • linux系统中可以通过以下指令查看【当然如果未配置过的话,默认值会以下面的公式计算出默认值】: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

使用以下两个 prometheus 指标【node_nf_conntrack_entriesnode_nf_conntrack_entries_limit】来了解当前已使用的conntrace数量,发现已使用量接近极限:

1
2
# This query displays % of utilisation of conntrack table space
(node_nf_conntrack_entries / on (pod) node_nf_conntrack_entries_limit / on (pod) group_right kube_pod_info) * 100

解决方案

立即修复

知道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
2
3
4
5
6
7
8
9
10
11
# Init-container which will run on pod startup
initContainers:
- name: sysctl
image: marketplace.gcr.io/google/centos7
args:
- -c
- sysctl -w net.netfilter.nf_conntrack_max=262144
command:
- /bin/sh
securityContext:
privileged: true

DaemonSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Daemon-set which will run on node-startup on node matching on nodekey: nodevalue.
# Sleep is a hacky way to prevent DaemonSet termination.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: sysctl-conntrack
spec:
template:
spec:
nodeSelector:
nodekey: nodevalue
containers:
- name: sysctl-buddy
image: marketplace.gcr.io/google/centos7
securityContext:
privileged: true
command: ["/bin/sh"]
args:
- -c
- sysctl -w net.netfilter.nf_conntrack_max=262144 && sleep 31536000000

推荐方案:修改 conntrack 限制(见下文 nf_conntrack 部分)

nf_conntrack

基本知识

nf_conntrack是Linux内核连接跟踪的模块,常用在iptables中,比如

1
2
-A INPUT -m state --state RELATED,ESTABLISHED  -j RETURN
-A INPUT -m state --state INVALID -j DROP

可以通过cat /proc/net/nf_conntrack来查看当前跟踪的连接信息,这些信息以哈希形式(用链地址法处理冲突)存在内存中,并且每条记录大约占300B空间。

nf_conntrack相关的内核参数有三个:

  • nf_conntrack_max:连接跟踪表的大小,默认262144
  • nf_conntrack_buckets:哈希表的大小,(nf_conntrack_max/nf_conntrack_buckets就是每条哈希记录链表的长度),默认65536
  • nf_conntrack_tcp_timeout_established:tcp会话的超时时间(秒),默认是432000 (5天)

推荐配置

1
2
3
4
5
6
# 建议根据内存计算该值`CONNTRACK_MAX = 内存 (bytes) / 16384 / (x / 32)`,并满足`nf_conntrack_max = (4 * nf_conntrack_buckets)`
# x指的是当前服务器是64位还是32位/使用lscpu或uname查看
net.netfilter.nf_conntrack_max=524288
net.netfilter.nf_conntrack_tcp_timeout_established=1800
# 建议满足`nf_conntrack_buckets = (nf_conntrack_max / 4)`
net.netfilter.nf_conntrack_buckets=131072

如何修改 conntrack 限制

在Linux系统中,要增加节点的conntrack(连接跟踪)限制,可以按照以下步骤进行操作:

  1. 打开终端并使用 root 或具有 sudo 权限的用户登录。

  2. 编辑 /etc/sysctl.conf 文件【也可以编辑个新文件:/etc/sysctl.d/xxx.conf

    1
    sudo vi /etc/sysctl.conf
  3. 在文件中添加以下行,以增加 conntrack 限制:

    1
    net.netfilter.nf_conntrack_max = 新的连接跟踪限制值

    将 "新的连接跟踪限制值" 替换为你希望设置的实际限制值。

  4. 保存并关闭文件。

  5. 在终端中执行以下命令,使更改生效:

    1
    sudo sysctl -p

这样就完成了节点的conntrack限制的增加。

注:这些更改将在下次系统启动时保持生效。

引自官方 kube-prometheus runbooks

意义

当前节点链接数量已接近极限。

问题

当达到限制时,一些连接将被被丢弃,从而降低服务质量。

排查

检查节点上的当前连接值。检查哪些应用生成了大量连接。

解决

将一些 Pod 迁移到另一个节点。或直接在节点上增加 conntrack 限制,记住要使其在节点重新启动后持续存在。