Linux 生產內核網絡參數調優分析
本文總結了常見的 Linux 內核參數及相關問題。修改內核參數前,您需要:
- 從實際需要出發,最好有相關數據的支撐,不建議隨意調整內核參數。
- 了解參數的具體作用,且注意同類型或版本環境的內核參數可能有所不同。
1. 查看和修改 Linux 實例內核參數
1.1 方法一
通過
/proc/sys/
目錄
查看內核參數: 使用
cat
查看對應文件的內容,例如執行命令cat /proc/sys/net/ipv4/tcp_tw_recycle
查看net.ipv4.tcp_tw_recycle
的值。修改內核參數: 使用
echo
修改內核參數對應的文件,例如執行命令echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle
將net.ipv4.tcp_tw_recycle
的值修改為 0。
注意:
/proc/sys/
目錄是 Linux 內核在啟動后生成的偽目錄,其目錄下的net
文件夾中存放了當前系統中開啟的所有內核參數、目錄樹結構與參數的完整名稱相關,如net.ipv4.tcp_tw_recycle
,它對應的文件是/proc/sys/net/ipv4/tcp_tw_recycle
,文件的內容就是參數值。- 方法一 修改的參數值僅在當次運行中生效,系統重啟后會回滾歷史值,一般用于臨時性的驗證修改的效果。若需要永久性修改,請參閱 方法二(https://help.aliyun.com/knowledge_detail/41334.html#method2)。
1.2 方法二
通過
sysctl.conf
文件
查看內核參數: 執行命令
sysctl -a
查看當前系統中生效的所有參數。修改內核參數:
執行命令
/sbin/sysctl -w kernel.parameter="example"
修改參數,如sysctl -w net.ipv4.tcp_tw_recycle="0"
。執行命令
vi /etc/sysctl.conf
修改/etc/sysctl.conf
文件中的參數。執行命令
/sbin/sysctl -p
使配置生效。
注:調整內核參數后內核處于不穩定狀態,請務必重啟實例。
2. Linux 網絡相關內核參數引發的常見問題及處理
2.1 Linux 實例 NAT 哈希表滿導致丟包
此處涉及的內核參數:
net.netfilter.nf_conntrack_buckets
net.nf_conntrack_max
2.1.1 問題現象
Linux 實例出現間歇性丟包,無法連接實例,通過 tracert、mtr 等工具排查,外部網絡未見異常。同時,如下圖所示,在系統日志中重復出現大量(table full, dropping packet.
)錯誤信息。
Feb 6 16:05:07 i-*** kernel: nf_conntrack: table full, dropping packet.
2.1.2 原因分析
ip_conntrack 是 Linux 系統內 NAT 的一個跟蹤連接條目的模塊。ip_conntrack 模塊會使用一個哈希表記錄 TCP 協議 established connection 記錄,當這個哈希表滿了的時候,便會導致 nf_conntrack: table full, dropping packet
錯誤。Linux 系統會開辟一個空間用來維護每一個 TCP 鏈接,這個空間的大小與 nf_conntrack_buckets
、nf_conntrack_max
相關,后者的默認值是前者的 4 倍,而前者在系統啟動后無法修改,所以一般都是建議調大 nf_conntrack_max
。
注意:系統維護連接比較消耗內存,請在系統空閑和內存充足的情況下調大
nf_conntrack_max
,且根據系統的情況而定。
2.1.3 決思路
使用管理終端登錄實例。
執行命令
# vi /etc/sysctl.conf
編輯系統內核配置。修改哈希表項最大值參數:
net.netfilter.nf_conntrack_max = 655350
。修改超時參數:
net.netfilter.nf_conntrack_tcp_timeout_established = 1200
,默認情況下 timeout 是 432000(秒)。執行命令
# sysctl -p
使配置生效。
2.2 Time wait bucket table overflow 報錯
此處涉及的內核參數:
net.ipv4.tcp_max_tw_buckets
2.2.1 問題現象
Linux 實例 /var/log/message
日志全是類似 kernel: TCP: time wait bucket table overflow
的報錯信息,提示 time wait bucket table
溢出,如下:
Feb 18 12:28:38 i-*** kernel: TCP: time wait bucket table overflow
Feb 18 12:28:44 i-*** kernel: printk: 227 messages suppressed.
執行命令 netstat -ant|grep TIME_WAIT|wc -l
統計處于 TIME_WAIT 狀態的 TCP 連接數,發現處于 TIME_WAIT 狀態的 TCP 連接非常多。
2.2.2 原因分析
參數 net.ipv4.tcp_max_tw_buckets
可以調整內核中管理 TIME_WAIT 狀態的數量,當實例中處于 TIME_WAIT 及需要轉換為 TIME_WAIT 狀態連接數之和超過了 net.ipv4.tcp_max_tw_buckets
參數值時,message 日志中將報錯 time wait bucket table
,同時內核關閉超出參數值的部分 TCP 連接。您需要根據實際情況適當調高 net.ipv4.tcp_max_tw_buckets
,同時從業務層面去改進 TCP 連接。
2.2.3 解決思路
執行命令
netstat -anp |grep tcp |wc -l
統計 TCP 連接數。執行命令
vi /etc/sysctl.conf
,查詢net.ipv4.tcp_max_tw_buckets
參數。如果確認連接使用很高,容易超出限制。調高參數
net.ipv4.tcp_max_tw_buckets
,擴大限制。執行命令
# sysctl -p
使配置生效。
2.3 Linux 實例中 FIN_WAIT2 狀態的 TCP 鏈接過多
此處涉及的內核參數:
net.ipv4.tcp_fin_timeout
2.3.1 問題現象
FIN_WAIT2 狀態的 TCP 鏈接過多。
2.3.2 原因分析
- HTTP 服務中,Server 由于某種原因會主動關閉連接,例如 KEEPALIVE 超時的情況下。作為主動關閉連接的 Server 就會進入 FIN_WAIT2 狀態。
- TCP/IP 協議棧中,存在半連接的概念,FIN_WAIT2 狀態不算做超時,如果 Client 不關閉,FIN_WAIT_2 狀態將保持到系統重啟,越來越多的 FIN_WAIT_2 狀態會致使內核 Crash。
- 建議調小
net.ipv4.tcp_fin_timeout
參數,減少這個數值以便加快系統關閉處于FIN_WAIT2
狀態的 TCP 連接。
2.3.3 解決思路
- 執行命令
vi /etc/sysctl.conf
,修改或加入以下內容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
- 執行命令
# sysctl -p
使配置生效。
注:由于
FIN_WAIT2
狀態的 TCP 連接會進入TIME_WAIT
狀態,請同時參閱 time wait bucket table overflow 報錯。
2.4 Linux 實例中出現大量 CLOSE_WAIT 狀態的 TCP 連接
2.4.1 問題現象
執行命令 netstat -atn|grep CLOSE_WAIT|wc -l
發現當前系統中處于 CLOSE_WAIT
狀態的 TCP 連接非常多。
2.4.2 原因分析
關閉 TCP 連接時,TCP 連接的兩端都可以發起關閉連接的請求,若對端發起了關閉連接,但本地沒有關閉連接,那么該連接就會處于 CLOSE_WAIT 狀態。雖然該連接已經處于半開狀態,但是已經無法和對端通信,需要及時的釋放掉該鏈接。建議從業務層面及時判斷某個連接是否已經被對端關閉,即在程序邏輯中對連接及時關閉檢查。
2.4.3 解決思路
編程語言中對應的讀、寫函數一般包含了檢測 CLOSE_WAIT TCP 連接功能,例如:
Java 語言:
- 通過
read
方法來判斷 I/O 。當 read 方法返回-1
時則表示已經到達末尾。 - 通過
close
方法關閉該鏈接。
C 語言:
- 檢查
read
的返回值。- 若等于 0 則可以關閉該連接。
- 若小于 0 則查看 errno,若不是 AGAIN 則同樣可以關閉連接。
2.5 客戶端配置 NAT 后仍無法訪問 ECS 或 RDS 遠端服務器
此處涉及的內核參數:
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_timestamps
2.5.1 問題現象
客戶端配置 NAT 后無法訪問遠端 ECS、RDS,包括配置了 SNAT 的 VPC ECS 。同時無法訪問連接其他 ECS 或 RDS 等云產品,抓包檢測發現遠端對客戶端發送的 SYN 包沒有響應。
2.5.2 原因分析
若遠端服務器的內核參數 net.ipv4.tcp_tw_recycle
和 net.ipv4.tcp_timestamps
的值都為 1,則遠端服務器會檢查每一個報文中的時間戳(Timestamp),若 Timestamp 不是遞增的關系,不會響應這個報文。配置 NAT 后,遠端服務器看到來自不同的客戶端的源 IP 相同,但 NAT 前每一臺客戶端的時間可能會有偏差,報文中的 Timestamp 就不是遞增的情況。
2.5.3 解決思路
- 遠端服務器為 ECS 時,修改參數
net.ipv4.tcp_tw_recycle
為 0。 - 遠端服務器為 RDS 等 PaaS 服務時。RDS 無法直接修改內核參數,需要在客戶端上修改參數
net.ipv4.tcp_tw_recycle
和net.ipv4.tcp_timestamps
為 0。
3. 總結, 以上涉及 Linux 內核參數說明
參數 | 說明 |
---|---|
net.ipv4.tcp_max_syn_backlog | 該參數決定了系統中處于 SYN_RECV 狀態的 TCP 連接數量。SYN_RECV 狀態指的是當系統收到 SYN 后,作了 SYN+ACK 響應后等待對方回復三次握手階段中的最后一個 ACK 的階段。 |
net.ipv4.tcp_syncookies | 該參數表示是否打開 TCP 同步標簽(SYN_COOKIES ),內核必須開啟并編譯 CONFIG_SYN_COOKIES,SYN_COOKIES 可以防止一個套接字在有過多試圖連接到達時引起過載。默認值 0 表示關閉。當該參數被設置為 1 且 SYN_RECV 隊列滿了之后,內核會對 SYN 包的回復做一定的修改,即,在響應的 SYN+ACK 包中,初始的序列號是由源 IP + Port、目的 IP + Port 及時間這五個參數共同計算出一個值組成精心組裝的 TCP 包。由于 ACK 包中確認的序列號并不是之前計算出的值,惡意攻擊者無法響應或誤判,而請求者會根據收到的 SYN+ACK 包做正確的響應。啟用 net.ipv4.tcp_syncookies 后,會忽略 net.ipv4.tcp_max_syn_backlog 。 |
net.ipv4.tcp_synack_retries | 該參數指明了處于 SYN_RECV 狀態時重傳 SYN+ACK 包的次數。 |
net.ipv4.tcp_abort_on_overflow | 設置該參數為 1 時,當系統在短時間內收到了大量的請求,而相關的應用程序未能處理時,就會發送 Reset 包直接終止這些鏈接。建議通過優化應用程序的效率來提高處理能力,而不是簡單地 Reset。默認值: 0 |
net.core.somaxconn | 該參數定義了系統中每一個端口最大的監聽隊列的長度,是個全局參數。該參數和 net.ipv4.tcp_max_syn_backlog 有關聯,后者指的是還在三次握手的半連接的上限,該參數指的是處于 ESTABLISHED 的數量上限。若您的 ECS 實例業務負載很高,則有必要調高該參數。listen(2) 函數中的參數 backlog 同樣是指明監聽的端口處于 ESTABLISHED 的數量上限,當 backlog 大于 net.core.somaxconn 時,以 net.core.somaxconn 參數為準。 |
net.core.netdev_max_backlog | 當內核處理速度比網卡接收速度慢時,這部分多出來的包就會被保存在網卡的接收隊列上,而該參數說明了這個隊列的數量上限。 |
4. 生產集群內核配置參考
- 4.1 參考1: 筆者所在公司的 Iot 生產環境
EMQ
集群的sysctl.conf
配置 (CentOS 7.4 4C 32G)
# see: https://www.kernel.org/doc/Documentation/sysctl
fs.file-max = 1048576
fs.nr_open = 2097152
net.core.somaxconn = 32768
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_mem = 378798 505064 757596
net.ipv4.tcp_rmem = 1024 4096 16777216
net.ipv4.tcp_wmem = 1024 4096 16777216
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.ip_local_port_range = 1024 65000
net.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
- 4.2 參考2: 感謝 lework/kainstall 的作者, 基于純
shell
的 kubernetes 生產集群的sysctl
配置
# see: https://www.kernel.org/doc/Documentation/sysctl
#############################################################################################
# 調整虛擬內存
#############################################################################################
# Default: 30
# 0 - 任何情況下都不使用swap。
# 1 - 除非內存不足(OOM),否則不使用swap。
vm.swappiness = 0
# 內存分配策略
#0 - 表示內核將檢查是否有足夠的可用內存供應用進程使用;如果有足夠的可用內存,內存申請允許;否則,內存申請失敗,并把錯誤返回給應用進程。
#1 - 表示內核允許分配所有的物理內存,而不管當前的內存狀態如何。
#2 - 表示內核允許分配超過所有物理內存和交換空間總和的內存
vm.overcommit_memory=1
# OOM時處理
# 1關閉,等于0時,表示當內存耗盡時,內核會觸發OOM killer殺掉最耗內存的進程。
vm.panic_on_oom=0
# vm.dirty_background_ratio 用于調整內核如何處理必須刷新到磁盤的臟頁。
# Default value is 10.
# 該值是系統內存總量的百分比,在許多情況下將此值設置為5是合適的。
# 此設置不應設置為零。
vm.dirty_background_ratio = 5
# 內核強制同步操作將其刷新到磁盤之前允許的臟頁總數
# 也可以通過更改 vm.dirty_ratio 的值(將其增加到默認值30以上(也占系統內存的百分比))來增加
# 推薦 vm.dirty_ratio 的值在60到80之間。
vm.dirty_ratio = 60
# vm.max_map_count 計算當前的內存映射文件數。
# mmap 限制(vm.max_map_count)的最小值是打開文件的ulimit數量(cat /proc/sys/fs/file-max)。
# 每128KB系統內存 map_count應該大約為1。 因此,在32GB系統上,max_map_count為262144。
# Default: 65530
vm.max_map_count = 2097152
#############################################################################################
# 調整文件
#############################################################################################
fs.may_detach_mounts = 1
# 增加文件句柄和inode緩存的大小,并限制核心轉儲。
fs.file-max = 2097152
fs.nr_open = 2097152
fs.suid_dumpable = 0
# 文件監控
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=524288
fs.inotify.max_queued_events=16384
#############################################################################################
# 調整網絡設置
#############################################################################################
# 為每個套接字的發送和接收緩沖區分配的默認內存量。
net.core.wmem_default = 25165824
net.core.rmem_default = 25165824
# 為每個套接字的發送和接收緩沖區分配的最大內存量。
net.core.wmem_max = 25165824
net.core.rmem_max = 25165824
# 除了套接字設置外,發送和接收緩沖區的大小
# 必須使用net.ipv4.tcp_wmem和net.ipv4.tcp_rmem參數分別設置TCP套接字。
# 使用三個以空格分隔的整數設置這些整數,分別指定最小,默認和最大大小。
# 最大大小不能大于使用net.core.wmem_max和net.core.rmem_max為所有套接字指定的值。
# 合理的設置是最小4KiB,默認64KiB和最大2MiB緩沖區。
net.ipv4.tcp_wmem = 20480 12582912 25165824
net.ipv4.tcp_rmem = 20480 12582912 25165824
# 增加最大可分配的總緩沖區空間
# 以頁為單位(4096字節)進行度量
net.ipv4.tcp_mem = 65536 25165824 262144
net.ipv4.udp_mem = 65536 25165824 262144
# 為每個套接字的發送和接收緩沖區分配的最小內存量。
net.ipv4.udp_wmem_min = 16384
net.ipv4.udp_rmem_min = 16384
# 啟用TCP窗口縮放,客戶端可以更有效地傳輸數據,并允許在代理方緩沖該數據。
net.ipv4.tcp_window_scaling = 1
# 提高同時接受連接數。
net.ipv4.tcp_max_syn_backlog = 10240
# 將net.core.netdev_max_backlog的值增加到大于默認值1000
# 可以幫助突發網絡流量,特別是在使用數千兆位網絡連接速度時,
# 通過允許更多的數據包排隊等待內核處理它們。
net.core.netdev_max_backlog = 65536
# 增加選項內存緩沖區的最大數量
net.core.optmem_max = 25165824
# 被動TCP連接的SYNACK次數。
net.ipv4.tcp_synack_retries = 2
# 允許的本地端口范圍。
net.ipv4.ip_local_port_range = 2048 65535
# 防止TCP時間等待
# Default: net.ipv4.tcp_rfc1337 = 0
net.ipv4.tcp_rfc1337 = 1
# 減少tcp_fin_timeout連接的時間默認值
net.ipv4.tcp_fin_timeout = 15
# 積壓套接字的最大數量。
# Default is 128.
net.core.somaxconn = 32768
# 打開syncookies以進行SYN洪水攻擊保護。
net.ipv4.tcp_syncookies = 1
# 避免Smurf攻擊
# 發送偽裝的ICMP數據包,目的地址設為某個網絡的廣播地址,源地址設為要攻擊的目的主機,
# 使所有收到此ICMP數據包的主機都將對目的主機發出一個回應,使被攻擊主機在某一段時間內收到成千上萬的數據包
net.ipv4.icmp_echo_ignore_broadcasts = 1
# 為icmp錯誤消息打開保護
net.ipv4.icmp_ignore_bogus_error_responses = 1
# 啟用自動縮放窗口。
# 如果延遲證明合理,這將允許TCP緩沖區超過其通常的最大值64K。
net.ipv4.tcp_window_scaling = 1
# 打開并記錄欺騙,源路由和重定向數據包
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# 告訴內核有多少個未附加的TCP套接字維護用戶文件句柄。 萬一超過這個數字,
# 孤立的連接會立即重置,并顯示警告。
# Default: net.ipv4.tcp_max_orphans = 65536
net.ipv4.tcp_max_orphans = 65536
# 不要在關閉連接時緩存指標
net.ipv4.tcp_no_metrics_save = 1
# 啟用RFC1323中定義的時間戳記:
# Default: net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_timestamps = 1
# 啟用選擇確認。
# Default: net.ipv4.tcp_sack = 1
net.ipv4.tcp_sack = 1
# 增加 tcp-time-wait 存儲桶池大小,以防止簡單的DOS攻擊。
# net.ipv4.tcp_tw_recycle 已從Linux 4.12中刪除。請改用net.ipv4.tcp_tw_reuse。
net.ipv4.tcp_max_tw_buckets = 14400
net.ipv4.tcp_tw_reuse = 1
# accept_source_route 選項使網絡接口接受設置了嚴格源路由(SSR)或松散源路由(LSR)選項的數據包。
# 以下設置將丟棄設置了SSR或LSR選項的數據包。
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# 打開反向路徑過濾
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 禁用ICMP重定向接受
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# 禁止發送所有IPv4 ICMP重定向數據包。
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# 開啟IP轉發.
net.ipv4.ip_forward = 1
# 禁止IPv6
net.ipv6.conf.lo.disable_ipv6=1
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# 要求iptables不對bridge的數據進行處理
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
# arp緩存
# 存在于 ARP 高速緩存中的最少層數,如果少于這個數,垃圾收集器將不會運行。缺省值是 128
net.ipv4.neigh.default.gc_thresh1=2048
# 保存在 ARP 高速緩存中的最多的記錄軟限制。垃圾收集器在開始收集前,允許記錄數超過這個數字 5 秒。缺省值是 512
net.ipv4.neigh.default.gc_thresh2=4096
# 保存在 ARP 高速緩存中的最多記錄的硬限制,一旦高速緩存中的數目高于此,垃圾收集器將馬上運行。缺省值是 1024
net.ipv4.neigh.default.gc_thresh3=8192
# 持久連接
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
# conntrack表
net.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_buckets=262144
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
#############################################################################################
# 調整內核參數
#############################################################################################
# 地址空間布局隨機化(ASLR)是一種用于操作系統的內存保護過程,可防止緩沖區溢出攻擊。
# 這有助于確保與系統上正在運行的進程相關聯的內存地址不可預測,
# 因此,與這些流程相關的缺陷或漏洞將更加難以利用。
# Accepted values: 0 = 關閉, 1 = 保守隨機化, 2 = 完全隨機化
kernel.randomize_va_space = 2
# 調高 PID 數量
kernel.pid_max = 65536
kernel.threads-max=30938
# coredump
kernel.core_pattern=core
# 決定了檢測到soft lockup時是否自動panic,缺省值是0
kernel.softlockup_all_cpu_backtrace=1
kernel.softlockup_panic=1