日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NETGEAR拒绝连接请求_破案:Kubernetes/Docker 上无法解释的连接超时

發布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NETGEAR拒绝连接请求_破案:Kubernetes/Docker 上无法解释的连接超时 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

編譯:bot(才云)

技術校對:星空下的文仔(才云)

編者按:將應用遷移到 Kubernetes 時,有時候工程師們會發現一些令人費解的連接超時,無論怎么排查都找不到原因。在這篇文章中,軟件架構師 Maxime Lagresle分享了自己團隊的親身經驗。

Linux 內核有一個眾所周知的問題,就是在做 SNAT(修改數據包的源地址)時容易出現 SYN 丟包。

默認情況下,SNAT 一般使用 iptables 偽裝規則在 Docker 和 Flannel 的傳出連接上執行,當多個容器同時嘗試和同一外部地址建立新連接時,丟包情況就會發生。在一些情景下,可能兩個連接會被分配到一個端口用做地址轉換,導致一個或多個丟包,以及至少 1 秒的連接延遲。

Linux 的源代碼中提到了這個現象,內核也已經支持一個緩解此問題的 flag,但目前社區還沒有太多相關文檔。為了讓更多開發者,尤其是不熟悉 DNAT 的 Kubernetes 新手提前做好心理準備,在這篇文章中,我們將嘗試去調查這個問題,并尋找可靠的解決方案。

注:DNAT 上也存在相同的問題,這意味著在 Kubernetes 上,你訪問 ClusterIP 時也可能丟包。當你從 Pod 發送請求到 ClusterIP,默認情況下,kube-proxy(通過 iptables)會把 ClusterIP 改成你想要訪問的 Service 的某個 Pod IP,而該問題的存在會導致 DNS 解析域名時出現間歇性延遲,詳參 Issue 56903。

背景簡介

Maxime Lagresle 是某公司后端架構團隊的軟件架構師。他的團隊花了一年時間構建了一個 PaaS,經過謹慎評估,他們決定把基于 Capistrano/Marathon/Bash 的部署都遷移到 Kubernetes 上。

那時候,他們的設置依賴 Ubuntu Xenial 虛擬機(Docker v17.06)、Kubernetes v1.8,以及在主機網關模式下運行的 Flannel v1.9.0 。

在遷移時,Maxime Lagresle 團隊注意到,當把這些部署放到 Kubernetes 上運行時,應用程序中的連接超時會明顯增加。而在他們把第一個基于 Scala 的應用程序遷移上去后,超時的情況就更明顯了——相比平時的幾百毫秒,幾乎每一秒都會有一個響應非常慢的請求。但這個應用程序非常普通,只負責公開 REST 端點并查詢平臺上的其他 Service,收集、處理數據并將數據返回給客戶端。

經過觀察,他們發現請求的響應時間很奇怪,幾乎都被延遲了 1-3 秒。于是他們決定找出原因。

縮小問題范圍

在他們的待辦列表中,有一項任務是監控 KubeDNS 的表現。由于依賴 HTTP 客戶端,名稱解析時間可能是連接時間的一部分,他們決定先處理該任務,并確保該組件運行良好。

為了判斷這一猜想,Maxime Lagresle 團隊寫了一個小的 DaemonSet,它可以直接查詢 KubeDNS 和數據中心名稱服務器,并將響應時間發送到 InfluxDB。很快,圖表顯示響應時間非常快,名稱解析并不是延遲的罪魁禍首。

之后,他們又將重點轉移到探索這些延遲背后的含義上。負責該 Scala 應用的團隊先做了一些修改,使響應慢的請求在后臺繼續發送,并在向客戶端拋出超時錯誤后記錄持續時間。Maxime Lagresle 團隊則在運行應用程序的 Kubernetes 節點上做網絡跟蹤,嘗試將響應慢的請求與網絡轉儲的內容進行匹配。

10.244.38.20 嘗試連接到端口 80 上的 10.16.46.24

結果顯示,延遲是由第一個網絡數據包的重傳引起的,該數據包的作用是啟動連接(具有 SYN 標志的數據包)。這解釋了請求響應時間的延遲量為什么是 1-3 秒,因為這種數據包的重傳延遲是 1 秒(第二次)、3 秒(第三次)、6 秒、12 秒、24 秒……以此類推。

這是一個有趣的發現,因為丟失 SYN 數據包不是由隨機網絡故障導致的,而可能是網絡設備或 SYN 泛洪保護算法主動丟棄了新連接。

在默認的 Docker 安裝中,每個容器都有一個虛擬網絡接口(veth)上的 IP,它和主接口(如 eth0、docker0)一樣連接著 Docker 主機上的 Linux 網橋。容器通過網橋互相通信,如果容器想連接 Docker 主機外部地址,那么數據包會先進入網橋,再通過 eth0 路由到服務器外部。

下圖的示例已經對默認 Docker 設置進行了調整,以匹配網絡捕獲中看到的網絡配置:

實際上 veth 端口是成對出現的,但這里不需要關注這點

Maxime Lagresle 團隊隨機查看了網橋上的數據包,之后繼續查看虛擬機的主接口 eth0,并根據結果集中查看網絡基礎架構和虛擬機。

10.244.38.20 嘗試連接到端口 80 上的 10.16.46.24

網絡捕獲結果顯示,第一個 SYN 包在 13:42:23.828339 從容器網絡接口(veth)離開,經過網橋(cni0)。在 13:42:24.826211 待了 1 秒后,容器沒有從 10.16.46.24 得到響應就重傳了這個包。所以再一次,這個包會先出現在容器的網絡接口,然后是網橋。

在下一行,我們可以看到這個包在 IP 地址和端口從 10.244.38.20:38050 轉換成 10.16.34.2:10011 之后,在 13:42:24.826263 離開了 eth0。下一行顯示遠程服務是如何響應的。

很明顯,問題很可能出在虛擬機上,與其他基礎架構無關。為了配置更靈活的解決方案,他們編寫了一個非常簡單的 Go 程序,可以通過一些可配置的設置對端點發出請求:

  • 兩個請求之間的延遲;
  • 并發請求的數量;
  • 超時;
  • 要調用的端點。

要連接的遠程端點是具有 Nginx 的虛擬機,測試程序將針對此端點發出請求,并記錄任何高于一秒的響應時間。

Go 程序:https://github.com/maxlaverse/snat-race-conn-test

解決問題

已知數據包是在網橋之間丟包的,eth0 正是執行 SNAT 操作的地方。所以,如果因為某些原因,導致 Linux 內核無法分配一個空閑的源端口來做地址轉換,他們就看不到這個包出 eth0。

這里可以設一個簡單的測試來做驗證——嘗試 pod-to-pod 通信并記錄響應延遲的請求的數量。Maxime Lagresle 團隊做了測試,發現沒有丟包。接下來,他們準備深入看一下 conntrack。

注:為了更好地理解后續內容,建議讀者先了解一些關于源網絡地址轉換的知識。

用戶空間中的 conntrack

在整個過程中,他們提出了一系列猜想:

猜想一:大部分連接都被轉成相同的 host:port。

這一點已經被否定了。

猜想二:這個現象很可能是一些配置錯誤的 SYN 泛洪保護引起的。

他們檢查了網絡內核參數,并沒有找到自己不知道的機制;增加了 conntrack 表的大小,內核日志也沒有報錯。所以這個猜想也不正確。

猜想三:端口復用。如果端口耗盡并且沒有可用于 SNAT 操作的端口,那么數據包是可能會被丟棄或拒絕的。

他們查看了 conntrack 表,發現 conntrack 包有一個命令,可以顯示一些統計信息(conntrack -S)。在運行該命令時,有一個字段非常有趣:“insert_failed”具有非零值。

他們再次運行測試程序,并密切關注字段的統計信息,發現如果按一個丟包導致 1 秒請求延遲、兩個丟包導致 3 秒請求延遲來算,統計信息里的數據和丟包的數量是完全一致的!

man page 上有對那個字段的清楚描述:嘗試列表插入但失敗的條目數(如果已存在相同的條目,則會發生)。但這個描述沒有解答在什么情況下插入會失敗?無論如何,在低負載服務器發生丟包怎么看都不像是正常現象。

Netfilter NAT & Conntrack 內核模塊

在閱讀了 Netfilter 內核代碼之后,他們決定重新編譯它并添加一些跟蹤。

NAT 代碼在 POSTROUTING 鏈上被 hook 了兩次。首先,通過修改源 IP 和端口來修改包的結構;其次,如果期間沒有丟包,在 conntrack 表中記錄轉換。這意味著 SNAT 端口分配與表中的插入之間存在延遲,如果存在沖突和數據包丟失,則可能會導致插入失敗。

在 tcp 連接上執行 SNAT 時,NAT 模塊會做以下嘗試:

  • 第一步:如果數據包的源 IP 在目標 NAT 池中,并且元組可用,則返回(數據包保持不變);
  • 第二步:找到 NAT 池中使用最少的 IP,并用它替換數據包中的源 IP;
  • 第三步:檢查端口是否在允許的端口范圍內(默認為 1024-64512),以及具有該端口的元組是否可用。如果是,請返回(源 IP 已更改,端口保留)。注:SNAT 端口范圍不受內核參數值net.ipv4.ip_local_port_rangekernel 的影響;
  • 第四步:端口不可用,通過調用 nf_nat_l4proto_unique_tuple() 請求 tcp 層為 SNAT 找到一個唯一的端口。

按照上述步驟,當主機僅運行一個容器時,NAT 模塊很可能在第三步之后返回,容器內部進程使用的本地端口也將被保留并用于傳出連接。當 Docker 主機運行多個容器時,連接的源端口更可能已被另一個容器的連接使用。在這種情況下,調用 nf_nat_l4proto_unique_tuple () 以查找 NAT 操作的可用端口。

Netfilter 還支持另外兩種算法來查找 SNAT 空閑端口:

  • 部分隨機端口搜索初始位置分配。在 SNAT 規則有NF_NAT_RANGE_PROTO_RANDOM flag 的時候被使用;
  • 完全隨機端口搜索初始位置分配。在規則有 NF_NAT_RANGE_PROTO_RANDOM_FULLY flag 的時候被使用。

NF_NAT_RANGE_PROTO_RANDOM 雖然降低了兩個線程以相同初始端口啟動的次數,但仍有很多錯誤。Maxime Lagresle 團隊在使用 NF_NAT_RANGE_PROTO_RANDOM_FULLY 時,才發現 conntrack 表插入錯誤的次數明顯變少:在 Docker 測試虛擬機上,使用默認偽裝規則,且有 10-80 個線程連接到同一主機時,conntrack 表的插入失敗率大約在 2% 到 4%;如果在內核強制使用完全隨機,錯誤就降到了 0(在真實集群中確實也接近 0)。

激活 K8s 上的完全隨機端口分配

NF_NAT_RANGE_PROTO_RANDOM_FULLY flag 需要在偽裝規則中設置。在Maxime Lagresle 團隊的 Kubernetes 設置中,Flannel 負責添加這些規則。它在 Docker 鏡像構建期間從源代碼構建 iptables。iptables 工具不支持設置此 flag,但 Maxime Lagresle 團隊已經提交了一個合并的小補丁并添加了此功能。

Flannel 補丁:

https://gist.github.com/maxlaverse/1fb3bfdd2509e317194280f530158c98

他們現在使用的是應用了這個補丁的 Flannel 修改版本,并在偽裝規則上增加了 --random-fullyflag(4 行更改)。通過 DaemonSet,他們可以在每個節點上獲取 conntrack 統計信息,并將指標發送到 InfluxDB 以密切關注插入錯誤。

通過這個補丁,他們的錯誤數量已經從每個節點幾秒一次下降到整個集群幾小時一次,效果顯著。

小結

Kubernetes 正值快速發展,但上述問題卻到現在還存在著,討論它的人也不多,這一點是令人驚訝的。隨著越來越多應用程序連接到相同的端點,這一問題的嚴重性將被很快暴露出來。

我們需要采取一些額外的解決措施,因為每個人都在使用這種 DNS 循環,或者將 IP 添加到每個主機的 NAT 池。如果你在工作中曾遇到過這個問題,希望這篇文章對你有所幫助!

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的NETGEAR拒绝连接请求_破案:Kubernetes/Docker 上无法解释的连接超时的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。