=======================Linux網絡優化篇====================
概念:
網絡七層模型:
應用層,負責為應用程序提供統一的接口。
表示層,負責把數據轉換成兼容接收系統的格式。
會話層,負責維護計算機之間的通信連接。
傳輸層,負責為數據加上傳輸表頭,形成數據包。
網絡層,負責數據的路由和轉發。
數據鏈路層,負責 MAC 尋址、錯誤偵測和改錯。
物理層,負責在物理網絡中傳輸數據幀
網絡四層模型:
應用層,負責向用戶提供一組應用程序,比如 HTTP、FTP、DNS 等。
傳輸層,負責端到端的通信,比如 TCP、UDP 等。
網絡層,負責網絡包的封裝、尋址和路由,比如 IP、ICMP 等。
網絡接口層,負責網絡包在物理網絡中的傳輸,比如 MAC 尋址、錯誤偵測以及通過網卡傳輸網絡幀等。
----------
網絡接口配置的最大傳輸單元(MTU),就規定了最大的 IP 包大小。在我們最常用的以太網中,MTU 默認值是 1500(這也是 Linux 的默認值)。一旦網絡包超過 MTU 的大小,就會在網絡層分片,以保證分片后的 IP 包不大於 MTU 值。顯然,MTU 越大,需要的分包也就越少,自然,網絡吞吐能力就越好。
===============================================================================
Linux網絡收發流程:
***接受過程:
當一個網絡幀到達網卡后,網卡會通過 DMA 方式,把這個網絡包放到收包隊列中;然后通過硬中斷,告訴中斷處理程序已經收到了網絡包。
接着,網卡中斷處理程序會為網絡幀分配內核數據結構(sk_buff),並將其拷貝到 sk_buff 緩沖區中;然后再通過軟中斷,通知內核收到了新的網絡幀。
接下來,內核協議棧從緩沖區中取出網絡幀,並通過網絡協議棧,從下到上逐層處理這個網絡幀。比如,
在鏈路層檢查報文的合法性,找出上層協議的類型(比如 IPv4 還是 IPv6),再去掉幀頭、幀尾,然后交給網絡層。
網絡層取出 IP 頭,判斷網絡包下一步的走向,比如是交給上層處理還是轉發。當網絡層確認這個包是要發送到本機后,就會取出上層協議的類型(比如 TCP 還是 UDP),去掉 IP 頭,再交給傳輸層處理。
傳輸層取出 TCP 頭或者 UDP 頭后,根據 < 源 IP、源端口、目的 IP、目的端口 > 四元組作為標識,找出對應的 Socket,並把數據拷貝到 Socket 的接收緩存中。
最后,應用程序就可以使用 Socket 接口,讀取到新接收到的數據了。
***發送過程
首先,應用程序調用 Socket API(比如 sendmsg)發送網絡包。
由於這是一個系統調用,所以會陷入到內核態的套接字層中。套接字層會把數據包放到 Socket 發送緩沖區中。
接下來,網絡協議棧從 Socket 發送緩沖區中,取出數據包;再按照 TCP/IP 棧,從上到下逐層處理。比如,傳輸層和網絡層,分別為其增加 TCP 頭和 IP 頭,執行路由查找確認下一跳的 IP,並按照 MTU 大小進行分片。
分片后的網絡包,再送到網絡接口層,進行物理地址尋址,以找到下一跳的 MAC 地址。然后添加幀頭和幀尾,放到發包隊列中。這一切完成后,會有軟中斷通知驅動程序:發包隊列中有新的網絡幀需要發送。
最后,驅動程序通過 DMA ,從發包隊列中讀出網絡幀,並通過物理網卡把它發送出去。
=================================================================================
性能指標:
帶寬,表示鏈路的最大傳輸速率,單位通常為 b/s (比特 / 秒)。
吞吐量,表示單位時間內成功傳輸的數據量,單位通常為 b/s(比特 / 秒)或者 B/s(字節 / 秒)。吞吐量受帶寬限制,而吞吐量 / 帶寬,也就是該網絡的使用率。
延時,表示從網絡請求發出后,一直到收到遠端響應,所需要的時間延遲。在不同場景中,這一指標可能會有不同含義。比如,它可以表示,建立連接需要的時間(比如 TCP 握手延時),或一個數據包往返所需的時間(比如 RTT)。
PPS,是 Packet Per Second(包 / 秒)的縮寫,表示以網絡包為單位的傳輸速率。PPS 通常用來評估網絡的轉發能力,比如硬件交換機,通常可以達到線性轉發(即 PPS 可以達到或者接近理論最大值)。而基於 Linux 服務器的轉發,則容易受網絡包大小的影響。
除了這些指標,網絡的可用性(網絡能否正常通信)、並發連接數(TCP 連接數量)、丟包率(丟包百分比)、重傳率(重新傳輸的網絡包比例)等也是常用的性能指標。
================================================================================
小記:ifconfig 和 ip 分別屬於軟件包 net-tools 和 iproute2,iproute2 是 net-tools 的下一代。通常情況下它們會在發行版中默認安裝。但如果你找不到 ifconfig 或者 ip 命令,可以安裝這兩個軟件包。
ifconfig 和 ip 命令輸出的指標基本相同
相關重要指標:
第一,網絡接口的狀態標志。ifconfig 輸出中的 RUNNING ,或 ip 輸出中的 LOWER_UP ,都表示物理網絡是連通的,即網卡已經連接到了交換機或者路由器中。如果你看不到它們,通常表示網線被拔掉了。
第二,MTU 的大小。MTU 默認大小是 1500,根據網絡架構的不同(比如是否使用了 VXLAN 等疊加網絡),你可能需要調大或者調小 MTU 的數值。
第三,網絡接口的 IP 地址、子網以及 MAC 地址。這些都是保障網絡功能正常工作所必需的,你需要確保配置正確。
第四,網絡收發的字節數、包數、錯誤數以及丟包情況,特別是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指標不為 0 時,通常表示出現了網絡 I/O 問題。其中:
errors 表示發生錯誤的數據包數,比如校驗錯誤、幀同步錯誤等;
dropped 表示丟棄的數據包數,即數據包已經收到了 Ring Buffer,但因為內存不足等原因丟包;
overruns 表示超限數據包數,即網絡 I/O 速度過快,導致 Ring Buffer 中的數據包來不及處理(隊列滿)而導致的丟包;
carrier 表示發生 carrirer 錯誤的數據包數,比如雙工模式不匹配、物理電纜出現問題等;
collisions 表示碰撞數據包數。
================================================================================
netstat ss命令各字段說明:
當套接字處於連接狀態(Established)時,
Recv-Q 表示套接字緩沖還沒有被應用程序取走的字節數(即接收隊列長度)。
而 Send-Q 表示還沒有被遠端主機確認的字節數(即發送隊列長度)。
當套接字處於監聽狀態(Listening)時,
Recv-Q 表示 syn backlog 的當前值。
而 Send-Q 表示最大的 syn backlog 值。
所謂半連接,就是還沒有完成 TCP 三次握手的連接,連接只進行了一半,而服務器收到了客戶端的 SYN 包后,就會把這個連接放到半連接隊列中,然后再向客戶端發送 SYN+ACK 包。
而全連接,則是指服務器收到了客戶端的 ACK,完成了 TCP 三次握手,然后就會把這個連接挪到全連接隊列中。這些全連接中的套接字,還需要再被 accept() 系統調用取走,這樣,服務器就可以開始真正處理客戶端的請求了。
netstat -s/ss -s #查看協議棧信息
================================================================================
網絡吞吐和pps
sar 增加-n參數就可以查看網絡的統計信息,比如網絡接口(DEV)、網絡接口錯誤(EDEV)、TCP、UDP、ICMP等
sar -n DEV 1 #獲取網絡接口統計信息,每秒輸出一組數據
各字段含義:
rxpck/s 和 txpck/s 分別是接收和發送的 PPS,單位為包 / 秒。
rxkB/s 和 txkB/s 分別是接收和發送的吞吐量,單位是 KB/ 秒。
rxcmp/s 和 txcmp/s 分別是接收和發送的壓縮數據包數,單位是包 / 秒。
%ifutil 是網絡接口的使用率,即半雙工模式下為 (rxkB/s+txkB/s)/Bandwidth,而全雙工模式下為 max(rxkB/s, txkB/s)/Bandwidth。
ethtool eth0 | grep Speed #查看網卡類型(千兆、萬兆)
===============================================================================
連通性和延時
ping -c3 ip地址 #測試連通性,發送3次icmp包后停止
第一部分,是每個 ICMP 請求的信息,包括 ICMP 序列號(icmp_seq)、TTL(生存時間,或者跳數)以及往返延時。
第二部分,則是三次 ICMP 請求的匯總。
===============================================================================
io模型優化---io的多路復用
io事件通知方式:
水平觸發:只要文件描述符可以非阻塞地執行 I/O ,就會觸發通知。也就是說,應用程序可以隨時檢查文件描述符的狀態,然后再根據狀態,進行 I/O 操作。
邊緣觸發:只有在文件描述符的狀態發生改變(也就是 I/O 請求達到)時,才發送一次通知。這時候,應用程序需要盡可能多地執行 I/O,直到無法繼續讀寫,才可以停止。如果 I/O 沒執行完,或者因為某種原因沒來得及處理,那么這次通知也就丟失了。
三種io多路復用的實現方式:.
*********第一種,使用非阻塞 I/O 和水平觸發通知,比如使用 select 或者 poll。
根據剛才水平觸發的原理,select 和 poll 需要從文件描述符列表中,找出哪些可以執行 I/O ,然后進行真正的網絡 I/O 讀寫。由於 I/O 是非阻塞的,一個線程中就可以同時監控一批套接字的文件描述符,這樣就達到了單線程處理多請求的目的。
所以,這種方式的最大優點,是對應用程序比較友好,它的 API 非常簡單。
但是,應用軟件使用 select 和 poll 時,需要對這些文件描述符列表進行輪詢,這樣,請求數多的時候就會比較耗時。並且,select 和 poll 還有一些其他的限制。
select 使用固定長度的位相量,表示文件描述符的集合,因此會有最大描述符數量的限制。比如,在 32 位系統中,默認限制是 1024。並且,在 select 內部,檢查套接字狀態是用輪詢的方法,再加上應用軟件使用時的輪詢,就變成了一個 O(n^2) 的關系。
而 poll 改進了 select 的表示方法,換成了一個沒有固定長度的數組,這樣就沒有了最大描述符數量的限制(當然還會受到系統文件描述符限制)。但應用程序在使用 poll 時,同樣需要對文件描述符列表進行輪詢,這樣,處理耗時跟描述符數量就是 O(N) 的關系。
除此之外,應用程序每次調用 select 和 poll 時,還需要把文件描述符的集合,從用戶空間傳入內核空間,由內核修改后,再傳出到用戶空間中。這一來一回的內核空間與用戶空間切換,也增加了處理成本。
有沒有什么更好的方式來處理呢?答案自然是肯定的。
********第二種,使用非阻塞 I/O 和邊緣觸發通知,比如 epoll。
既然 select 和 poll 有那么多的問題,就需要繼續對其進行優化,而 epoll 就很好地解決了這些問題。
epoll 使用紅黑樹,在內核中管理文件描述符的集合,這樣,就不需要應用程序在每次操作時都傳入、傳出這個集合。
epoll 使用事件驅動的機制,只關注有 I/O 事件發生的文件描述符,不需要輪詢掃描整個集合。
不過要注意,epoll 是在 Linux 2.6 中才新增的功能(2.4 雖然也有,但功能不完善)。由於邊緣觸發只在文件描述符可讀或可寫事件發生時才通知,那么應用程序就需要盡可能多地執行 I/O,並要處理更多的異常事件。
********第三種,使用異步 I/O(Asynchronous I/O,簡稱為 AIO)。在前面文件系統原理的內容中,我曾介紹過異步 I/O 與同步 I/O 的區別。異步 I/O 允許應用程序同時發起很多 I/O 操作,而不用等待這些操作完成。而在 I/O 完成后,系統會用事件通知(比如信號或者回調函數)的方式,告訴應用程序。這時,應用程序才會去查詢 I/O 操作的結果。
異步 I/O 也是到了 Linux 2.6 才支持的功能,並且在很長時間里都處於不完善的狀態,比如 glibc 提供的異步 I/O 庫,就一直被社區詬病。同時,由於異步 I/O 跟我們的直觀邏輯不太一樣,想要使用的話,一定要小心設計,其使用難度比較高。
======================================================================================
進程的工作模型:
第一種,主進程 + 多個 worker 子進程,這也是最常用的一種模型。這種方法的一個通用工作模式就是:
主進程執行 bind() + listen() 后,創建多個子進程;
然后,在每個子進程中,都通過 accept() 或 epoll_wait() ,來處理相同的套接字。
注:此種方式會存在驚群問題,此后通過全局鎖解決,誰得到鎖誰就去處理
第二種,監聽到相同端口的多進程模型。在這種方式下,所有的進程都監聽相同的接口,並且開啟 SO_REUSEPORT 選項,由內核負責將請求負載均衡到這些監聽進程中去。
由於內核確保了只有一個進程被喚醒,就不會出現驚群問題了
=======================================================================================
c1000k:
100萬並發請求的處理思路
假設每個請求需要 16KB 內存的話,那么總共就需要大約 15 GB 內存。
而從帶寬上來說,假設只有 20% 活躍連接,即使每個連接只需要 1KB/s 的吞吐量,總共也需要 1.6 Gb/s 的吞吐量。千兆網卡顯然滿足不了這么大的吞吐量,所以還需要配置萬兆網卡,或者基於多網卡 Bonding 承載更大的吞吐量。
其次,從軟件資源上來說,大量的連接也會占用大量的軟件資源,比如文件描述符的數量、連接狀態的跟蹤(CONNTRACK)、網絡協議棧的緩存大小(比如套接字讀寫緩存、TCP 讀寫緩存)等等。
最后,大量請求帶來的中斷處理,也會帶來非常高的處理成本。這樣,就需要多隊列網卡、中斷負載均衡、CPU 綁定、RPS/RFS(軟中斷負載均衡到多個 CPU 核上),以及將網絡包的處理卸載(Offload)到網絡設備(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各種硬件和軟件的優化。
C1000K 的解決方法,本質上還是構建在 epoll 的非阻塞 I/O 模型上。只不過,除了 I/O 模型之外,還需要從應用程序到 Linux 內核、再到 CPU、內存和網絡等各個層次的深度優化,特別是需要借助硬件,來卸載那些原來通過軟件處理的大量功能。
======================================================================================
c10m
1000萬並發請求處理思路
實際上,在 C1000K 問題中,各種軟件、硬件的優化很可能都已經做到頭了。特別是當升級完硬件(比如足夠多的內存、帶寬足夠大的網卡、更多的網絡功能卸載等)后,你可能會發現,無論你怎么優化應用程序和內核中的各種網絡參數,想實現 1000 萬請求的並發,都是極其困難的。
究其根本,還是 Linux 內核協議棧做了太多太繁重的工作。從網卡中斷帶來的硬中斷處理程序開始,到軟中斷中的各層網絡協議處理,最后再到應用程序,這個路徑實在是太長了,就會導致網絡包的處理優化,到了一定程度后,就無法更進一步了。
要解決這個問題,最重要就是跳過內核協議棧的冗長路徑,把網絡包直接送到要處理的應用程序那里去。這里有兩種常見的機制,DPDK 和 XDP。
第一種機制,DPDK,是用戶態網絡的標准。它跳過內核協議棧,直接由用戶態進程通過輪詢的方式,來處理網絡接收。
說起輪詢,你肯定會下意識認為它是低效的象征,但是進一步反問下自己,它的低效主要體現在哪里呢?是查詢時間明顯多於實際工作時間的情況下吧!那么,換個角度來想,如果每時每刻都有新的網絡包需要處理,輪詢的優勢就很明顯了。比如:
在 PPS 非常高的場景中,查詢時間比實際工作時間少了很多,絕大部分時間都在處理網絡包;
而跳過內核協議棧后,就省去了繁雜的硬中斷、軟中斷再到 Linux 網絡協議棧逐層處理的過程,應用程序可以針對應用的實際場景,有針對性地優化網絡包的處理邏輯,而不需要關注所有的細節。
此外,DPDK 還通過大頁、CPU 綁定、內存對齊、流水線並發等多種機制,優化網絡包的處理效率。
第二種機制,XDP(eXpress Data Path),則是 Linux 內核提供的一種高性能網絡數據路徑。它允許網絡包,在進入內核協議棧之前,就進行處理,也可以帶來更高的性能。XDP 底層跟我們之前用到的 bcc-tools 一樣,都是基於 Linux 內核的 eBPF 機制實現的。
你可以看到,XDP 對內核的要求比較高,需要的是 Linux 4.8 以上版本,並且它也不提供緩存隊列。基於 XDP 的應用程序通常是專用的網絡應用,常見的有 IDS(入侵檢測系統)、DDoS 防御、 cilium 容器網絡插件等。
========================================================================================
總結:
C10K 問題的根源,一方面在於系統有限的資源;另一方面,也是更重要的因素,是同步阻塞的 I/O 模型以及輪詢的套接字接口,限制了網絡事件的處理效率。Linux 2.6 中引入的 epoll ,完美解決了 C10K 的問題,現在的高性能網絡方案都基於 epoll。
從 C10K 到 C100K ,可能只需要增加系統的物理資源就可以滿足;但從 C100K 到 C1000K ,就不僅僅是增加物理資源就能解決的問題了。這時,就需要多方面的優化工作了,從硬件的中斷處理和網絡功能卸載、到網絡協議棧的文件描述符數量、連接狀態跟蹤、緩存隊列等內核的優化,再到應用程序的工作模型優化,都是考慮的重點。
再進一步,要實現 C10M ,就不只是增加物理資源,或者優化內核和應用程序可以解決的問題了。這時候,就需要用 XDP 的方式,在內核協議棧之前處理網絡包;或者用 DPDK 直接跳過網絡協議棧,在用戶空間通過輪詢的方式直接處理網絡包。
========================================================================================
各協議層的性能指標:
1.轉發性能:
hping3 作為一個測試網絡包處理能力的性能工具。syn攻擊的工具
pktgen是Linux內核自帶的高性能網絡測試工具,pktgen命令不能直接找到,因為pktgen作為內核來運行,需要你加載pktgen內核模塊后,再通過/proc文件系統來交互。
modprobe pktgen
ls /proc/pktgen
pktgen 在每個 CPU 上啟動一個內核線程,並可以通過 /proc/net/pktgen 下面的同名文件,跟這些線程交互;而 pgctrl 則主要用來控制這次測試的開啟和停止
注:如果 modprobe 命令執行失敗,說明你的內核沒有配置 CONFIG_NET_PKTGEN 選項。這就需要你配置 pktgen 內核模塊(即 CONFIG_NET_PKTGEN=m)后,重新編譯內核,才可以使用。
使用 pktgen 測試網絡性能時,需要先給每個內核線程 kpktgend_X 以及測試網卡,配置 pktgen 選項,然后再通過 pgctrl 啟動測試。
-------------------------------------------------------------------------
發包測試:
# 定義一個工具函數,方便后面配置各種測試選項
function pgset() {
local result
echo $1 > $PGDEV
result=`cat $PGDEV | fgrep "Result: OK:"`
if [ "$result" = "" ]; then
cat $PGDEV | fgrep Result:
fi
}
# 為 0 號線程綁定 eth0 網卡
PGDEV=/proc/net/pktgen/kpktgend_0
pgset "rem_device_all" # 清空網卡綁定
pgset "add_device eth0" # 添加 eth0 網卡
# 配置 eth0 網卡的測試選項
PGDEV=/proc/net/pktgen/eth0
pgset "count 1000000" # 總發包數量
pgset "delay 5000" # 不同包之間的發送延遲 (單位納秒)
pgset "clone_skb 0" # SKB 包復制
pgset "pkt_size 64" # 網絡包大小
pgset "dst 192.168.0.30" # 目的 IP
pgset "dst_mac 11:11:11:11:11:11" # 目的 MAC
# 啟動測試
PGDEV=/proc/net/pktgen/pgctrl
pgset "start"
查看測試報告:
cat /proc/net/pktgen/eth0
報告選項說明:
第一部分的 Params 是測試選項;
第二部分的 Current 是測試進度,其中, packts sofar(pkts-sofar)表示已經發送了100萬個包,也就表明測試已完成。
第三部分的 Result 是測試結果,包含測試所用時間、網絡包數量和分片、PPS、吞吐量以及錯誤數。
---------------------------------------------------------------------------
注:pktgen在內核4.4中不存在,在內核3.10中存在
========================================================================================
TCP/UDP性能:
tcp/udp的性能測試工具:iperf/netperf
---------------------------------------------------------
開啟一個端口監聽連接:
# -s 表示啟動服務端,-i 表示匯報間隔,-p 表示監聽端口
$ iperf3 -s -i 1 -p 10000
在另一個窗口進行連接測試:
# -c 表示啟動客戶端,192.168.0.30 為目標服務器的 IP
# -b 表示目標帶寬 (單位是 bits/s)
# -t 表示測試時間
# -P 表示並發數,-p 表示目標服務器監聽端口
$ iperf3 -c 192.168.0.30 -b 1G -t 15 -P 2 -p 10000
---------------------------------------------------------
========================================================================================
HTTP性能:
http性能測試工具:ab
---------------------------------------------------------
# -c 表示並發請求數為 1000,-n 表示總的請求數為 10000
$ ab -c 1000 -n 10000 http://192.168.0.30/
---------------------------------------------------------
========================================================================================
應用負載性能:
wrk、TCPCopy、Jmeter 或者 LoadRunner命令確認應用程序的實際負載能力
---------------------------------------------------------
安裝wrk:
$ https://github.com/wg/wrk/tree/4.1.0
$ cd wrk
$ yum install build-essential -y
$ make
$ sudo cp wrk /usr/local/bin/
測試nginx性能:
# -c 表示並發連接數 1000,-t 表示線程數為 2
$ wrk -c 1000 -t 2 http://192.168.0.30/
---------------------------------------------------------
=======================================================================================
DNS域名解析:
nslookup time.geekbang.org
# +trace 表示開啟跟蹤查詢
# +nodnssec 表示禁止 DNS 安全擴展
$ dig +trace +nodnssec time.geekbang.org
-------------------------------------------------------------------
dnsmasq是最常用的DNS緩存服務之一
DNS-dnsmasq安裝配置:
輕量級集合DNS,HTTP,TFTP軟件。
本初僅使用DNS功能。
用途 : 給本地局域網服務器提供:hosts主機記錄,自定義域名,以及公網域名DNS轉發解析。
集中配置內網服務器的hosts記錄,替代內網bind服務功能。
yum安裝
安裝
yum -y install dnsmasq
修改配置文件
cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
#重新填寫配置文件 /etc/dnsmasq.conf
##偵聽端口
port=53
##服務啟動用戶及用戶組
user=nobody
group=nobody
##業務偵聽地址 - interface 選項和 listen-address 選項可以同時使用
listen-address=10.10.10.10,127.0.0.1
##不加載本地的 /etc/hosts 文件
no-hosts
##添加讀取額外的 hosts 文件路徑,可以多次指定。如果指定為目錄,則讀取目錄中的所有文件。
addn-hosts=/data/dnsmasq/dnsmasq.hosts
##讀取目錄中的所有文件,文件更新將自動讀取
hostsdir=/data/dnsmasq/dnsmasq.d
##記錄dns查詢日志,如果指定 log-queries=extra 那么在每行開始處都有額外的日志信息。
log-queries
##設置日志記錄器
log-facility=/data/dnsmasq/log/dnsmasq.log
##異步log,緩解阻塞,提高性能。默認為5,最大100。
log-async=50
##指定 EDNS.0 UDP 包的最大尺寸,默認為 RFC5625 推薦的 edns-packet-max=4096
edns-packet-max=4096
##指定接口
interface=ens33
##指定不提供 DHCP 或 TFTP 服務的接口,僅提供 DNS 服務。
no-dhcp-interface=ens33
##指定 resolv-file 文件路徑(上游DNS服務器),默認/etc/resolv.dnsmasq
resolv-file=/data/dnsmasq/resolv.dnsmasq
##嚴格按照resolv.conf中的順序進行查找
strict-order
##重啟后清空緩存
clear-on-reload
##完整的域名才向上游服務器查找,如果僅僅是主機名僅查找hosts文件
domain-needed
##緩存條數,默認為150條,cache-size=0 禁用緩存。
cache-size=1000
##不緩存未知域名緩存,默認情況下dnsmasq緩存未知域名並直接返回為客戶端。
no-negcache
##指定DNS同屬查詢轉發數量
dns-forward-max=1000
創建相關配置文件及文件夾
mkdir -p /data/dnsmasq/{dnsmasq.d,log}
touch /data/dnsmasq/{dnsmasq.hosts,resolv.dnsmasq}
填寫DNS轉發服務器(提供非自定義域名查詢)
#新增配置 /data/dnsmasq/resolv.dnsmasq
nameserver 223.5.5.5
nameserver 1.2.4.8
填寫hosts主機記錄(提供域名hosts記錄集中查詢)
#新增配置 /data/dnsmasq/dnsmasq.hosts
10.10.10.10 test10
10.10.10.11 test11
10.10.10.12 test12
修改addn-hosts指定hosts記錄文件,需重啟dnsmasq,可以通過hostsdir指定域名配置文件添加解析。
填寫自定義域名(提供內網自定義域名查詢)
#新增配置文件 /data/dnsmasq/dnsmasq.d/k8s.test (為方便區分不同的二級域名,建議按二級域名創建配置文件)
10.10.10.11 etcd.k8s.test
啟動服務並設置開機啟動
systemctl start dnsmasq.service
systemctl enable dnsmasq.service
所有服務器設置DNS指向10.10.10.10
#修改配置項 /etc/sysconfig/network-scripts/ifcfg-eth0
PEERDNS=no #拒絕接受DHCP分發的DNS配置
DNS1=10.10.10.10 #自定義配置DNS服務器地址
#重啟網絡配置
systemctl restart network.service
其他DNS用法
添加指定泛域名通過指定DNS服務器解析(防域名被劫持,或者轉發指定域名解析)
#增加配置 /etc/dnsmasp.conf
server=/sohu.com/10.1.1.1
添加指定泛域名解析成指定IP (可用來屏蔽特定的域名)
#增加配置 /etc/dnsmasp.conf
address=/baidu.com/2.2.2.2
添加A記錄
#增加配置 /etc/dnsmasp.conf
host-record=test13.test,10.10.10.13
添加別名記錄(需要先添加源地址解析記錄,在添加別名記錄)
#增加配置 /data/dnsmasq/dnsmasq.d/test.test
10.10.10.20 20.test.test
#增加配置 /etc/dnsmasp.conf
cname=10.test.test,20.test.test
-------------------------------------------------------------------------
nslookup -type=PTR 35.190.27.188 8.8.8.8 #PTR反解析
==================================================================================
# -w 表示只輸出 HTTP 狀態碼及總時間,-o 表示將響應重定向到 /dev/null
$ curl -s -w 'Http code: %{http_code}\nTotal time:%{time_total}s\n' -o /dev/null http://192.168.0.30/
DDOS模擬與防護:
hping3模擬DDOS攻擊:
# -S 參數表示設置 TCP 協議的 SYN(同步序列號),-p 表示目的端口為 80
# -i u10 表示每隔 10 微秒發送一個網絡幀
$ hping3 -S -p 80 -i u10 192.168.0.30
iptables限制syn連接數:
# 限制 syn 並發數為每秒 1 次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
# 限制單個 IP 在 60 秒新建立的連接數為 10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT
#半連接容量配置:
查看:
$ sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 256
配置:
$ sysctl -w net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_max_syn_backlog = 1024
更改連接重試次數:
$ sysctl -w net.ipv4.tcp_synack_retries=1
net.ipv4.tcp_synack_retries = 1
#開啟SYN Cookies用於處理相同請求的連接
$ sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
#以上配置的永久生效配置:
$ cat /etc/sysctl.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_max_syn_backlog = 1024
sysctl -p
==================================================================================
hping3測試網絡延遲:
hping3 -c 3 -S -p 80 baidu.com
# -c 表示發送 3 次請求,-S 表示設置 TCP SYN,-p 表示端口號為 80
traceroute測試網絡延遲:
# --tcp 表示使用 TCP 協議,-p 表示端口號,-n 表示不對結果中的 IP 地址執行反向域名解析
traceroute --tcp -p 80 -n baidu.com
hping3 -c 3 -S -p 8080 192.168.0.30 #hping3壓力測試
tcpdump -nn tcp port 8080 -w nginx.pcap #tcpdump抓測試包保存
wrk --latency -c 100 -t 2 --timeout 2 http://192.168.0.30:8080/ #測試網絡延遲
strace -f wrk --latency -c 100 -t 2 --timeout 2 http://192.168.0.30:8080/ #strace命令追蹤命令的系統調用
注:大量小包發送,可能與tcp算法有關,Nagle算法,是TCP協議中用於減少小包發送數量的一種優化算法,目的是為了提高實際帶寬的利用率。tcp_nodelay off;nginx中這個關閉了,就會每次請求的包都會發送。開啟后可以提高發送效率。
=================================================================================
NAT原理:
靜態NAT,即內網ip與公網ip是一對一的永久映射關系;
動態NAT,即內網ip從公網ip池中,動態選擇一個進行映射;
網絡地址端口轉換NAPT,即把內網ip映射到公網ip的不同端口上,讓多個內網ip可以共享一個公網ip地址。
三類NAPT:
第一類是源地址轉換 SNAT,即目的地址不變,只替換源 IP 或源端口。SNAT 主要用於,多個內網 IP 共享同一個公網 IP ,來訪問外網資源的場景。
第二類是目的地址轉換 DNAT,即源 IP 保持不變,只替換目的 IP 或者目的端口。DNAT 主要通過公網 IP 的不同端口號,來訪問內網的多種服務,同時會隱藏后端服務器的真實 IP 地址。
第三類是雙向地址轉換,即同時使用 SNAT 和 DNAT。當接收到網絡包時,執行 DNAT,把目的 IP 轉換為內網 IP;而在發送網絡包時,執行 SNAT,把源 IP 替換為外部 IP。
雙向地址轉換,其實就是外網 IP 與內網 IP 的一對一映射關系,所以常用在虛擬化環境中,為虛擬機分配浮動的公網 IP 地址。
iptables包括 filter(用於過濾)、nat(用於 NAT)、mangle(用於修改分組數據) 和 raw(用於原始數據包)等。
SNAT轉換的兩種方式:
$ iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j MASQUERADE
$ iptables -t nat -A POSTROUTING -s 192.168.0.2 -j SNAT --to-source 100.100.100.100
DNAT轉換:
$ iptables -t nat -A PREROUTING -d 100.100.100.100 -j DNAT --to-destination 192.168.0.2
雙向地址轉換:
$ iptables -t nat -A POSTROUTING -s 192.168.0.2 -j SNAT --to-source 100.100.100.100
$ iptables -t nat -A PREROUTING -d 100.100.100.100 -j DNAT --to-destination 192.168.0.2
開啟Linux轉發功能:
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
$ sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
$ cat /etc/sysctl.conf | grep ip_forward
net.ipv4.ip_forward=1
=======================================================================================
systemtap工具:
SystemTap 是 Linux 的一種動態追蹤框架,它把用戶提供的腳本,轉換為內核模塊來執行,用來監測和跟蹤內核的行為。
---------------------------------------------
systemtap安裝:
# 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
---------------------------------------------
NAT正常工作至少需要兩個步驟:
第一,利用 Netfilter 中的鈎子函數(Hook),修改源地址或者目的地址。
第二,利用連接跟蹤模塊 conntrack ,關聯同一個連接的請求和響應。
寫systemtap的stap腳本:(不懂)
---------------------------------------------
#! /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
執行ab命令,可以在stap命令監控端看到追蹤的輸出
$ ab -c 5000 -n 10000 -r -s 30 http://192.168.0.30:8080/
通過perf record和perf report命令可以記錄ab壓測的調用
perf record -a -g -- sleep 30
perf report -g graph,0
# 統計總的連接跟蹤數
$ conntrack -L -o extended | wc -l
# 統計 TCP 協議各個狀態的連接跟蹤數
$ conntrack -L -o extended | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}'
# 統計各個源 IP 的連接跟蹤數
$ conntrack -L -o extended | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10
===================================================================================
網絡通信各層性能指標:
1.首先是網絡接口層和網絡層,它們主要負責網絡包的封裝、尋址、路由,以及發送和接收。每秒可處理的網絡包數 PPS,就是它們最重要的性能指標(特別是在小包的情況下)。你可以用內核自帶的發包工具 pktgen ,來測試 PPS 的性能。
2.再向上到傳輸層的 TCP 和 UDP,它們主要負責網絡傳輸。對它們而言,吞吐量(BPS)、連接數以及延遲,就是最重要的性能指標。你可以用 iperf 或 netperf ,來測試傳輸層的性能。
不過要注意,網絡包的大小,會直接影響這些指標的值。所以,通常,你需要測試一系列不同大小網絡包的性能。
3.最后,再往上到了應用層,最需要關注的是吞吐量(BPS)、每秒請求數以及延遲等指標。你可以用 wrk、ab 等工具,來測試應用程序的性能。
網絡性能優化:
1.從網絡io角度:
第一種是最常用的 I/O 多路復用技術 epoll,主要用來取代 select 和 poll。這其實是解決 C10K 問題的關鍵,也是目前很多網絡應用默認使用的機制。
第二種是使用異步 I/O(Asynchronous I/O,AIO)。AIO 允許應用程序同時發起很多 I/O 操作,而不用等待這些操作完成。等到 I/O 完成后,系統會用事件通知的方式,告訴應用程序結果。不過,AIO 的使用比較復雜,你需要小心處理很多邊緣情況。
2.從進程的工作模型:
第一種,主進程 + 多個 worker 子進程。其中,主進程負責管理網絡連接,而子進程負責實際的業務處理。這也是最常用的一種模型。
第二種,監聽到相同端口的多進程模型。在這種模型下,所有進程都會監聽相同接口,並且開啟 SO_REUSEPORT 選項,由內核負責,把請求負載均衡到這些監聽進程中去。
3.應用層網絡協議優化:
使用長連接取代短連接,可以顯著降低 TCP 建立連接的成本。在每秒請求次數較多時,這樣做的效果非常明顯。
使用內存等方式,來緩存不常變化的數據,可以降低網絡 I/O 次數,同時加快應用程序的響應速度。
使用 Protocol Buffer 等序列化的方式,壓縮網絡 I/O 的數據量,可以提高應用程序的吞吐。
使用 DNS 緩存、預取、HTTPDNS 等方式,減少 DNS 解析的延遲,也可以提升網絡 I/O 的整體速度。
4.套接字角度
增大每個套接字的緩沖區大小 net.core.optmem_max;
增大套接字接收緩沖區大小 net.core.rmem_max 和發送緩沖區大小 net.core.wmem_max;
增大 TCP 接收緩沖區大小 net.ipv4.tcp_rmem 和發送緩沖區大小 net.ipv4.tcp_wmem。
注:
tcp_rmem 和 tcp_wmem 的三個數值分別是 min,default,max,系統會根據這些設置,自動調整 TCP 接收 / 發送緩沖區的大小。
udp_mem 的三個數值分別是 min,pressure,max,系統會根據這些設置,自動調整 UDP 發送緩沖區的大小。
套接字接口額外配置選項:
為 TCP 連接設置 TCP_NODELAY 后,就可以禁用 Nagle 算法;
為 TCP 連接開啟 TCP_CORK 后,可以讓小包聚合成大包后再發送(注意會阻塞小包的發送);
使用 SO_SNDBUF 和 SO_RCVBUF ,可以分別調整套接字發送緩沖區和接收緩沖區的大小
5.tcp協議優化:
第一類,在請求數比較大的場景下,你可能會看到大量處於 TIME_WAIT 狀態的連接,它們會占用大量內存和端口資源。這時,我們可以優化與 TIME_WAIT 狀態相關的內核選項,比如采取下面幾種措施。
增大處於 TIME_WAIT 狀態的連接數量 net.ipv4.tcp_max_tw_buckets ,並增大連接跟蹤表的大小 net.netfilter.nf_conntrack_max。
減小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,讓系統盡快釋放它們所占用的資源。
開啟端口復用 net.ipv4.tcp_tw_reuse。這樣,被 TIME_WAIT 狀態占用的端口,還能用到新建的連接中。
增大本地端口的范圍 net.ipv4.ip_local_port_range 。這樣就可以支持更多連接,提高整體的並發能力。
增加最大文件描述符的數量。你可以使用 fs.nr_open 和 fs.file-max ,分別增大進程和系統的最大文件描述符數;或在應用程序的 systemd 配置文件中,配置 LimitNOFILE ,設置應用程序的最大文件描述符數。
第二類,為了緩解 SYN FLOOD 等,利用 TCP 協議特點進行攻擊而引發的性能問題,你可以考慮優化與 SYN 狀態相關的內核選項,比如采取下面幾種措施。
增大 TCP 半連接的最大數量 net.ipv4.tcp_max_syn_backlog ,或者開啟 TCP SYN Cookies net.ipv4.tcp_syncookies ,來繞開半連接數量限制的問題(注意,這兩個選項不可同時使用)。
減少 SYN_RECV 狀態的連接重傳 SYN+ACK 包的次數 net.ipv4.tcp_synack_retries。
第三類,在長連接的場景中,通常使用 Keepalive 來檢測 TCP 連接的狀態,以便對端連接斷開后,可以自動回收。但是,系統默認的 Keepalive 探測間隔和重試次數,一般都無法滿足應用程序的性能要求。所以,這時候你需要優化與 Keepalive 相關的內核選項,比如:
縮短最后一次數據包到 Keepalive 探測包的間隔時間 net.ipv4.tcp_keepalive_time;
縮短發送 Keepalive 探測包的間隔時間 net.ipv4.tcp_keepalive_intvl;
減少 Keepalive 探測失敗后,一直到通知應用程序前的重試次數 net.ipv4.tcp_keepalive_probes。
6.udp協議優化:
UDP 提供了面向數據報的網絡協議,它不需要網絡連接,也不提供可靠性保障。所以,UDP 優化,相對於 TCP 來說,要簡單得多。這里我也總結了常見的幾種優化方案。
跟上篇套接字部分提到的一樣,增大套接字緩沖區大小以及 UDP 緩沖區范圍;
跟前面 TCP 部分提到的一樣,增大本地端口號的范圍;
根據 MTU 大小,調整 UDP 數據包的大小,減少或者避免分片的發生。
7.網絡層優化:
網絡層,負責網絡包的封裝、尋址和路由,包括 IP、ICMP 等常見協議。在網絡層,最主要的優化,其實就是對路由、 IP 分片以及 ICMP 等進行調優。
第一種,從路由和轉發的角度出發,你可以調整下面的內核選項。
在需要轉發的服務器中,比如用作 NAT 網關的服務器或者使用 Docker 容器時,開啟 IP 轉發,即設置 net.ipv4.ip_forward = 1。
調整數據包的生存周期 TTL,比如設置 net.ipv4.ip_default_ttl = 64。注意,增大該值會降低系統性能。
開啟數據包的反向地址校驗,比如設置 net.ipv4.conf.eth0.rp_filter = 1。這樣可以防止 IP 欺騙,並減少偽造 IP 帶來的 DDoS 問題。
第二種,從分片的角度出發,最主要的是調整 MTU(Maximum Transmission Unit)的大小。
通常,MTU 的大小應該根據以太網的標准來設置。以太網標准規定,一個網絡幀最大為 1518B,那么去掉以太網頭部的 18B 后,剩余的 1500 就是以太網 MTU 的大小。
在使用 VXLAN、GRE 等疊加網絡技術時,要注意,網絡疊加會使原來的網絡包變大,導致 MTU 也需要調整。
比如,就以 VXLAN 為例,它在原來報文的基礎上,增加了 14B 的以太網頭部、 8B 的 VXLAN 頭部、8B 的 UDP 頭部以及 20B 的 IP 頭部。換句話說,每個包比原來增大了 50B。
所以,我們就需要把交換機、路由器等的 MTU,增大到 1550, 或者把 VXLAN 封包前(比如虛擬化環境中的虛擬網卡)的 MTU 減小為 1450。
另外,現在很多網絡設備都支持巨幀,如果是這種環境,你還可以把 MTU 調大為 9000,以提高網絡吞吐量。
第三種,從 ICMP 的角度出發,為了避免 ICMP 主機探測、ICMP Flood 等各種網絡問題,你可以通過內核選項,來限制 ICMP 的行為。
比如,你可以禁止 ICMP 協議,即設置 net.ipv4.icmp_echo_ignore_all = 1。這樣,外部主機就無法通過 ICMP 來探測主機。
或者,你還可以禁止廣播 ICMP,即設置 net.ipv4.icmp_echo_ignore_broadcasts = 1。
8.鏈路層優化:
網絡層的下面是鏈路層,所以最后,我們再來看鏈路層的優化方法。
鏈路層負責網絡包在物理網絡中的傳輸,比如 MAC 尋址、錯誤偵測以及通過網卡傳輸網絡幀等。自然,鏈路層的優化,也是圍繞這些基本功能進行的。接下來,我們從不同的幾個方面分別來看。
由於網卡收包后調用的中斷處理程序(特別是軟中斷),需要消耗大量的 CPU。所以,將這些中斷處理程序調度到不同的 CPU 上執行,就可以顯著提高網絡吞吐量。這通常可以采用下面兩種方法。
比如,你可以為網卡硬中斷配置 CPU 親和性(smp_affinity),或者開啟 irqbalance 服務。
再如,你可以開啟 RPS(Receive Packet Steering)和 RFS(Receive Flow Steering),將應用程序和軟中斷的處理,調度到相同 CPU 上,這樣就可以增加 CPU 緩存命中率,減少網絡延遲。
另外,現在的網卡都有很豐富的功能,原來在內核中通過軟件處理的功能,可以卸載到網卡中,通過硬件來執行。
TSO(TCP Segmentation Offload)和 UFO(UDP Fragmentation Offload):在 TCP/UDP 協議中直接發送大包;而 TCP 包的分段(按照 MSS 分段)和 UDP 的分片(按照 MTU 分片)功能,由網卡來完成 。
GSO(Generic Segmentation Offload):在網卡不支持 TSO/UFO 時,將 TCP/UDP 包的分段,延遲到進入網卡前再執行。這樣,不僅可以減少 CPU 的消耗,還可以在發生丟包時只重傳分段后的包。
LRO(Large Receive Offload):在接收 TCP 分段包時,由網卡將其組裝合並后,再交給上層網絡處理。不過要注意,在需要 IP 轉發的情況下,不能開啟 LRO,因為如果多個包的頭部信息不一致,LRO 合並會導致網絡包的校驗錯誤。
GRO(Generic Receive Offload):GRO 修復了 LRO 的缺陷,並且更為通用,同時支持 TCP 和 UDP。
RSS(Receive Side Scaling):也稱為多隊列接收,它基於硬件的多個接收隊列,來分配網絡接收進程,這樣可以讓多個 CPU 來處理接收到的網絡包。
VXLAN 卸載:也就是讓網卡來完成 VXLAN 的組包功能。
最后,對於網絡接口本身,也有很多方法,可以優化網絡的吞吐量。
比如,你可以開啟網絡接口的多隊列功能。這樣,每個隊列就可以用不同的中斷號,調度到不同 CPU 上執行,從而提升網絡的吞吐量。
再如,你可以增大網絡接口的緩沖區大小,以及隊列長度等,提升網絡傳輸的吞吐量(注意,這可能導致延遲增大)。
你還可以使用 Traffic Control 工具,為不同網絡流量配置 QoS。
9.C10M情況優化:
第一種,使用 DPDK 技術,跳過內核協議棧,直接由用戶態進程用輪詢的方式,來處理網絡請求。同時,再結合大頁、CPU 綁定、內存對齊、流水線並發等多種機制,優化網絡包的處理效率。
第二種,使用內核自帶的 XDP 技術,在網絡包進入內核協議棧前,就對其進行處理,這樣也可以實現很好的性能。
====================================================================================
關於網絡緩沖區的說法:
網卡收發網絡包時,通過 DMA 方式交互的環形緩沖區;
網卡中斷處理程序為網絡幀分配的,內核數據結構 sk_buff 緩沖區;
應用程序通過套接字接口,與網絡協議棧交互時的套接字緩沖區。
環形緩沖區,由於需要 DMA 與網卡交互,理應屬於網卡設備驅動的范圍。
sk_buff 緩沖區,是一個維護網絡幀結構的雙向鏈表,鏈表中的每一個元素都是一個網絡幀(Packet)。雖然 TCP/IP 協議棧分了好幾層,但上下不同層之間的傳遞,實際上只需要操作這個數據結構中的指針,而無需進行數據復制。
套接字緩沖區,則允許應用程序,給每個套接字配置不同大小的接收或發送緩沖區。應用程序發送數據,實際上就是將數據寫入緩沖區;而接收數據,其實就是從緩沖區中讀取。至於緩沖區中數據的進一步處理,則由傳輸層的 TCP 或 UDP 協議來完成。
sk_buff、套接字緩沖、連接跟蹤等,都通過 slab 分配器來管理。你可以直接通過 /proc/slabinfo,來查看它們占用的內存大小。
