(服務器用的阿里雲主機,CentOS 7.3,彷佛無論內存多少阿里雲都把 conntrack_max 設成 65536)php
症狀
CentOS服務器,負載正常,但請求大量超時,服務器/應用訪問日誌看不到相關請求記錄。前端
在dmesg或/var/log/messages看到大量如下記錄:vim
kernel: nf_conntrack: table full, dropping packet.服務器
緣由
服務器訪問量大,內核netfilter模塊conntrack相關參數配置不合理,致使新鏈接被drop掉。網絡
詳細
nf_conntrack模塊在kernel 2.6.15(2006-01-03發佈) 被引入,支持ipv4和ipv6,取代只支持ipv4的ip_connktrack,用於跟蹤鏈接的狀態,供其餘模塊使用。架構
最多見的使用場景是 iptables 的 nat 和 state 模塊:tcp
nat 根據轉發規則修改IP包的源/目標地址,靠nf_conntrack的記錄才能讓返回的包能路由到發請求的機器。
state 直接用 nf_conntrack 記錄的鏈接狀態(NEW/ESTABLISHED/RELATED/INVALID)來匹配防火牆過濾規則。
iptables性能
nf_conntrack用1個哈希表記錄已創建的鏈接,包括其餘機器到本機、本機到其餘機器、本機到本機(例如 ping 127.0.0.1 也會被跟蹤)。測試
若是鏈接進來比釋放的快,把哈希表塞滿了,新鏈接的數據包會被丟掉,此時netfilter變成了一個黑洞,致使拒絕服務。 這發生在3層(網絡層),應用程序毫無辦法。阿里雲
各發行版區別:
CentOS (7.3) 默認加載該模塊
Ubuntu (16.10+) 和 Kali Linux (2016.1+) 默認不加載,不會有這問題
查看
netfilter 相關的內核參數:
sudo sysctl -a | grep conntrack
sudo sysctl -a | grep conntrack | grep timeout
netfilter模塊加載時的bucket和max配置:
sudo dmesg | grep conntrack
哈希表使用狀況:
grep conntrack /proc/slabinfo
當前跟蹤的鏈接數:
sudo sysctl net.netfilter.nf_conntrack_count
跟蹤的每一個鏈接的詳情:
cat /proc/net/nf_conntrack
cat /proc/net/nf_conntrack | awk '/^.tcp.$/ {count[$6]++} END {for(state in count) print state, count[state]}'
cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10
stackoverflow - details of /proc/net/ip_conntrack / nf_conntrack
經常使用參數說明
net.netfilter.nf_conntrack_count
有說法是這數字持續超過 nf_conntrack_max 的 20% 就該考慮調高上限了。
net.netfilter.nf_conntrack_buckets
net.netfilter.nf_conntrack_max
net.nf_conntrack_max
如今凡有那麼點用戶量的服務器跟蹤20萬以上鍊接很正常,真按系統默認值也勉強能用,但阿里雲彷佛設了特別保守的默認值,bucket爲 16384,max爲 65536,這是倒退回了07-11年CentOS 5-6的時代。
推薦bucket至少 262144,max至少 1048576,不夠再繼續加
https://wiki.khnet.info/index.php/Conntrack_tuning, 2008-01
縮短超時時間可讓netfilter更快地把跟蹤的記錄從哈希表裏移除。
調優的基本思路是先看 /proc/net/nf_conntrack ,哪一種協議哪一種狀態的鏈接最多,改小對應的超時參數
。
注意要充分測試,確保不影響業務。
net.netfilter.nf_conntrack_tcp_timeout_fin_wait # 默認 120 秒
net.netfilter.nf_conntrack_tcp_timeout_time_wait # 默認 120 秒
net.netfilter.nf_conntrack_tcp_timeout_close_wait # 默認 60 秒
net.netfilter.nf_conntrack_tcp_timeout_established # 默認 432000 秒(5天)
net.netfilter.nf_conntrack_generic_timeout # 默認 600 秒(10分鐘)
#net.netfilter.nf_conntrack_tcp_timeout_max_retrans # 默認 300 秒
#net.netfilter.nf_conntrack_tcp_timeout_unacknowledged # 默認 300 秒
調優
A. 調整內核參數
若是不能關掉防火牆,基本思路就是上面說的,調大nf_conntrack_buckets和nf_conntrack_max,調小超時時間。
除了有關聯的參數,儘可能一次只改一處,記下默認值,效果不明顯或更差就還原。
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
echo 262144 | sudo tee /sys/module/nf_conntrack/parameters/hashsize
sudo sysctl net.netfilter.nf_conntrack_buckets
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576
suod sysctl -w net.nf_conntrack_max=1048576
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=15
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300
用sysctl -w或echo xxx > /pro/sys/net/netfilter/xxx作的修改在重啓後會失效。
若是測試過沒問題,能夠編輯/etc/sysctl.d/下的配置文件(舊系統是/etc/sysctl.conf),系統啓動時會加載裏面的設置。
sudo vim /etc/sysctl.d/90-conntrack.conf
net.netfilter.nf_conntrack_max=1048576
net.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
net.netfilter.nf_conntrack_tcp_timeout_close_wait=15
net.netfilter.nf_conntrack_tcp_timeout_established=300
sudo sysctl -p /etc/sysctl.d/90-conntrack.conf
B. 關閉防火牆
對不直接暴露在公網、沒有用到NAT轉發的服務器來講,關閉Linux防火牆是最簡單也是最佳的辦法。
一般防火牆一關,sysctl -a裏就沒有netfilter相關的參數了。若是有例外,照上面調整。
sudo systemctl stop firewalld
sudo systemctl disable firewalld
sudo service iptables stop
sudo chkconfig --del iptables
【注意】如下是網上有些文章提到的解決方法,其實很差用,只記錄下來做備忘。
C. 設置不跟蹤鏈接的規則(不推薦)
對須要防火牆的機器,能夠在iptables設置NOTRACK規則,減小要跟蹤的鏈接數
【注意】設置成不跟蹤的鏈接沒法拿到狀態,可能會致使keep-alive用不了:
咱們改以前 ESTAB 的鏈接1000多,改以後超過1.5w,響應時間幾十秒,前端基本連不上。
sudo iptables-save
sudo iptables -I INPUT 1 -m state --state UNTRACKED -j ACCEPT
sudo iptables -t raw -A PREROUTING -i lo -j NOTRACK
sudo iptables -t raw -A OUTPUT -o lo -j NOTRACK
sudo service iptables save
說明:
-t raw 會加載 iptable_raw 模塊(kernel 2.6+ 都有)
raw表基本就幹一件事,經過-j NOTRACK給不須要被鏈接跟蹤的包打標記(UNTRACKED狀態),告訴nf_conntrack不要跟蹤鏈接
raw 的優先級大於 filter,mangle,nat,包含 PREROUTING(針對進入本機的包) 和 OUTPUT(針對從本機出去的包) 鏈
缺點:很差維護,服務器對外端口較多或有變化時,容易改出問題
D. 禁用相關模塊(不推薦)
只要iptables還有規則用到nat和state模塊,就不適合關掉netfilter,不然這些規則會失效。
例如這條默認規則(一般寫在第1條或很靠前的位置):
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
表示放行已經創建的鏈接,再也不往下匹配其餘規則(第一次創建時已經所有檢查過關了)。關掉netfilter會拿不到狀態,致使每一個請求都要從頭至尾檢查一次,影響性能。
所以若是iptables不能關,最好不要禁用netfilter。
若是確實須要禁用:
sudo lsmod | egrep "ip_table|iptable|nat|conntrack"
sudo iptables-save
sudo vim /etc/sysconfig/iptables-config
sudo service iptables stop
sudo chkconfig --del iptables
sudo rmmod iptable_nat
sudo rmmod ip6table_nat
sudo rmmod nf_defrag_ipv4
sudo rmmod nf_defrag_ipv6
sudo rmmod nf_nat
sudo rmmod nf_nat_ipv4
sudo rmmod nf_nat_ipv6
sudo rmmod nf_conntrack
sudo rmmod nf_conntrack_ipv4
sudo rmmod nf_conntrack_ipv6
sudo rmmod xt_conntrack
缺點:若是環境/配置文件不是徹底由你掌控或沒有很好的管理,容易出問題