家用網路瓶頸排查:從「打不開」到跑滿千兆

這篇記錄我把自己家的網路從「完全打不開」折騰到「跑滿千兆對稱」的全过程。方法論比結論有用 —— 後端面試很愛問「你怎麼定位一個慢的問題」,而家用網路剛好是這個問題最便宜、也最不會弄壞東西的練習場。

先給結論:我的三個假設裡有兩個是錯的,而且錯的方向相反。 一個讓我白花两天設計「繞開 CGNAT」的方案,另一個讓我差點花錢去升級設備。

起點:我到底想解決什麼

手上有一台四盤位 NAS,裡面跑 Docker。想要的是:人在外面時,能從公網訪問自己架的服務。

在动手之前我先给自己设了約束:不把管理後端與 SSH 暴露到公網、不開 UPnP、不設 DMZ。這不是保守,是因為家用環境沒有可寫的防火牆規則、也沒有第二個人會發現你被打穿了。

假設一:「我家一定是 CGNAT」—— 錯

CGNAT(Carrier-Grade NAT,運營商級網路地址轉換) 指的是運營商把自己池子里的公网地址輪流發給幾百個用戶,你路由器看到的 WAN IP 根本不是公網地址,而是運營商內部的一大層私有地址。判斷它有多要緊:在 CGNAT 下,任何端口轉發都不可能生效,因為公網沒人知道該把包送回哪個私網地址。

業界普遍assume「家庭寬帶大概率是 CGNAT」,我也就直接接受了,還花時間研究了幾種靠主动出站繞開它的方案(反向隧道那一類)。

然後我做了一件很笨但很正確的事:去看路由器自己顯示的 WAN IP,再比对我從公網看到的出口 IP。

  • 路由器狀態頁顯示的 WAN IPv4
  • 瀏覽器打開任何「我的 IP 是什麼」頁面顯示的 IPv4

兩者一模一樣,而且那個地址落在公網範圍內。到此就能推翻 CGNAT 假設 —— 如果中間還有一層運營商 NAT,這兩個值不可能相等。順帶確認了另外兩件事:沒有公網 IPv6;NAT 只有一層。

這一條值得單獨記

「兩個 IP 是否相同」是一個不需要任何工具、也不會誤判的測試。它比讀文章、比問人、比猜運營商政策都可靠。

教訓:把業界共識當成自己的現狀,是排查裡最貴的一種捷徑。 一個十分鐘能做完的實測,我拖了两天去設計一套並不需要存在的方案。

打通入站:端口轉發與一次性容器

確認有公網 IPv4 之後,路就清楚了:路由器做端口轉發(我那台 TP-Link 上這個功能叫「虛擬服务器」),NAS 裡起一個容器接住請求。

為了不牽動任何現有服務,我用一個一次性的 Nginx 容器做探針:bridge 網絡、只映射一個端口、測完就刪。這裡有一個我坚持的原則 —— 探測用的東西必須比正式服務更容易拆掉。

刪除順序也是測試的一部分:先刪兩條虛擬服务器規則,再刪容器,最後確認公網 IP 上不留任何開放端口。

判讀尺子:三種報錯對應三種完全不同的病因

這段是這篇最值钱的部分。同一個「打不開」,瀏覽器給的报错訊息其實把故障位置精確到了一層:

報錯包走到哪裡斷了最可能的病因
ERR_CONNECTION_TIMED_OUT包被丟棄,根本沒到主機運營商封端口、上游防火牆攔、或根本没路由可達
ERR_CONNECTION_REFUSED到了主機,但沒人监听,主機主動回了 RST端口轉發填錯、容器沒起來、或服務只綁了 127.0.0.1
ERR_SSL_PROTOCOL_ERROR鏈路完全通後端只說 HTTP,而客戶端在講 TLS —— 是協議不匹配,不是連不通

第三行是我實際上最常踩的:用 https:// 去访问一个只聽 HTTP 的端口,報的是 SSL 錯誤,看起來像「安全層出問題」,其實是通了。很多人會在这里回頭去折腾證書,方向完全錯。

反过来也成立:如果你拿到了 ERR_CONNECTION_REFUSED,那其實是个好消息 —— 說明路由、轉發、防火牆這一路都是通的,只剩最後一公里。

用這把尺子測下來:80 與 443 的入站都能到 NAS,沒有任何端口被上游屏蔽。這又推翻了一個我原本打算拿來當決策依據的前提(「到時可能要改用非标準端口」)。

假設二:「頻寬當然是夠的」—— 錯得最離譜的一個

連通性沒問題之後,我随手跑了一次 speedtest:93 下載 / 94 上載 Mbps。

我買的是千兆線路。93/94 這個數字太眼熟了 —— 它就是百兆乙太網(Fast Ethernet)的物理上限。千兆鏈路跑成這個樣子,幾乎只有一種解釋:某一跳協商到了 100 Mbps。

於是我沒有去怀疑運營商(那是最省事的結論),而是沿著資料路徑一節一節去找協商速率:

  1. NAS 網卡與路由器 LAN 口的連結速率 —— 正常千兆
  2. 路由器 WAN 側的協商速率 —— 卡在 100 Mbps

範圍就此收到一條線上:從 NAS 到路由器那段是成品跳線、明線直連,不會有问题;問題出在當年工程埋牆的那根線到 WAN 口這一段。它老到只在裡面壓了四芯(百兆只需要 4 根線,千兆要 8 根),或者外被已硬化導致接觸不良,鏈路自動降檔。

換掉那根線、接回 TP-Link 的 WAN 口之後:

換線前換線後
下載93 Mbps951.80 Mbps
上載94 Mbps944.04 Mbps
Ping—4 ms

我一分钱没花,拿回十倍頻寬。 而上傳能跑到 944 Mbps 這一事實,後面會變成「自建到底值不值」的關鍵論據 —— 家用寬帶的上傳頻寬是免費的,而公網服務的成本大头恰恰是出口頻寬。

為什麼這個診斷方法值得搬到工作上

「93/94 太接近 100」這個推理没有用任何專業工具,靠的是知道每一層的物理上限是多少。放到後端就是一樣的事:一個請求 200 ms,如果它比網路往返下限還小,那瓶頸一定在應用層;如果它剛好頂在某个連結的帶寬上限上,别去看代碼,去看那一跳的鏈路。 先問「這個數字像哪一層的上限」,再去抓包。 顺序反了會浪費很多時間。

剩下的唯一硬約束:IP 是動態的

連通性和頻寬都不是問題了,最後一根釘子:我的公網 IP 會變。 当天它就換了三次,而且跨了不同的地址段(大概是上游按 MAC 位址分配所致,所以不能假設會落回同一個區間)。

我沒寫具體值,只寫結論:任何自動化腳本都不能對 IP 段做假設,每次都必須真實探測。 於是清單上只剩一項:DDNS。搭配已驗證可用的端口轉發 + 反向代理 + Let’s Encrypt(HTTPS 憑證自動續期),這套東西就能自己活著。

順便記一個我說服自己放弃的方向:NAS 與路由器都有 2.5G 網口,但桌機是有線網卡是千兆的 Intel I219-V。桌面電腦到 NAS 這段的上限就是千兆,把線插到 2.5G 口對日常使用毫无收益。升級要在整條路徑上每一跳都夠快才有意義 —— 这和性能优化里「最慢的那一跳決定一切」是同一件事。

過程裡的紀律

這段排查之所以没把事情搞砸,靠的是三條規則:

  1. 探針要能一次刪乾淨。 一次性容器 + 獨立規則,测完立刻拆。
  2. 每換一個變量就重新測一次。 一次只改一處,否則你不知道是哪個動作起效了。
  3. 寫下每一步的觀測值,包括看似無用的。 那個 93/94 如果不是被我記下來,就不会有後面「它像百兆上限」這個聯想。

還沒有結案的部分

  • DDNS 還沒装。 這是入站自建唯一還缺的一塊。
  • 站點访问控制。 目前公網只暴露這個静态知識庫;等開始寫任何可能敏感的內容,前面就得加一道閘門。
  • 异機備份。 生成站点可以重建,原始筆記不行 —— 後者才是要備份的東西。

下一篇

这套排查的产出手册 —— 怎麼把筆記搭成可公開發布的靜態站點:把個人知識庫建起來並公開發布