程開發(fā)實(shí)踐)
WSL2 裝好之后,大部分人停在能在 Windows 里敲 Linux 命令這一步就滿足了。但只要你有第二臺(tái)機(jī)器——哪怕只是同一張桌子上的另一臺(tái)筆記本、一臺(tái)平板,或者公司里那臺(tái)常年開著的編譯服務(wù)器——你很快就會(huì)碰到一個(gè)很具體的需求:我要從別的設(shè)備直接連進(jìn)這臺(tái) WSL2,而不是每次都先遠(yuǎn)程桌面到 Windows 再開個(gè)終端窗口。這時(shí)候 NAT 模式就開始礙事了。這篇內(nèi)容講的就是我在這條路上來(lái)回折騰了幾輪之后沉淀下來(lái)的做法:把 WSL2 的網(wǎng)絡(luò)入口打通,用 SSH 作為統(tǒng)一入口,再讓 VSCode 的 Remote-SSH 直接落在這個(gè)入口上。適合已經(jīng)用過(guò) WSL2、能看懂基本 Linux 命令、但被 IP 漂移和端口轉(zhuǎn)發(fā)折磨過(guò)的開發(fā)者;如果你剛裝完 WSL2,建議先跑通基礎(chǔ)環(huán)境再回來(lái)看,不然容易被細(xì)節(jié)淹沒(méi)。1. 先想清楚:我為什么要逃離 NAT 模式1.1 那個(gè)每次重啟都會(huì)變的 172 開頭的地址WSL2 默認(rèn)跑在一個(gè)輕量虛擬機(jī)里,Windows 主機(jī)構(gòu)建了一個(gè)虛擬網(wǎng)卡給它做 NAT 轉(zhuǎn)發(fā)。你在 WSL2 里執(zhí)行ip addr,大概率會(huì)看到eth0上掛著一個(gè)172.x.x.x的地址。這個(gè)地址不是你路由器分配的,是 WSL 內(nèi)部的虛擬 DHCP 給的,而且每次wsl --shutdown之后再啟動(dòng),它大概率會(huì)變。這就是一切麻煩的源頭。我最早的做法很樸素:需要從別的機(jī)器連進(jìn)來(lái)時(shí),先跳回 Windows 敲一遍wsl hostname -I把地址抄下來(lái),再去另一臺(tái)機(jī)器上ssh user172.x.x.x。第一次還能忍,第三次就開始煩了,因?yàn)槟阌肋h(yuǎn)在重復(fù)同一件事,而且這個(gè)地址不能寫進(jìn)任何配置文件,更不能寫進(jìn)腳本——寫了第二天就失效。真正讓我下決心改造的,是我把一份構(gòu)建腳本扔到另一臺(tái)機(jī)器上跑,腳本里硬編碼了 WSL 的地址,隔天早上定時(shí)任務(wù)全線失敗。所以這里要先明確一個(gè)判斷標(biāo)準(zhǔn):如果你的使用場(chǎng)景里,連接發(fā)起方永遠(yuǎn)是 Windows 主機(jī)自己,那么 NAT 模式完全夠用,你甚至不需要任何額外配置,localhost轉(zhuǎn)發(fā)已經(jīng)幫你處理好了。只有當(dāng)連接發(fā)起方是別的設(shè)備或者別的容器時(shí),你才需要往下走。1.2 局域網(wǎng)里那臺(tái)設(shè)備連不進(jìn)來(lái)的真實(shí)原因很多人第一反應(yīng)是防火墻,關(guān)掉防火墻發(fā)現(xiàn)還是連不上,就懵了。其實(shí)原因很簡(jiǎn)單:NAT 模式下,WSL2 的那臺(tái)虛擬機(jī)和你的物理局域網(wǎng)之間隔了一層地址轉(zhuǎn)換。局域網(wǎng)里的設(shè)備發(fā)包給 Windows 主機(jī)的 IP,Windows 收到了,但它不知道該轉(zhuǎn)發(fā)給內(nèi)部的哪臺(tái)虛擬機(jī),于是直接丟棄。這不是防火墻攔的,是壓根沒(méi)有路由規(guī)則。這就解釋了為什么在 Windows 主機(jī)上ssh localhost能通,換成ssh 192.168.1.x就不通——前者走的是 WSL 自己注冊(cè)的本地回環(huán)轉(zhuǎn)發(fā),后者走的是物理網(wǎng)卡,兩條路徑完全不同。理解這一點(diǎn)非常關(guān)鍵,后面所有方案的取舍都建立在它之上:我們要做的不是打開某個(gè)開關(guān),而是在 NAT 邊界上鑿一個(gè)洞,或者干脆換掉 NAT 這層結(jié)構(gòu)。順便說(shuō)一個(gè)常見誤解:有人會(huì)去 WSL2 里執(zhí)行ip route看到默認(rèn)網(wǎng)關(guān)是172.x.x.1,就以為把這個(gè)網(wǎng)關(guān)改成路由器地址就能橋接。改完之后大概率是徹底斷網(wǎng),因?yàn)?WSL 的虛擬交換機(jī)不認(rèn)外部的 MAC 和路由,恢復(fù)起來(lái)還得wsl --shutdown重來(lái)。這條路我試過(guò),浪費(fèi)時(shí)間,不建議。2. 三條技術(shù)路線的真實(shí)取舍,別在錯(cuò)誤的方向上耗時(shí)間2.1 端口轉(zhuǎn)發(fā):最土但最可控的方案netsh interface portproxy是 Windows 自帶的端口轉(zhuǎn)發(fā)工具,原理是在 Windows 主機(jī)上監(jiān)聽一個(gè)端口,把流量轉(zhuǎn)發(fā)到 WSL2 的內(nèi)部地址。命令長(zhǎng)這樣,需要管理員權(quán)限的 PowerShell:# 假設(shè) WSL2 當(dāng)前地址是 172.28.100.5,想把宿主機(jī)的 2222 端口轉(zhuǎn)到 WSL 的 22 netsh interface portproxy add v4tov4 listenport2222 listenaddress0.0.0.0 connectport22 connectaddress172.28.100.5配套還要放行防火墻:New-NetFirewallRule -DisplayName WSL SSH 2222 -Direction Inbound -Protocol TCP -LocalPort 2222 -Action Allow這個(gè)方案最大的優(yōu)點(diǎn)是不改變?nèi)魏尉W(wǎng)絡(luò)結(jié)構(gòu),失敗了刪掉規(guī)則就恢復(fù)原樣,風(fēng)險(xiǎn)極低。缺點(diǎn)是它把IP 會(huì)變這個(gè)問(wèn)題原封不動(dòng)地留給了你:WSL 重啟、地址變了,轉(zhuǎn)發(fā)規(guī)則還指向舊地址,連接就會(huì)超時(shí),而且超時(shí)的表現(xiàn)和服務(wù)沒(méi)起來(lái)幾乎一模一樣,排查起來(lái)很迷惑。我的處理辦法是寫一個(gè)開機(jī)腳本,每次 WSL 啟動(dòng)后自動(dòng)刷新轉(zhuǎn)發(fā)規(guī)則。核心邏輯就是取當(dāng)前地址、刪舊規(guī)則、加新規(guī)則。這個(gè)腳本我放在任務(wù)計(jì)劃里,登錄時(shí)觸發(fā)。它不優(yōu)雅,但運(yùn)行了一年多沒(méi)出過(guò)問(wèn)題,原因很簡(jiǎn)單:它把不確定性收斂到了一個(gè)地方,你只需要盯住這一個(gè)腳本有沒(méi)有跑成功。2.2 鏡像網(wǎng)絡(luò)模式:最接近橋接體驗(yàn)的官方路子如果你用的是較新的 Windows 11 和較新的 WSL 版本,有一個(gè)更干凈的選擇:鏡像網(wǎng)絡(luò)模式(mirrored networking)。它讓 WSL2 直接復(fù)用 Windows 主機(jī)的網(wǎng)絡(luò)接口和地址,不再單獨(dú)做一層 NAT。開啟方式是在用戶目錄下建一個(gè).wslconfig文件,路徑是C:\Users\你的用戶名\.wslconfig,內(nèi)容:[wsl2] networkingModemirrored保存后執(zhí)行wsl --shutdown,等幾秒再啟動(dòng),進(jìn)去ip addr,你會(huì)發(fā)現(xiàn)網(wǎng)絡(luò)接口變了,地址和 Windows 主機(jī)在同一網(wǎng)段。此時(shí)在 WSL2 里跑的服務(wù),Windows 上用localhost:端口能直接訪問(wèn),局域網(wǎng)里的其他設(shè)備用Windows 主機(jī)的 IP 端口也能直接訪問(wèn),不需要任何portproxy規(guī)則。這個(gè)模式之所以被很多人叫作橋接,是因?yàn)閺氖褂皿w驗(yàn)上看它確實(shí)達(dá)到了橋接的效果:同一網(wǎng)段、可直接被外部訪問(wèn)、不用轉(zhuǎn)發(fā)。但從實(shí)現(xiàn)上說(shuō)它不是二層橋接,只是把主機(jī)的網(wǎng)絡(luò)棧共享給了 WSL。這個(gè)區(qū)別在實(shí)際使用中通常無(wú)關(guān)緊要,但在排查問(wèn)題時(shí)很重要——比如你會(huì)發(fā)現(xiàn) WSL 里看到的網(wǎng)卡列表和 Windows 上高度一致,包括那些虛擬網(wǎng)卡,這就是共享網(wǎng)絡(luò)棧的直接體現(xiàn)。鏡像模式有一個(gè)必須注意的前提:它要求較新的 WSL 版本。可以先跑wsl --version看一眼,如果命令本身報(bào)錯(cuò)說(shuō)不認(rèn)識(shí),說(shuō)明版本偏舊,先升級(jí)。另外在某些企業(yè)環(huán)境里,主機(jī)的網(wǎng)絡(luò)策略或安全軟件會(huì)對(duì)這種共享?xiàng)5男袨橛蓄~外限制,表現(xiàn)是開起來(lái)之后主機(jī)自己斷網(wǎng)了,遇到這種情況直接改回默認(rèn)模式,別硬剛。2.3 想用 Hyper-V 外部虛擬交換機(jī)做真橋接?我勸你放棄網(wǎng)上不少教程會(huì)告訴你:打開 Hyper-V 管理器,創(chuàng)建一個(gè)外部虛擬交換機(jī)綁定物理網(wǎng)卡,然后把 WSL 的虛擬網(wǎng)卡掛上去,這樣就實(shí)現(xiàn)了真正的橋接。這個(gè)說(shuō)法在早期的 Hyper-V 虛擬機(jī)上成立,但在 WSL2 上基本行不通。原因是 WSL2 的虛擬網(wǎng)卡由 WSL 自己管理,并不會(huì)以可以隨意掛載到外部交換機(jī)的形式暴露出來(lái),你在 Hyper-V 管理器里往往找不到對(duì)應(yīng)的可操作對(duì)象。更糟的是,一旦你在 Hyper-V 里創(chuàng)建了綁定物理網(wǎng)卡的外部交換機(jī),Windows 主機(jī)的網(wǎng)絡(luò)配置可能被改寫,輕則 IP 變了,重則直接斷網(wǎng),而恢復(fù)過(guò)程需要?jiǎng)h掉交換機(jī)、重置網(wǎng)卡,有時(shí)候還得重啟。我在這上面栽過(guò)一次,當(dāng)時(shí)以為是自己操作不對(duì),反復(fù)試了三遍,每次都把主機(jī)網(wǎng)絡(luò)搞掛,最后確認(rèn)這條路在 WSL2 上就是不成立的。把三條路線放在一起對(duì)比,結(jié)論就很清楚了:方案外部設(shè)備可訪問(wèn)IP 是否穩(wěn)定對(duì)主機(jī)網(wǎng)絡(luò)的侵入性恢復(fù)難度適用場(chǎng)景netsh 端口轉(zhuǎn)發(fā)是,但要放行防火墻否,需腳本刷新低,僅增加轉(zhuǎn)發(fā)規(guī)則刪規(guī)則即可老版本系統(tǒng)、企業(yè)受限環(huán)境鏡像網(wǎng)絡(luò)模式是,直接可達(dá)是,跟隨主機(jī)地址中,改變網(wǎng)絡(luò)棧結(jié)構(gòu)改回配置重啟新版本系統(tǒng),首選Hyper-V 外部交換機(jī)理論可行,實(shí)際難不確定高,可能打斷主機(jī)網(wǎng)絡(luò)高,常需重啟修復(fù)不建議我的實(shí)際選擇是:主力機(jī)用鏡像網(wǎng)絡(luò)模式,備用機(jī)上留一套端口轉(zhuǎn)發(fā)腳本兜底。兩套方案并存聽起來(lái)冗余,但正是這套冗余讓我在換機(jī)器、重裝系統(tǒng)、版本回退的幾種情況下都沒(méi)有卡住過(guò)。3. 讓 sshd 在 WSL2 里真正跑起來(lái)并活過(guò)重啟3.1 安裝與 sshd_config 里真正需要改的那幾行網(wǎng)絡(luò)通了只是修好了路,還得有個(gè)服務(wù)在門口收件。WSL2 的發(fā)行版里默認(rèn)通常沒(méi)裝 SSH 服務(wù)端,先裝:sudo apt update sudo apt install -y openssh-server裝完之后先別急著改配置,直接sudo service ssh start,然后在 Windows 上試著連一次。先跑通默認(rèn)配置,再逐項(xiàng)加固,這個(gè)順序很重要。我見過(guò)太多人一上來(lái)就把PermitRootLogin、PasswordAuthentication、AllowUsers全改了,結(jié)果連不上,還得回頭一項(xiàng)項(xiàng)排查到底是哪一行出的問(wèn)題。默認(rèn)配置跑通之后,打開/etc/ssh/sshd_config,真正需要?jiǎng)拥钠鋵?shí)就三處:# 明確監(jiān)聽所有地址,排除只綁回環(huán)的可能 ListenAddress 0.0.0.0 # 端口按需改,避開 Windows 主機(jī)上可能已被占用的 22 Port 2222 # 密鑰登錄為主,密碼登錄視安全要求關(guān)閉 PubkeyAuthentication yes PasswordAuthentication no這里解釋一下為什么把端口改掉。Windows 10/11 自帶一個(gè)可選的 OpenSSH Server 功能,如果你之前手滑裝過(guò)并啟用了它,Windows 側(cè)的 22 端口就被占了。此時(shí)你在 WSL 里也開 22,再用端口轉(zhuǎn)發(fā)或者鏡像模式,就會(huì)出現(xiàn)連接上了但認(rèn)證的是 Windows 那個(gè)服務(wù)的詭異現(xiàn)象——表現(xiàn)是用戶名密碼怎么輸都不對(duì),而且報(bào)錯(cuò)信息完全看不出是連錯(cuò)了地方。換一個(gè)非標(biāo)準(zhǔn)端口(2222、2202之類)能一勞永逸地躲開這個(gè)坑。改完配置先用sudo sshd -t做語(yǔ)法檢查,這個(gè)命令只校驗(yàn)不啟動(dòng),能提前發(fā)現(xiàn)拼寫錯(cuò)誤。確認(rèn)無(wú)誤再重啟服務(wù)。3.2 密鑰登錄:那兩個(gè)權(quán)限數(shù)字比密鑰本身更容易出錯(cuò)生成密鑰對(duì)在 Windows 側(cè)做就行,ssh-keygen -t ed25519 -f $HOME\.ssh\id_ed25519_wsl,然后把公鑰內(nèi)容追加到 WSL2 里的~/.ssh/authorized_keys。這一步看起來(lái)簡(jiǎn)單,但權(quán)限問(wèn)題幾乎人人都踩過(guò)。OpenSSH 對(duì)權(quán)限的檢查非常嚴(yán)格,而且它的檢查邏輯在服務(wù)端:如果~/.ssh或authorized_keys的權(quán)限過(guò)于寬松(比如組用戶或其他用戶可寫),它會(huì)靜默拒絕使用這個(gè)密鑰,日志里可能只有一句很含糊的 Authentication refused: bad ownership or modes。修復(fù)方式:chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys還有兩個(gè)容易忽略的點(diǎn)。第一,~/.ssh的所有上級(jí)目錄也不能對(duì)組用戶或其他用戶可寫,如果你把家目錄權(quán)限改成了777,密鑰同樣會(huì)失效。第二,如果 WSL2 里的用戶是通過(guò) Windows 用戶映射進(jìn)來(lái)的,注意authorized_keys要放在實(shí)際登錄用戶的家目錄下,而不是 root 的目錄下。我見過(guò)有人把公鑰追加到了sudo之后的 root 家目錄,然后一直困惑為什么用普通用戶登錄不進(jìn)去。密鑰驗(yàn)證是否真的生效,有個(gè)快速判斷方法:ssh -v看輸出里有沒(méi)有Offering public key以及緊隨其后的Server accepts key。如果只有前者沒(méi)有后者,基本就是服務(wù)端權(quán)限檢查沒(méi)過(guò);如果連前者都沒(méi)有,說(shuō)明客戶端的IdentityFile路徑寫錯(cuò)了。這個(gè)二分法能省掉大量瞎猜的時(shí)間。3.3 開機(jī)自啟:別指望手動(dòng)service ssh startWSL2 的實(shí)例在你不使用時(shí)會(huì)自動(dòng)休眠,下次訪問(wèn)時(shí)重新拉起。這意味著你手動(dòng)start的 sshd,在下一次實(shí)例重啟后就不在了。表現(xiàn)是今天配好了,明天早上連不上,進(jìn)去一看進(jìn)程沒(méi)了。這個(gè)問(wèn)題如果不解決,整套配置就是一次性的。兩種接管方式。第一種是啟用 systemd,在/etc/wsl.conf里寫:[boot] systemdtrue保存后wsl --shutdown,重進(jìn),systemctl status能正常輸出就說(shuō)明 systemd 起來(lái)了,然后sudo systemctl enable --now ssh即可實(shí)現(xiàn)開機(jī)自啟。這是目前更推薦的方式,因?yàn)楹芏嗥渌?wù)也依賴 systemd。第二種是不啟用 systemd,直接用wsl.conf的 boot 命令:[boot] command service ssh start這個(gè)方式更輕,不改變 init 系統(tǒng),適合那些不想動(dòng) systemd 的環(huán)境。缺點(diǎn)是它只能執(zhí)行單條命令,想同時(shí)啟動(dòng)多個(gè)服務(wù)就得包一層腳本。我的建議是:如果你的發(fā)行版和 WSL 版本支持 systemd,就用 systemd。理由不是 systemd 更好,而是生態(tài)更一致——你在別的 Linux 機(jī)器上學(xué)到的systemctl enable經(jīng)驗(yàn)可以直接復(fù)用,不用為 WSL 單獨(dú)記一套。4. 把 VSCode 的 Remote-SSH 接到這個(gè)入口上4.1 config 文件怎么寫才不用每次輸一堆參數(shù)VSCode 的 Remote-SSH 擴(kuò)展讀取的是標(biāo)準(zhǔn) SSH 配置文件,Windows 側(cè)路徑是C:\Users\用戶名\.ssh\config。寫好之后 VSCode 左側(cè)遠(yuǎn)程資源管理器里會(huì)直接列出這個(gè)主機(jī)名,點(diǎn)一下就能連:Host wsl-dev HostName 127.0.0.1 Port 2222 User devuser IdentityFile ~/.ssh/id_ed25519_wsl ServerAliveInterval 30 ServerAliveCountMax 3這里有幾個(gè)參數(shù)值得說(shuō)明。HostName填127.0.0.1是配合端口轉(zhuǎn)發(fā)或鏡像模式的寫法,因?yàn)樽罱K暴露端口的是 Windows 主機(jī);如果你走的是純局域網(wǎng)直連,就填主機(jī)的局域網(wǎng) IP。ServerAliveInterval 30是每 30 秒發(fā)一次心跳,配合ServerAliveCountMax 3,能在網(wǎng)絡(luò)短暫抖動(dòng)時(shí)避免連接被判定為斷開——WSL2 實(shí)例休眠再喚醒的場(chǎng)景下,這個(gè)設(shè)置能顯著減少連接已關(guān)閉的提示頻率。IdentityFile里的~在 Windows 的 SSH 客戶端下會(huì)解析到用戶目錄,一般沒(méi)問(wèn)題,但如果你在 VSCode 里遇到找不到密鑰的報(bào)錯(cuò),直接改成絕對(duì)路徑C:/Users/xxx/.ssh/id_ed25519_wsl,注意用正斜杠。還有一個(gè)細(xì)節(jié):改完 config 之后,如果連接行為沒(méi)變化,可能是 VSCode 緩存了舊配置。這時(shí)候執(zhí)行一次命令面板里的Remote-SSH: Kill VS Code Server on Host,清掉遠(yuǎn)端的服務(wù)端進(jìn)程,再重連,通常就恢復(fù)正常了。4.2 首次連接會(huì)遇到的三道坎第一道是 host key 確認(rèn)。首次連接會(huì)彈出一個(gè)指紋確認(rèn),選繼續(xù)即可。但如果你之前用同一個(gè) IP 和端口連過(guò)別的機(jī)器,會(huì)遇到REMOTE HOST IDENTIFICATION HAS CHANGED的硬性拒絕。這是因?yàn)榕f指紋還留在known_hosts里。處理方式是ssh-keygen -R [127.0.0.1]:2222,注意帶端口時(shí)要加方括號(hào)和引號(hào),不帶端口的話格式不同,寫錯(cuò)了刪不掉。這個(gè)報(bào)錯(cuò)看起來(lái)嚇人,實(shí)際上是保護(hù)機(jī)制在正常工作,不用緊張。第二道是遠(yuǎn)端服務(wù)端安裝。Remote-SSH 連接成功后,會(huì)在遠(yuǎn)端自動(dòng)下載并安裝一個(gè)輕量的服務(wù)端組件。這個(gè)過(guò)程需要遠(yuǎn)端能訪問(wèn)下載源。如果 WSL2 里的網(wǎng)絡(luò)出口有問(wèn)題,就會(huì)卡在 Setting up SSH Host 這一步很久最后失敗。遇到這種情況,可以看 VSCode 的輸出面板里 Remote-SSH 通道的具體報(bào)錯(cuò),通常會(huì)給出下載地址和失敗原因。實(shí)在裝不上,也可以手動(dòng)把安裝包放進(jìn)遠(yuǎn)端指定目錄,不過(guò)這條路比較繞,優(yōu)先解決網(wǎng)絡(luò)出口問(wèn)題。第三道是擴(kuò)展的安裝位置。這是新手最容易困惑的地方:Remote-SSH 連上之后,你的工作區(qū)在遠(yuǎn)端,大部分?jǐn)U展需要安裝到遠(yuǎn)端。在擴(kuò)展面板里,已安裝的擴(kuò)展會(huì)顯示一個(gè)在 SSH: wsl-dev 上安裝的按鈕,點(diǎn)一下才會(huì)在遠(yuǎn)端生效。像語(yǔ)言服務(wù)、調(diào)試器、格式化工具這些必須裝遠(yuǎn)端;主題、快捷鍵這類界面相關(guān)的裝本地就行。搞混了的表現(xiàn)是擴(kuò)展明明裝了但就是不工作,排查半天發(fā)現(xiàn)裝錯(cuò)了位置。4.3 WSL 擴(kuò)展和 Remote-SSH,到底該用哪個(gè)這里必須說(shuō)清楚一個(gè)容易混淆的點(diǎn):VSCode 有一個(gè)官方的 WSL 擴(kuò)展,它直接對(duì)接 WSL2 實(shí)例,不需要 SSH,也不需要網(wǎng)絡(luò)配置,打開文件夾時(shí)選 Connect to WSL 就行。那既然有這個(gè),為什么還要折騰 Remote-SSH?答案是使用場(chǎng)景不同。WSL 擴(kuò)展要求 VSCode 跑在 Windows 主機(jī)上,它通過(guò)本地管道和 WSL 通信。如果你的開發(fā)機(jī)就是這臺(tái) Windows,那 WSL 擴(kuò)展是更省事的選擇,網(wǎng)絡(luò)部分完全不用管。但如果你的場(chǎng)景是:代碼跑在 A 機(jī)器的 WSL2 里,而你在 B 機(jī)器上寫代碼,那 WSL 擴(kuò)展就無(wú)能為力了,因?yàn)樗荒苓B本機(jī)的 WSL。這時(shí)候 Remote-SSH 才是正解,因?yàn)樗举|(zhì)上是通過(guò) SSH 連一臺(tái) Linux 機(jī)器,至于那臺(tái)機(jī)器是物理機(jī)、虛擬機(jī)還是 WSL2,它并不關(guān)心。我自己的用法是兩者并存:坐在主力機(jī)前的時(shí)候,用 WSL 擴(kuò)展,啟動(dòng)快、零配置;出門用筆記本的時(shí)候,用 Remote-SSH 連回主力機(jī)的 WSL2。兩個(gè)入口指向同一份代碼,互不干擾。需要注意的是,不要在兩邊同時(shí)打開同一個(gè)項(xiàng)目并同時(shí)修改,雖然不會(huì)立刻沖突,但文件監(jiān)聽和自動(dòng)格式化可能互相打架,造成難以復(fù)現(xiàn)的奇怪問(wèn)題。5. 那些讓我卡了半天的坑,以及我的排查順序5.1 服務(wù)在跑但連不上:從監(jiān)聽地址開始查最典型的現(xiàn)象:WSL2 里ps aux | grep sshd明明有進(jìn)程,service ssh status也說(shuō) running,但外面就是連不上。這時(shí)候別急著懷疑網(wǎng)絡(luò),先確認(rèn)它到底監(jiān)聽在哪個(gè)地址上:ss -tlnp | grep sshd如果輸出是127.0.0.1:2222而不是0.0.0.0:2222或[::]:2222,那問(wèn)題就找到了——它只監(jiān)聽回環(huán),外部流量進(jìn)不來(lái)。原因通常是sshd_config里有ListenAddress 127.0.0.1的殘留,或者被某個(gè)覆蓋配置改掉了。改成0.0.0.0重啟即可。我的排查順序固定為四步,基本能覆蓋九成情況:第一步看監(jiān)聽地址(ss -tlnp),第二步從本機(jī)連一次(ssh -p 2222 user127.0.0.1,排除認(rèn)證問(wèn)題),第三步從 Windows 連一次(區(qū)分是 WSL 內(nèi)部問(wèn)題還是轉(zhuǎn)發(fā)問(wèn)題),第四步從局域網(wǎng)第三臺(tái)設(shè)備連一次(區(qū)分是主機(jī)防火墻問(wèn)題還是路由問(wèn)題)。每一步只增加一個(gè)變量,這樣一旦某一步失敗,問(wèn)題范圍立刻縮小一半。5.2 開了鏡像模式,宿主機(jī)反而連不上了這個(gè)坑很反直覺:改配置之前l(fā)ocalhost能連,改成鏡像模式之后反而連不上了。原因通常是端口沖突——鏡像模式下,WSL2 和 Windows 主機(jī)共享網(wǎng)絡(luò)端口空間,如果 Windows 上已經(jīng)有進(jìn)程占了那個(gè)端口(比如前面提過(guò)的 Windows 自帶 SSH 服務(wù)占了 22),WSL 里的服務(wù)就綁不上,或者綁上了但流量被 Windows 那個(gè)進(jìn)程接走了。驗(yàn)證方法很直接:在 Windows 上netstat -ano | findstr :2222,看有沒(méi)有別的 PID 占著。有的話要么換端口,要么把占用進(jìn)程停掉。我現(xiàn)在的習(xí)慣是給 WSL 的 SSH 固定用一個(gè)不常見的端口,徹底避開這類沖突,省得每次都要查。還有一種情況是防火墻的配置文件區(qū)分。Windows 防火墻對(duì)專用網(wǎng)絡(luò)和公用網(wǎng)絡(luò)的規(guī)則是分開的,你放行了專用網(wǎng)絡(luò),但當(dāng)前網(wǎng)卡被識(shí)別成公用網(wǎng)絡(luò),規(guī)則就不生效。表現(xiàn)是主機(jī)自己能連、局域網(wǎng)連不上。檢查方式是看網(wǎng)絡(luò)屬性里的配置文件類型,如果顯示公用,要么切換成專用,要么在防火墻規(guī)則里把公用網(wǎng)絡(luò)也勾上。5.3 IP 漂移導(dǎo)致的間歇性失敗,以及我怎么把它固定下來(lái)端口轉(zhuǎn)發(fā)方案下,最煩的不是配置麻煩,而是它會(huì)在你完全沒(méi)預(yù)期的時(shí)候失效。某天早上連不上,進(jìn)去一看,WSL 地址從172.28.100.5變成了172.30.200.8,轉(zhuǎn)發(fā)規(guī)則還指著舊地址。這種失敗沒(méi)有任何報(bào)錯(cuò)提示,只有超時(shí),很容易誤判成服務(wù)掛了。我的解決辦法是把刷新邏輯寫成一個(gè) PowerShell 腳本,登錄時(shí)自動(dòng)執(zhí)行:$wslIp (wsl hostname -I).Trim().Split( )[0] if ($wslIp) { netsh interface portproxy delete v4tov4 listenport2222 listenaddress0.0.0.0 2$null netsh interface portproxy add v4tov4 listenport2222 listenaddress0.0.0.0 connectport2222 connectaddress$wslIp Write-Output 已刷新轉(zhuǎn)發(fā)規(guī)則 - $wslIp }這個(gè)腳本的關(guān)鍵在于先刪后加,因?yàn)閍dd一個(gè)已存在的規(guī)則會(huì)報(bào)錯(cuò),而delete一個(gè)不存在的規(guī)則也會(huì)報(bào)錯(cuò),所以用2$null把刪除時(shí)的噪音吞掉。腳本執(zhí)行完可以用netsh interface portproxy show all確認(rèn)當(dāng)前規(guī)則指向的地址是不是最新的。說(shuō)到底,這個(gè)腳本解決的不是技術(shù)問(wèn)題,而是把需要人工記住的事變成機(jī)器自動(dòng)做的事。這類自動(dòng)化在小規(guī)模環(huán)境里往往被忽略,覺得手動(dòng)改一下也就半分鐘,但半分鐘乘以一年兩百次,加上每次都要重新回憶命令語(yǔ)法的時(shí)間成本,賬就不一樣了。6. 跑穩(wěn)之后我把配置固化下來(lái)的幾個(gè)習(xí)慣配置這東西,只要不落到文件里,過(guò)三個(gè)月自己都想不起來(lái)當(dāng)初改了什么。我現(xiàn)在給自己定了兩條規(guī)矩:第一,所有非默認(rèn)的改動(dòng)都寫進(jìn)版本管理的 dotfiles 倉(cāng)庫(kù),包括.wslconfig、sshd_config的差異部分、Windows 側(cè)的 SSH config 和那個(gè)刷新腳本;第二,在倉(cāng)庫(kù)根目錄放一個(gè)README,只寫為什么這么配而不寫怎么配,因?yàn)樵趺磁渚W(wǎng)上到處都有,為什么這么配才是三個(gè)月后真正需要的信息。具體到版本管理,sshd_config我不直接覆蓋系統(tǒng)文件,而是維護(hù)一份片段,用的時(shí)候手動(dòng)合并或用配置片段目錄加載。這樣做的好處是系統(tǒng)升級(jí)時(shí)不會(huì)因?yàn)槲募桓倪^(guò)而彈出一堆交互詢問(wèn)。.wslconfig直接在倉(cāng)庫(kù)里放一份完整版本,換機(jī)器時(shí)拷過(guò)去就行,內(nèi)容少,維護(hù)成本低。還有一個(gè)容易被忽略的固化點(diǎn):把驗(yàn)證步驟寫成腳本。我寫了一個(gè)很短的健康檢查腳本,依次檢查 sshd 進(jìn)程、監(jiān)聽地址、Windows 側(cè)轉(zhuǎn)發(fā)規(guī)則、防火墻規(guī)則是否存在,全部通過(guò)就打印一行 OK。每次換機(jī)器或者系統(tǒng)大版本更新后跑一遍,比手動(dòng)一項(xiàng)項(xiàng)確認(rèn)快得多,也避免了以為配好了其實(shí)漏了一步的尷尬。最后分享一個(gè)我在反復(fù)重裝中總結(jié)出來(lái)的小經(jīng)驗(yàn):先跑通最小路徑,再逐層加固。最小路徑指的是:默認(rèn) sshd 配置 密碼登錄 端口轉(zhuǎn)發(fā),能在另一臺(tái)機(jī)器上ssh進(jìn)去看到提示符,這一步通了,后面所有的密鑰、端口改號(hào)、systemd 接管、鏡像模式,都只是在這條已驗(yàn)證的路徑上做替換,任何一步出問(wèn)題都可以立刻回退到上一步可用的狀態(tài)。反過(guò)來(lái),如果一上來(lái)就把所有優(yōu)化項(xiàng)全堆上去,一旦不通,你面對(duì)的是一堆未知變量的組合,排查成本會(huì)翻好幾倍。這套思路我在別的環(huán)境配置上也一直在用,屢試不爽。