如何在香港服务器运行 Windows Server 时,配置 WSL2 与 Hyper-V 共存环境以支撑开发与测试?

香港机房机柜 18U 位置的那台新上架服务器还散着金属的温度,KVM 上的 BIOS 自检一行行掠过。客户催着要一套“既能跑 Linux 开发栈,又能保留 Windows 内核级测试环境”的方案,而且要能随时快照回滚、低成本扩容。我的第一反应是:WSL2 + Hyper-V 共存——把“日常开发的 Linux 容器与工具链”放 WSL2,把“需要完整 Windows 内核/驱动/域环境的集成测试”放 Hyper-V 虚机。
这活儿听上去简单,真正落地到一台“境外机房、带宽计费、夜间维护窗口”的 Windows Server 上,细节一多,就容易踩坑。下面是我那晚一步步做下来的完整过程、参数、脚本、坑点与补救,尽量写得像你站我旁边一起值夜班。
目标架构
宿主操作系统:Windows Server 2022 Datacenter(桌面体验),单机。
并存虚拟化:
- WSL2(Ubuntu 22.04 LTS):承载 Node/Go/Java 微服务编译、Docker 引擎与测试容器;
- Hyper-V:承载 2~3 台 Windows/SQL 等测试虚机(支持快照、隔离网络、端口转发)。
网络:
- WSL2 默认使用 Hyper-V 后端 NAT(vEthernet(WSL)),可经宿主机端口映射对外;
- Hyper-V 使用Internal + NAT 组合,统一做静态端口映射;
- 避免与机房网段冲突(常见 10.0/172.16/192.168 段)。
硬件与系统参数(实机)
| 项目 | 参数 |
|---|---|
| 机型 | 1U 单路上架式(Supermicro 定制) |
| CPU | Intel Xeon Silver 4314(16C/32T,支持 VT-x/VT-d/SLAT/EPT) |
| 内存 | 128 GB DDR4-3200(4 × 32 GB) |
| 系统盘 | 2 × 960 GB SATA SSD(RAID1) |
| 数据盘 | 2 × 1.92 TB NVMe U.2(RAID0,用于虚拟化与构建缓存) |
| 网卡 | 2 × 10GbE(Bond 到上游 vLAN,实配单线) |
| OS | Windows Server 2022 Datacenter 21H2(桌面体验),英文界面/中文输入 |
若你的宿主是云/虚拟化里的一台 VM(如 ESXi/Hyper-V/Proxmox 上的二级虚拟机),记得开启嵌套虚拟化(下面会写具体开法)。
网络规划(避免冲突)
| 用途 | 虚拟交换机/接口 | 网段 | 说明 |
|---|---|---|---|
| WSL2 后端 | vEthernet (WSL) | 自动(通常 172.22.x.0/20) | WSL2 自建 HNS NAT |
| Hyper-V NAT | NATSwitch (Internal) |
10.80.0.0/24 | 我们自建,给测试 VM 用 |
| 宿主对外 | Intel 10GbE | 机房分配(例如 203.XX.XX.XX/29) | 仅宿主直连,不桥接 |
步骤总览(备忘清单)
- 检查与开启虚拟化:BIOS/宿主/嵌套虚拟化能力确认
- 启用 Windows 角色/功能:Hyper-V、VirtualMachinePlatform、WSL
- 安装与定制 WSL2:发行版、磁盘位置迁移、资源限额、Docker on WSL
- 部署 Hyper-V:创建 Internal + NAT,建立测试 VM、CPU/内存策略
- 网络打通与端口映射:WSL2/VM 的端口统一从宿主暴露,写死映射表
- 编译与测试流水线示例:WSL2 编译 → Hyper-V VM 集成测试
- 常见坑与排障:安装失败、网段冲突、端口不通、I/O 抖动
- 备份与回滚:WSL2 导出/导入、Hyper-V Checkpoint/Export
1. 虚拟化能力检查与开启
1.1 BIOS / 宿主检查
开启 Intel VT-x / VT-d、SR-IOV(有则开)、Hyper-Threading。
Windows 中验证(PowerShell):
# CPU 是否支持虚拟化扩展
Get-CimInstance Win32_Processor |
Select-Object Name, VirtualizationFirmwareEnabled, SecondLevelAddressTranslationExtensions, VMMonitorModeExtensions
确保 VirtualizationFirmwareEnabled=True、SecondLevelAddressTranslationExtensions=True、VMMonitorModeExtensions=True。
1.2 若宿主自己是 VM(嵌套虚拟化)
Hyper-V 上的来宾要再跑 Hyper-V:在宿主 Hyper-V上执行:
Set-VMProcessor -VMName "YourWindowsServer2022VM" -ExposeVirtualizationExtensions $true
VMware ESXi:在 VM 的高级设置里启用 “Expose hardware assisted virtualization to the guest OS”;或在 .vmx 里:
vhv.enable = "TRUE"
2. 启用 Hyper-V / WSL 所需角色与功能
以管理员 PowerShell:
# 2.1 安装 Hyper-V(含管理工具/平台)
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
# 2.2 安装 WSL2 相关平台
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All
# 2.3 确保 Hypervisor 启动
bcdedit /set hypervisorlaunchtype Auto
# 2.4 重启
Restart-Computer
注意:Windows Server 2019 官方只完整支持 WSL1;WSL2 的稳妥方案是 Windows Server 2022 及更高。这篇做法基于 2022。
3. 安装与定制 WSL2
3.1 在线安装(有外网)
# 安装 wsl 组件与默认发行版(Ubuntu),并升级内核
wsl --install
wsl --update
# 如果你要指定发行版
wsl --install -d Ubuntu-22.04
3.2 离线安装(无 Store / 受限环境)
在可上网的机器上下载发行版 appx(或官方 wsl --install --web-download);
拷到服务器后:
Add-AppxPackage .\Ubuntu_22.04.0_x64.appx
3.3 设置 WSL2 为默认版本并初始化
wsl --set-default-version 2
wsl --list --online # 看可用发行版
wsl --list --verbose # 看已安装发行版与版本
3.4 把 WSL 虚拟磁盘移到数据盘(避免系统盘爆)
WSL2 的 VHDX 默认在 C:\Users\<User>\AppData\Local\Packages\...。我会迁到 D:\WSL:
# 以 Ubuntu 为例
wsl --shutdown
wsl --export Ubuntu-22.04 D:\WSL\ubuntu22.tar
wsl --unregister Ubuntu-22.04
wsl --import Ubuntu-22.04 D:\WSL\Ubuntu22 D:\WSL\ubuntu22.tar --version 2
再次 wsl -d Ubuntu-22.04 即可进入新的根文件系统;VHDX 会在 D:\WSL\Ubuntu22\ext4.vhdx。
3.5 限制资源与开启 systemd(便于 Docker)
在 用户目录创建 C:\Users\<User>\.wslconfig:
[wsl2]
memory=16GB
processors=12
swap=16GB
localhostForwarding=true
# Windows Server 2022 的 WSL 新版已支持 systemd
[experimental]
autoMemoryReclaim=gradual
进入 WSL 的 /etc/wsl.conf 开启 systemd:
[boot]
systemd=true
[automount]
options = "metadata,umask=22,fmask=11"
重启 WSL:
wsl --shutdown
wsl
3.6 在 WSL 安装 Docker(示例)
# Ubuntu 22.04 inside WSL
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
sudo systemctl enable --now docker
4. 部署 Hyper-V:交换机、NAT 与虚机
4.1 创建 Internal 交换机 + NAT
# 创建 Internal vSwitch
New-VMSwitch -SwitchName "NATSwitch" -SwitchType Internal
# 给主机侧接口配 IP(比如 10.80.0.1/24)
$if = Get-NetAdapter | Where-Object {$_.Name -like "vEthernet (NATSwitch)"}
New-NetIPAddress -IPAddress 10.80.0.1 -PrefixLength 24 -InterfaceIndex $if.ifIndex
# 创建 NAT
New-NetNat -Name "LabNat" -InternalIPInterfaceAddressPrefix 10.80.0.0/24
4.2 新建测试 VM(示例:Windows Server / SQL)
# 盘放在 NVMe 卷上,利于 I/O
$VMPath = "D:\VMs\WS-Tests"
New-VM -Name "WinTest01" -MemoryStartupBytes 8GB -Generation 2 -SwitchName "NATSwitch" -Path $VMPath
# CPU/内存策略
Set-VMProcessor -VMName "WinTest01" -Count 6 -ExposeVirtualizationExtensions $false
Set-VMMemory -VMName "WinTest01" -DynamicMemoryEnabled $true -MinimumBytes 4GB -MaximumBytes 16GB -Buffer 30
# 加载 ISO,装系统
Add-VMDvdDrive -VMName "WinTest01" -Path "D:\ISO\Windows_Server_2022.iso"
Start-VM "WinTest01"
装好系统后,给 VM 配静态 IP(10.80.0.10/24,网关 10.80.0.1,DNS 指向机房 DNS 或 10.80.0.1 再转发)。
5. 统一的端口映射(从宿主对外暴露)
我习惯所有服务只在宿主开放端口,再根据来源转发到 WSL 或 VM,方便审计与变更。
5.1 WSL2 端口映射(宿主 → WSL)
先查 WSL IP:
wsl hostname -I
# 比如 172.22.0.15
用 netsh 做 portproxy(示例把宿主 80/443 转给 WSL Nginx):
# HTTP
netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=172.22.0.15
# HTTPS
netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=443 connectaddress=172.22.0.15
WSL 的 IP 会变;为省事可以写一个开机/WSL 启动脚本自动刷新映射(见“坑点”章节)。
5.2 Hyper-V VM 端口映射(宿主 → VM)
用 HNS NAT 的静态映射(对 LabNat):
# 把宿主 1433 转给 SQL VM 10.80.0.10:1433
Add-NetNatStaticMapping -NatName "LabNat" -Protocol TCP -ExternalIPAddress "0.0.0.0" -ExternalPort 1433 -InternalIPAddress "10.80.0.10" -InternalPort 1433
5.3 防火墙规则
# 允许 80,443,1433 入站(按需)
New-NetFirewallRule -DisplayName "Lab-HTTP" -Direction Inbound -LocalPort 80 -Protocol TCP -Action Allow
New-NetFirewallRule -DisplayName "Lab-HTTPS" -Direction Inbound -LocalPort 443 -Protocol TCP -Action Allow
New-NetFirewallRule -DisplayName "Lab-SQL" -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow
5.4 我的端口映射表(当晚上线版)
| 宿主端口 | 去向 | 备注 |
|---|---|---|
| 80 / 443 | WSL2: 172.22.0.15 | Nginx 反代微服务 |
| 1433 | VM: 10.80.0.10 | SQL Server 开发库 |
| 2222 | WSL2: 172.22.0.15:22 | 仅内网调试用 |
| 5000-5010 | VM: 10.80.0.20:5000-5010 | UI 自动化测试 |
6. 开发/测试流水线:一条能跑的样例
6.1 在 WSL2 编译并容器化
# 在 WSL2 的 Ubuntu
git clone https://example.com/app.git
cd app
docker build -t registry.example.com/app:dev .
docker push registry.example.com/app:dev
6.2 在 WSL2 部署网关/反代(Nginx 片段)
# /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name _;
location /api/ {
proxy_pass http://10.80.0.10:8080/; # 转向 VM 上的后端
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
proxy_pass http://127.0.0.1:3000/; # 前端容器在 WSL
}
}
6.3 在 Hyper-V VM 跑集成测试(IIS/服务/SQL)
# VM 内,以管理员
Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole -All
# 部署 .NET 服务、连 SQL(略)
7. 常见坑与现场解法(当晚真被坑过)
7.1 wsl --install 提示内核缺失 / WSL2 无法启动
现象:首次进 WSL 报“请安装内核组件”。
解法:
- wsl --update 更新内核;
- 控制面板 → “启用或关闭 Windows 功能”确认 VirtualMachinePlatform 勾选;
- bcdedit /set hypervisorlaunchtype Auto 后重启。
7.2 机房网段冲突,WSL2/Hyper-V NAT“消失”
现象:机房用了 172.16/12 大网段,和 WSL2 默认 172.22.x.x 有时重叠导致不可达。
稳妥做法:避开 10/172/192 常见段给 Hyper-V NAT 用一个独特段(我用 10.80.0.0/24)。
WSL2 的 HNS NAT 段不易持久改,把对外端口终结在宿主,由宿主转发,减少“找 WSL IP”的需要。
7.3 WSL 重启后 IP 变化导致 portproxy 失效
解决脚本(宿主 PowerShell,放计划任务“用户登录/启动时”):
$wslIP = (wsl hostname -I).Trim().Split(" ")[0]
$map = @(
@{ListenPort=80; TargetPort=80},
@{ListenPort=443; TargetPort=443},
@{ListenPort=2222;TargetPort=22}
)
foreach($m in $map){
try{
netsh interface portproxy delete v4tov4 listenport=$($m.ListenPort) listenaddress=0.0.0.0 | Out-Null
}catch{}
netsh interface portproxy add v4tov4 listenport=$($m.ListenPort) listenaddress=0.0.0.0 connectport=$($m.TargetPort) connectaddress=$wslIP
}
7.4 netsh portproxy 只支持 IPv4
如果有 IPv6 入站需求,可在前面加一层 Nginx/HAProxy(Windows 版或容器),或用 Windows 新版 HNS 映射(较复杂,不在此展开)。
7.5 构建 I/O 抖动(Defender 实时扫描)
现象:Docker 构建写大量小文件,Windows Defender 扫描导致 I/O 抖动。
取舍:在合规前提下针对 D:\WSL\ 与 D:\VMs\ 做排除项,或将 Docker layer/cache 放 NVMe。
命令:
Add-MpPreference -ExclusionPath "D:\WSL","D:\VMs"
7.6 VM 时间漂移 / DNS 解析失败
开启 Windows Time 同步、把 VM DNS 指向宿主/机房权威;WSL 端如遇 VPN/DNS 变化,可在 /etc/wsl.conf 里:
[network]
generateResolvConf = true
然后 wsl --shutdown 重新生成 /etc/resolv.conf。
8. 性能与容量:我当晚的配额与观测
| 维度 | 配置 | 说明 |
|---|---|---|
| WSL2 CPU/内存 | 12 vCPU / 16 GB | .wslconfig 限额,编译足够 |
| VM1(SQL) | 6 vCPU / 动态 4–16 GB | OLTP 开发库 |
| VM2(App) | 4 vCPU / 动态 4–12 GB | .NET 服务集成测试 |
| 宿主剩余 | ~10–12 vCPU / 40 GB | 预留给峰值/缓存 |
| 磁盘布局 | WSL/VM 都走 NVMe | 明显降低构建时间 |
| 快照策略 | 每次版本前在 Hyper-V 做 Checkpoint | 回滚快 |
9. 备份与回滚
9.1 WSL2 快照(导出/导入)
wsl --export Ubuntu-22.04 D:\Backup\ubuntu22_$(Get-Date -f yyyyMMdd).tar
# 恢复
wsl --shutdown
wsl --unregister Ubuntu-22.04
wsl --import Ubuntu-22.04 D:\WSL\Ubuntu22 D:\Backup\ubuntu22_20250830.tar --version 2
9.2 Hyper-V 快照/导出
# Checkpoint(快照)
Checkpoint-VM -Name "WinTest01" -SnapshotName "pre-release-$(Get-Date -f yyyyMMddHHmm)"
# 导出 VM
Export-VM -Name "WinTest01" -Path "D:\Export\WinTest01"
10. 完整自动化安装脚本(可作蓝本)
以全新 Windows Server 2022 为前提,执行后重启、装 WSL、建 NAT、放通常用端口。
# run as admin
# --- Features ---
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All
bcdedit /set hypervisorlaunchtype Auto
# --- Reboot once ---
# Restart-Computer
# --- WSL base ---
wsl --install --no-distribution
wsl --update
wsl --set-default-version 2
# --- .wslconfig ---
$wslcfg = @"
[wsl2]
memory=16GB
processors=12
swap=16GB
localhostForwarding=true
[experimental]
autoMemoryReclaim=gradual
"@
$wslcfg | Out-File -FilePath "$env:USERPROFILE\.wslconfig" -Encoding ascii
# --- Hyper-V switch & NAT ---
if (-not (Get-VMSwitch -Name "NATSwitch" -ErrorAction SilentlyContinue)) {
New-VMSwitch -SwitchName "NATSwitch" -SwitchType Internal | Out-Null
}
$if = Get-NetAdapter | Where-Object {$_.Name -like "vEthernet (NATSwitch)"}
if (-not (Get-NetIPAddress -InterfaceIndex $if.ifIndex -ErrorAction SilentlyContinue)) {
New-NetIPAddress -IPAddress 10.80.0.1 -PrefixLength 24 -InterfaceIndex $if.ifIndex | Out-Null
}
if (-not (Get-NetNat -Name "LabNat" -ErrorAction SilentlyContinue)) {
New-NetNat -Name "LabNat" -InternalIPInterfaceAddressPrefix 10.80.0.0/24 | Out-Null
}
# --- Firewall common ports ---
$ports = 80,443,1433,2222
foreach($p in $ports){
if(-not (Get-NetFirewallRule -DisplayName "Lab-$p" -ErrorAction SilentlyContinue)){
New-NetFirewallRule -DisplayName "Lab-$p" -Direction Inbound -LocalPort $p -Protocol TCP -Action Allow | Out-Null
}
}
Write-Host "Base environment done. Install your WSL distro and VMs next."
11. 我会如何验证一切就绪(最短路径)
wsl -l -v 看到发行版是 VERSION=2,进入 wsl 能 systemctl status docker 正常;
docker run hello-world 成功;
Hyper-V 创建一个最小 VM,能拿到 10.80.0.x,ping 10.80.0.1 通;
宿主访问:curl http://127.0.0.1/ → 能看到 WSL Nginx;
远端访问:用你在机房 NAT 上的对外 IP + 80 端口能开站;
Add-NetNatStaticMapping 后,从外网 telnet <publicIP> 1433 通,客户端能连 SQL。
12. FAQ:我常被问到的两个问题
WSL2 与 Hyper-V 会不会冲突?
不会。WSL2 的虚拟化后端就是 Hyper-V。冲突反而多见在:你同时装了第三方虚拟化(旧版 VMware/VirtualBox)并让它们抢 Hypervisor。解决:用新版、或只在这台服务器上坚持 Hyper-V 体系。
要不要桥接 Hyper-V 到物理网卡?
测试/隔离环境我更推荐 Internal + NAT。桥接(External)到机房物理网卡容易牵扯上游交换机 vLAN、ARP 安全策略与 MAC 限制,不如端口映射稳。
结尾:夜里三点的那口气
凌晨 3 点 20 分,我在 KVM 上重启了最后一台测试 VM。Nginx 的健康检查全绿,GitLab Runner 把构建产物推上去,QA 同事从深圳远程连进来,SQL 库也通了。机房空调还在呼呼响,但我知道这套“WSL2 干开发、Hyper-V 做集成”的混合场已经跑顺了——以后不管是临时拉一台 Windows 测试域、还是在 WSL 里开十个容器做回归,都不过就是一行脚本的事。
我把那张手写端口映射表折好塞进笔记本,给机柜门上锁,踩着荔枝角还未亮的街,心里只有一句话:好用的环境,都是一点点熬出来的。
附:最小化自检清单(带打勾)
- BIOS 开启 VT-x/VT-d/SLAT
- Get-CimInstance Win32_Processor 三项为 True
- 安装 Hyper-V / WSL / VirtualMachinePlatform 并重启
- wsl --set-default-version 2 + wsl --update
- .wslconfig 限额与 systemd=true 生效
- NATSwitch + LabNat 正常、VM 能拿 10.80.0.x
- 宿主端口 → WSL/VM 映射可达(80/443/1433/…)
- 备份策略:wsl --export 与 Checkpoint-VM 可用