上一節,咱們學習了 NAT 的原理,明白瞭如何在 Linux 中管理 NAT 規則。先來簡單複習一下。html
NAT 技術可以重寫 IP 數據包的源 IP 或目的 IP,因此廣泛用來解決公網 IP 地址短缺的問題。它可讓網絡中的多臺主機,經過共享同一個公網 IP 地址,來訪問外網資源。同時,
因爲 NAT 屏蔽了內網網絡,也爲局域網中機器起到安全隔離的做用。linux
Linux 中的 NAT ,基於內核的鏈接跟蹤模塊實現。因此,它維護每一個鏈接狀態的同時,也對網絡性能有必定影響。那麼,碰到 NAT 性能問題時,咱們又該怎麼辦呢?
接下來,我就經過一個案例,帶你學習 NAT 性能問題的分析思路。nginx
下面的案例仍然基於 Ubuntu 18.04,一樣適用於其餘的 Linux 系統。我使用的案例環境是這樣的:docker
# Ubuntu $ apt-get install -y docker.io tcpdump curl apache2-utils # CentOS $ curl -fsSL https://get.docker.com | sh $ yum install -y tcpdump curl httpd-tools
大部分工具,你應該都比較熟悉,這裏我簡單介紹一下 SystemTap 。apache
SystemTap 是 Linux 的一種動態追蹤框架,它把用戶提供的腳本,轉換爲內核模塊來執行,用來監測和跟蹤內核的行爲。關於它的原理,你暫時不用深究,後面的內容還會介紹
到。這裏你只要知道怎麼安裝就能夠了:ubuntu
# Ubuntu apt-get install -y systemtap-runtime systemtap # Configure ddebs source echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \ sudo tee -a /etc/apt/sources.list.d/ddebs.list # Install dbgsym apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F2EDC64DC5AEE1F6B9C621F0C8CAB6595FDFF622 apt-get update apt install ubuntu-dbgsym-keyring stap-prep apt-get install linux-image-`uname -r`-dbgsym # CentOS yum install systemtap kernel-devel yum-utils kernel stab-prep
本次案例仍是咱們最多見的 Nginx,而且會用 ab 做爲它的客戶端,進行壓力測試。案例中總共用到兩臺虛擬機,我畫了一張圖來表示它們的關係。安全
接下來,咱們打開兩個終端,分別 SSH 登陸到兩臺機器上(如下步驟,假設終端編號與圖示 VM 編號一致),並安裝上面提到的這些工具。注意,curl 和 ab 只須要在客戶端
VM(即 VM2)中安裝。bash
同之前的案例同樣,下面的全部命令都默認以 root 用戶運行。若是你是用普通用戶身份登錄系統,請運行 sudo su root 命令,切換到 root 用戶。網絡
若是安裝過程當中有什麼問題,一樣鼓勵你先本身搜索解決,解決不了的,能夠在留言區向我提問。若是你之前已經安裝過了,就能夠忽略這一點了。併發
接下來,咱們就進入到案例環節。
爲了對比 NAT 帶來的性能問題,咱們首先運行一個不用 NAT 的 Nginx 服務,並用 ab測試它的性能。
在終端一中,執行下面的命令,啓動 Nginx,注意選項 --network=host ,表示容器使用Host 網絡模式,即不使用 NAT:
docker run --name nginx-hostnet --privileged --network=host -itd feisky/nginx:80
而後到終端二中,執行 curl 命令,確認 Nginx 正常啓動:
curl http://192.168.0.30/ ... <p><em>Thank you for using nginx.</em></p> </body> </html>
繼續在終端二中,執行 ab 命令,對 Nginx 進行壓力測試。不過在測試前要注意,Linux默認容許打開的文件描述數比較小,好比在個人機器中,這個值只有 1024:
# open files $ ulimit -n 1024
因此,執行 ab 前,先要把這個選項調大,好比調成 65536:
# 臨時增大當前會話的最大文件描述符數 $ ulimit -n 65536
接下來,再去執行 ab 命令,進行壓力測試:
# -c 表示併發請求數爲 5000,-n 表示總的請求數爲 10 萬 # -r 表示套接字接收錯誤時仍然繼續執行,-s 表示設置每一個請求的超時時間爲 2s $ ab -c 5000 -n 100000 -r -s 2 http://192.168.0.30/ ... Requests per second: 6576.21 [#/sec] (mean) Time per request: 760.317 [ms] (mean) Time per request: 0.152 [ms] (mean, across all concurrent requests) Transfer rate: 5390.19 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 177 714.3 9 7338 Processing: 0 27 39.8 19 961 Waiting: 0 23 39.5 16 951 Total: 1 204 716.3 28 7349 ...
關於 ab 輸出界面的含義,我已經在 怎麼評估系統的網絡性能 文章中介紹過,忘了的話本身先去複習。從此次的界面,你能夠看出:
記住這幾個數值,這將是接下來案例的基準指標。
注意,你的機器中,運行結果跟個人可能不同,不過不要緊,並不影響接下來的案例分析思路。
接着,回到終端一,中止這個未使用 NAT 的 Nginx 應用:
docker rm -f nginx-hostnet
再執行下面的命令,啓動今天的案例應用。案例應用監聽在 8080 端口,而且使用了DNAT ,來實現 Host 的 8080 端口,到容器的 8080 端口的映射關係:
docker run --name nginx --privileged -p 8080:8080 -itd feisky/nginx:nat
Nginx 啓動後,你能夠執行 iptables 命令,確認 DNAT 規則已經建立:
iptables -nL -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL ... Chain DOCKER (2 references) target prot opt source destination RETURN all -- 0.0.0.0/0 0.0.0.0/0 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:8080
你能夠看到,在 PREROUTING 鏈中,目的爲本地的請求,會轉到 DOCKER 鏈;而在DOCKER 鏈中,目的端口爲 8080 的 tcp 請求,會被 DNAT 到 172.17.0.2 的 8080 端
口。其中,172.17.0.2 就是 Nginx 容器的 IP 地址。
接下來,咱們切換到終端二中,執行 curl 命令,確認 Nginx 已經正常啓動:
curl http://192.168.0.30:8080/ ... <p><em>Thank you for using nginx.</em></p> </body> </html>
而後,再次執行上述的 ab 命令,不過此次注意,要把請求的端口號換成 8080:
# -c 表示併發請求數爲 5000,-n 表示總的請求數爲 10 萬 # -r 表示套接字接收錯誤時仍然繼續執行,-s 表示設置每一個請求的超時時間爲 2s $ ab -c 5000 -n 100000 -r -s 2 http://192.168.0.30:8080/ ... apr_pollset_poll: The timeout specified has expired (70007) Total of 5602 requests completed
果真,剛纔正常運行的 ab ,如今失敗了,還報了鏈接超時的錯誤。運行 ab 時的 -s 參數,設置了每一個請求的超時時間爲 2s,而從輸出能夠看到,此次只完成了 5602 個請求。
既然是爲了獲得 ab 的測試結果,咱們不妨把超時時間延長一下試試,好比延長到 30s。延遲增大意味着要等更長時間,爲了快點獲得結果,咱們能夠同時把總測試次數,也減小到 10000:
ab -c 5000 -n 10000 -r -s 30 http://192.168.0.30:8080/ ... Requests per second: 76.47 [#/sec] (mean) Time per request: 65380.868 [ms] (mean) Time per request: 13.076 [ms] (mean, across all concurrent requests) Transfer rate: 44.79 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1300 5578.0 1 65184 Processing: 0 37916 59283.2 1 130682 Waiting: 0 2 8.7 1 414 Total: 1 39216 58711.6 1021 130682 ...
再從新看看 ab 的輸出,此次的結果顯示:
顯然,每一個指標都比前面差了不少。
那麼,碰到這種問題時,你會怎麼辦呢?你能夠根據前面的講解,先本身分析一下,再繼續學習下面的內容。
在上一節,咱們使用 tcpdump 抓包的方法,找出了延遲增大的根源。那麼今天的案例,
咱們仍然能夠用相似的方法尋找線索。不過,如今換個思路,由於今天咱們已經事先知道了問題的根源——那就是 NAT。
回憶一下 Netfilter 中,網絡包的流向以及 NAT 的原理,你會發現,要保證 NAT 正常工做,就至少須要兩個步驟:
是否是這兩個地方出現了問題呢?咱們用前面提到的動態追蹤工具 SystemTap 來試試。因爲今天案例是在壓測場景下,併發請求數大大下降,而且咱們清楚知道 NAT 是罪魁禍
首。因此,咱們有理由懷疑,內核中發生了丟包現象。
咱們能夠回到終端一中,建立一個 dropwatch.stp 的腳本文件,並寫入下面的內容:
#! /usr/bin/env stap ############################################################ # Dropwatch.stp # Author: Neil Horman <nhorman@redhat.com> # An example script to mimic the behavior of the dropwatch utility # http://fedorahosted.org/dropwatch ############################################################ # Array to hold the list of drop points we find global locations # Note when we turn the monitor on and off probe begin { printf("Monitoring for dropped packets\n") } probe end { printf("Stopping dropped packet monitor\n") } # increment a drop counter for every location we drop at probe kernel.trace("kfree_skb") { locations[$location] <<< 1 } # Every 5 seconds report our drop locations probe timer.sec(5) { printf("\n") foreach (l in locations-) { printf("%d packets dropped at %s\n", @count(locations[l]), symname(l)) } delete locations }
這個腳本,跟蹤內核函數 kfree_skb() 的調用,並統計丟包的位置。文件保存好後,執行下面的 stap 命令,就能夠運行丟包跟蹤腳本。這裏的 stap,是 SystemTap 的命令行工具:
stap --all-modules dropwatch.stp Monitoring for dropped packets
當你看到 probe begin 輸出的 「Monitoring for dropped packets」 時,代表SystemTap 已經將腳本編譯爲內核模塊,並啓動運行了。
接着,咱們切換到終端二中,再次執行 ab 命令:
ab -c 5000 -n 10000 -r -s 30 http://192.168.0.30:8080/
而後,再次回到終端一中,觀察 stap 命令的輸出:
10031 packets dropped at nf_hook_slow 676 packets dropped at tcp_v4_rcv 7284 packets dropped at nf_hook_slow 268 packets dropped at tcp_v4_rcv
實際測試代碼:
Monitoring for dropped packets 14185 packets dropped at nf_hook_slow 1200 packets dropped at tcp_v4_rcv 74 packets dropped at sk_stream_kill_queues 12 packets dropped at tcp_v4_do_rcv 2 packets dropped at br_stp_rcv 5279 packets dropped at nf_hook_slow 339 packets dropped at tcp_v4_rcv 3 packets dropped at br_stp_rcv 5376 packets dropped at nf_hook_slow 527 packets dropped at tcp_v4_rcv 3 packets dropped at br_stp_rcv 2 packets dropped at __udp4_lib_rcv 1 packets dropped at __udp4_lib_rcv 5208 packets dropped at nf_hook_slow 419 packets dropped at tcp_v4_rcv 2 packets dropped at br_stp_rcv 1 packets dropped at sk_stream_kill_queues
你會發現,大量丟包都發生在 nf_hook_slow 位置。看到這個名字,你應該能想到,這是在 Netfilter Hook 的鉤子函數中,出現丟包問題了。可是不是 NAT,還不能肯定。接下
來,咱們還得再跟蹤 nf_hook_slow 的執行過程,這一步能夠經過 perf 來完成。咱們切換到終端二中,再次執行 ab 命令:
ab -c 5000 -n 10000 -r -s 30 http://192.168.0.30:8080/
而後,再次切換回終端一,執行 perf record 和 perf report 命令
# 記錄一會(好比 30s)後按 Ctrl+C 結束 $ perf record -a -g -- sleep 30 # 輸出報告 $ perf report -g graph,0
在 perf report 界面中,輸入查找命令 / 而後,在彈出的對話框中,輸入 nf_hook_slow;
最後再展開調用棧,就能夠獲得下面這個調用圖:
實際測試截圖以下:
從這個圖咱們能夠看到,nf_hook_slow 調用最多的有三個地方,分別是ipv4_conntrack_in、br_nf_pre_routing 以及 iptable_nat_ipv4_in。換言之,
nf_hook_slow 主要在執行三個動做
第一,接收網絡包時,在鏈接跟蹤表中查找鏈接,併爲新的鏈接分配跟蹤對象(Bucket)。
第二,在 Linux 網橋中轉發包。這是由於案例 Nginx 是一個 Docker 容器,而容器的網絡經過網橋來實現;
第三,接收網絡包時,執行 DNAT,即把 8080 端口收到的包轉發給容器。
到這裏,咱們其實就找到了性能降低的三個來源。這三個來源,都是 Linux 的內核機制,因此接下來的優化,天然也是要從內核入手。
根據之前各個資源模塊的內容,咱們知道,Linux 內核爲用戶提供了大量的可配置選項,這些選項能夠經過 proc 文件系統,或者 sys 文件系統,來查看和修改。除此以外,你還
能夠用 sysctl 這個命令行工具,來查看和修改內核配置。
好比,咱們今天的主題是 DNAT,而 DNAT 的基礎是 conntrack,因此咱們能夠先看看,內核提供了哪些 conntrack 的配置選項。
咱們在終端一中,繼續執行下面的命令:
sysctl -a | grep conntrack net.netfilter.nf_conntrack_count = 180 net.netfilter.nf_conntrack_max = 1000 net.netfilter.nf_conntrack_buckets = 65536 net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60 net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120 ...
你能夠看到,這裏最重要的三個指標:
net.netfilter.nf_conntrack_count,表示當前鏈接跟蹤數;
net.netfilter.nf_conntrack_max,表示最大鏈接跟蹤數;
net.netfilter.nf_conntrack_buckets,表示鏈接跟蹤表的大小
因此,這個輸出告訴咱們,當前鏈接跟蹤數是 180,最大鏈接跟蹤數是 1000,鏈接跟蹤表的大小,則是 65536。
回想一下前面的 ab 命令,併發請求數是 5000,而請求數是 100000。顯然,跟蹤表設置成,只記錄 1000 個鏈接,是遠遠不夠的。
實際上,內核在工做異常時,會把異常信息記錄到日誌中。好比前面的 ab 測試,內核已經在日誌中報出了 「nf_conntrack: table full」 的錯誤。執行 dmesg 命令,你就能夠看到:
dmesg | tail [104235.156774] nf_conntrack: nf_conntrack: table full, dropping packet [104243.800401] net_ratelimit: 3939 callbacks suppressed [104243.800401] nf_conntrack: nf_conntrack: table full, dropping packet [104262.962157] nf_conntrack: nf_conntrack: table full, dropping packet
其中,net_ratelimit 表示有大量的日誌被壓縮掉了,這是內核預防日誌攻擊的一種措施。而當你看到 「nf_conntrack: table full」 的錯誤時,就代表 nf_conntrack_max 過小了。
那是否是,直接把鏈接跟蹤表調大就能夠了呢?調節前,你先得明白,鏈接跟蹤表,其實是內存中的一個哈希表。若是鏈接跟蹤數過大,也會耗費大量內存。
其實,咱們上面看到的 nf_conntrack_buckets,就是哈希表的大小。哈希表中的每一項,都是一個鏈表(稱爲 Bucket),而鏈表長度,就等於 nf_conntrack_max 除以
nf_conntrack_buckets。
好比,咱們能夠估算一下,上述配置的鏈接跟蹤表佔用的內存大小:
# 鏈接跟蹤對象大小爲 376,鏈表項大小爲 16 nf_conntrack_max* 鏈接跟蹤對象大小 +nf_conntrack_buckets* 鏈表項大小 = 1000*376+65536*16 B = 1.4 MB
接下來,咱們將 nf_conntrack_max 改大一些,好比改爲 131072(即nf_conntrack_buckets 的 2 倍):
sysctl -w net.netfilter.nf_conntrack_max=131072 $ sysctl -w net.netfilter.nf_conntrack_buckets=65536
而後再切換到終端二中,從新執行 ab 命令。注意,此次咱們把超時時間也改回原來的2s:
ab -c 5000 -n 100000 -r -s 2 http://192.168.0.30:8080/ ... Requests per second: 6315.99 [#/sec] (mean) Time per request: 791.641 [ms] (mean) Time per request: 0.158 [ms] (mean, across all concurrent requests) Transfer rate: 4985.15 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 355 793.7 29 7352 Processing: 8 311 855.9 51 14481 Waiting: 0 292 851.5 36 14481 Total: 15 666 1216.3 148 14645
實際測試結果:
[root@luoahong ~]# ab -c 5000 -n 100000 -r -s 2 http://192.168.118.89:8080/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 192.168.118.89 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests Server Software: nginx/1.15.8 Server Hostname: 192.168.118.89 Server Port: 8080 Document Path: / Document Length: 612 bytes Concurrency Level: 5000 Time taken for tests: 26.785 seconds Complete requests: 100000 Failed requests: 781 (Connect: 0, Receive: 0, Length: 778, Exceptions: 3) Write errors: 0 Total transferred: 83842590 bytes HTML transferred: 60723864 bytes Requests per second: 3733.47 [#/sec] (mean) Time per request: 1339.236 [ms] (mean) Time per request: 0.268 [ms] (mean, across all concurrent requests) Transfer rate: 3056.87 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 730 1478.1 87 15755 Processing: 9 402 941.7 94 22465 Waiting: 0 389 907.4 91 22464 Total: 14 1132 1771.0 461 22678 Percentage of the requests served within a certain time (ms) 50% 461 66% 1129 75% 1282 80% 1503 90% 3118 95% 3810 98% 7262 99% 8201 100% 22678 (longest request)
果真,如今你能夠看到:
這個結果,已經比剛纔的測試好了不少,也很接近最初不用 NAT 時的基準結果了。不過,你可能仍是很好奇,鏈接跟蹤表裏,到底都包含了哪些東西?這裏的東西,又是怎
麼刷新的呢?
實際上,你能夠用 conntrack 命令行工具,來查看鏈接跟蹤表的內容。好比:
# -L 表示列表,-o 表示以擴展格式顯示 $ conntrack -L -o extended | head ipv4 2 tcp 6 7 TIME_WAIT src=192.168.0.2 dst=192.168.0.96 sport=51744 dport=8080 src=172.17.0.2 dst=192.168.0.2 sport=8080 dport=51744 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 6 TIME_WAIT src=192.168.0.2 dst=192.168.0.96 sport=51524 dport=8080 src=172.17.0.2 dst=192.168.0.2 sport=8080 dport=51524 [ASSURED] mark=0 use=1
實際測試結果以下:
root@luoahong:~# conntrack -L -o extended | head ipv4 2 tcp 6 100 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=55447 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=55447 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 103 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=39769 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=39769 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 119 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=43054 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=43054 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 117 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=35335 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=35335 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 99 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=58149 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=58149 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 119 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=46923 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=46923 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 118 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=59380 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=59380 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 119 SYN_SENT src=192.168.118.87 dst=192.168.118.89 sport=50264 dport=8080 [UNREPLIED] src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=50264 mark=0 use=1 ipv4 2 tcp 6 118 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=43499 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=43499 [ASSURED] mark=0 use=1 ipv4 2 tcp 6 114 TIME_WAIT src=192.168.118.87 dst=192.168.118.89 sport=53573 dport=8080 src=172.17.0.2 dst=192.168.118.87 sport=8080 dport=53573 [ASSURED] mark=0 use=1
從這裏你能夠發現,鏈接跟蹤表裏的對象,包括了協議、鏈接狀態、源 IP、源端口、目的IP、目的端口、跟蹤狀態等。因爲這個格式是固定的,因此咱們能夠用 awk、sort 等工
具,對其進行統計分析。
好比,咱們仍是以 ab 爲例。在終端二啓動 ab 命令後,再回到終端一中,執行下面的命令:
# 統計總的鏈接跟蹤數 $ conntrack -L -o extended | wc -l 14289 # 統計 TCP 協議各個狀態的鏈接跟蹤數 $ conntrack -L -o extended | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}' SYN_RECV 4 CLOSE_WAIT 9 ESTABLISHED 2877 FIN_WAIT 3 SYN_SENT 2113 TIME_WAIT 9283 # 統計各個源 IP 的鏈接跟蹤數 $ conntrack -L -o extended | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10 14116 192.168.0.2 172 192.168.0.96
實際測試結果以下:
root@luoahong:~# conntrack -L -o extended | wc -l conntrack v1.4.4 (conntrack-tools): 15809 flow entries have been shown. 15809 root@luoahong:~# conntrack -L -o extended | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}' conntrack v1.4.4 (conntrack-tools): 16720 flow entries have been shown. LAST_ACK 210 SYN_RECV 225 CLOSE_WAIT 108 ESTABLISHED 1702 FIN_WAIT 180 SYN_SENT 2715 TIME_WAIT 11573 root@luoahong:~# conntrack -L -o extended | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10 conntrack v1.4.4 (conntrack-tools): 19852 flow entries have been shown. 19847 192.168.118.87 2 255.255.255.255 2 192.168.118.255 1 192.168.118.89
這裏統計了總鏈接跟蹤數,TCP 協議各個狀態的鏈接跟蹤數,以及各個源 IP 的鏈接跟蹤數。你能夠看到,大部分 TCP 的鏈接跟蹤,都處於 TIME_WAIT 狀態,而且它們大都來自
於 192.168.0.2 這個 IP 地址(也就是運行 ab 命令的 VM2)。
這些處於 TIME_WAIT 的鏈接跟蹤記錄,會在超時後清理,而默認的超時時間是 120s,你能夠執行下面的命令來查看:
因此,若是你的鏈接數很是大,確實也應該考慮,適當減少超時時間。除了上面這些常見配置,conntrack 還包含了其餘不少配置選項,你能夠根據實際須要,
參考 nf_conntrack 的文檔來配置。
今天,我帶你一塊兒學習了,如何排查和優化 NAT 帶來的性能問題。
因爲 NAT 基於 Linux 內核的鏈接跟蹤機制來實現。因此,在分析 NAT 性能問題時,咱們能夠先從 conntrack 角度來分析,好比用 systemtap、perf 等,分析內核中 conntrack
的行文;而後,經過調整 netfilter 內核選項的參數,來進行優化。
其實,Linux 這種經過鏈接跟蹤機制實現的 NAT,也常被稱爲有狀態的 NAT,而維護狀態,也帶來了很高的性能成本。
因此,除了調整內核行爲外,在不須要狀態跟蹤的場景下(好比只須要按預約的 IP 和端口進行映射,而不須要動態映射),咱們也可使用無狀態的 NAT (好比用 tc 或基於DPDK 開發),來進一步提高性能。