ref https://testerhome.com/topics/7509

nf_conntrack 模块在 kernel 2.6.15 被引入, 支持 ipv4 和 ipv6, 取代只支持 ipv4 的 ip_conntrack 用于跟踪连接的状态,供其他模块使用,工作在三层网络

最常见的使用场景是 iptables 的 nat 表 和 state 模块

nf_conntrack 用 1 个哈希表记录已建立的连接,包括其他机器到本机、本机到其他机器、本机到本机(例如 ping 127.0.0.1 也会被跟踪)

如果连接进来比释放的快,把哈希表塞满了,新连接的数据包会被丢掉,此时 netfilter 变成了一个黑洞,导致拒绝服务

查询

# 列出所有 nf_conntrack 相关的内核参数
sysctl -a | grep conntrack

# 只看超时相关参数
sysctl -a | grep conntrack | grep timeout

# /proc/sys/net/netfilter/ 下能看到一堆跟nf_conntrack参数同名的文件,值也一样

# nf_conntrack_buckets 使用情况
grep conntrack /proc/slabinfo
# 前4个数字分别为:
# 当前活动对象数、可用对象总数、每个对象的大小(字节)、包含至少1个活动对象的分页数

# 查看跟踪的每个连接的详情
cat /proc/net/nf_conntrack
# 统计里面的TCP连接的各状态和条数
cat /proc/net/nf_conntrack | awk '/^.*tcp.*$/ {sum[$6]++} END {for(status in sum) print status, sum[status]}'
# 记录数最多的10个ip
cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10

# 记录格式:
# 网络层协议名、网络层协议编号、传输层协议名、传输层协议编号、记录失效前剩余秒数、连接状态(不是所有协议都有)
# 之后都是key=value或flag格式,1行里最多2个同名key(如 src 和 dst),第1次出现的来自请求,第2次出现的来自响应

# flag:
# [ASSURED]  请求和响应都有流量
# [UNREPLIED]  没收到响应,哈希表满的时候这些连接先扔掉

details of /proc/net/ip_conntrack / nf_conntrack

常用参数说明

默认参数相当保守,在这年代如果不调整,基本上有点用户就能出线上问题

# 哈希表大小,默认 16384
net.netfilter.nf_conntrack_buckets
# 这参数在模块加载时设置,直接修改没用,要这样:
echo <新值> > /sys/module/nf_conntrack/parameters/hashsize
# netfilter使用不可交换的内核空间内存,哈希表不是随便改多大都行

# 连接跟踪表大小,默认 nf_conntrack_buckets * 4 (65536)
net.netfilter.nf_conntrack_max
net.nf_conntrack_max
# 默认值实在太小,稍微忙点的服务器里起码跟踪100~200k个连接
# 如果改了这个,按比例改 nf_conntrack_buckets

# 哈希表里的每个元素(bucket)都是链表,每条conntrack记录是链表上的1个节点  
# 当conntrack数达到 conntrack_max 时,每个链表有 conntrack_max / conntrack_buckets 条记录(默认为4)

# 哈希表里的实时连接跟踪数(只读)
# 值来自 /proc/net/nf_conntrack 的条数
net.netfilter.nf_conntrack_count
# 有说法是这数字持续超过 nf_conntrack_max 的20%就该考虑调高上限了

# TCP每个状态都有超时设置项,可按需设置
# 这里的超时是指超过指定时间不再跟踪连接

# 默认 120 秒
net.netfilter.nf_conntrack_tcp_timeout_time_wait
# 我们的nf_conntrack文件里 30k+记录几乎全是 time_wait ,只有几百条 established ,其他忽略不计
# 等着释放的连接通常没必要继续跟踪这么久,可以调小点

# 默认 432000 秒(5天)
netfilter.ip_conntrack_tcp_timeout_established
# 应用程序有正确设置超时的话,这参数其实影响不大
# 服务端主动把连接关了就不再是 established 状态了

# 通用超时设置,作用于4层(传输层)未知或不支持的协议
# 默认 600 秒(10分钟)
net.netfilter.nf_conntrack_generic_timeout
# 在我们的场景里,缩到 60 秒也没看出效果

解决

推荐A,做不到就B + C

A. 关闭防火墙

对不直接暴露在公网、没有用到 NAT 转发的服务器来说,关闭 Linux 防火墙是最简单也是最佳的办法

# CentOS 7.x
service firewalld stop

# CentOS 6.x
service iptables stop
# 网上有些文章说关了 iptables 之后,用 iptables -L -n 之类查看规则也会导致 nf_conntrack 重新加载,实测并不会