nf_conntrack
模块在 kernel 2.6.15 被引入, 支持 ipv4 和 ipv6, 取代只支持 ipv4 的 ip_conntrack
用于跟踪连接的状态,供其他模块使用,工作在三层网络
最常见的使用场景是 iptables 的 nat
表 和 state
模块
state
直接用 nf_conntrack
记录的连接状态(NEW
/ESTABLISHED
/RELATED
/INVALID
)来匹配过滤规则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] 没收到响应,哈希表满的时候这些连接先扔掉
默认参数相当保守,在这年代如果不调整,基本上有点用户就能出线上问题
# 哈希表大小,默认 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
对不直接暴露在公网、没有用到 NAT 转发的服务器来说,关闭 Linux 防火墙是最简单也是最佳的办法
# CentOS 7.x
service firewalld stop
# CentOS 6.x
service iptables stop
# 网上有些文章说关了 iptables 之后,用 iptables -L -n 之类查看规则也会导致 nf_conntrack 重新加载,实测并不会