上一篇 下一篇 分享链接 返回 返回顶部

香港服务器的 Windows Server 2016 如何利用 Nano Server 简化部署并降低资源占用?

发布人:Minchunlin 发布时间:2025-08-25 09:06 阅读量:823


我站在香港荃湾机房 7 楼的过道里沉思,白天客户刚催了第 3 次扩容单:要把静态内容分发的边缘节点从 6 台扩到 16 台,但机柜的 10A 限流和 1U 空间已经把我卡死。那一刻,我做了个决定——把原来跑 Windows Server 2016 Server Core 的“胖子”都瘦成 Nano Server,把每台主机的资源压榨到刚刚好。第二天清晨,我从跳板机上按下了第一台 Nano 虚机的启动键,串起这一连串故事。

1)场景与目标(为什么选 Nano)

业务场景:香港边缘节点,主要职责是静态文件分发(IIS)、轻量 API 转发(HTTP 反向代理不在 Nano 上做,详见坑位说明)、以及容器化的边车小工具(日志打点、健康检查)。

部署目标:在不增加机柜电力与空间的前提下,把边缘节点从 6 台虚机扩到 16 台,同时将启动时间、内存占用和补丁窗口降低到更稳妥的水平。

约束:必须基于 Windows Server 2016 Datacenter 许可(和总部大客合同绑定)、同一代硬件与同一管理工具链(运维团队 PowerShell 为主)。

为什么是 Nano Server(2016)

极小镜像、无本地登录、纯远程管理,系统面更小、攻击面更小。

开箱能适配 IIS(Nano 包)、容器(Windows 容器包)、Hyper-V 来宾增强包等。

对比 Server Core,内存占用/补丁重启窗口/启动时长都有肉眼可见的收益。

2)现场硬件与网络拓扑

2.1 宿主机与存储(实测参数)

机架 服务器 CPU 内存 系统盘 数据盘 网卡 虚化 电力
R11 HPE DL360 Gen10(1U) Xeon Silver 4210R ×1(10C/20T) 64GB ECC DDR4 2×480GB SATA SSD(RAID1) 2×1.92TB NVMe U.2(RAID1 via HBA) 2×10GbE(Intel X722) Hyper-V 2016 单机 ≤ 1.1A @ 230V(实测)

2.2 网络与 VLAN

上联:2×10G LACP 到 TOR,内网 VLAN 200(管理)、201(业务前端),202(日志)。

机房:HK-IX 直连,电信/联通/移动回国专线各一条(BGP 由上游做)。

跳板机:Windows 10 管理站(内网 VLAN 200),装有 ADK/Nano 工具与 Hyper-V 管理器。

3)对比:GUI / Server Core / Nano(我在机房里量过)

以下为我在同批硬件、同 Hyper-V 主机上,用同一代补丁后的实测平均值:

指标 GUI(桌面体验) Server Core Nano Server
首次冷启动到 WinRM 可达 95–120 s 45–70 s 18–28 s
空载常驻内存(单 VM) 1.8–2.2 GB 0.9–1.2 GB 230–320 MB
系统盘占用(C:) 12–16 GB 6–8 GB ~650–900 MB(取决于包)
月均补丁重启窗口(每台) 2–3 次 1–2 次 0–1 次(我们多用镜像再生)
本地可视化维护 无(仅恢复控制台)

注:值随补丁水平、启用包而波动;这里反映的是趋势与量级,不是教科书数据。

4)准备工作(别在机房里现下包)

安装工具(在跳板机)

Windows ADK(用于映像处理)

Nano Server Image Builder(或 NanoServerImageGenerator 模块)

Hyper-V 管理工具 & PowerShell 5.1

准备介质

Windows Server 2016 安装 ISO(与宿主机补丁序列一致)

驱动包:如 Intel X722、NVMe/HBA 离线驱动(.inf/.cat/.sys)

凭据与证书

部署用本地管理员密码(强口令)

业务域的机器证书模板(后面导入到 Nano)

网络计划

分配好 Nano 虚机的固定 IP(VLAN 201)、管理 IP(VLAN 200,如需要双网卡)

预置 DNS、NTP、WinRM 信任列表

5)构建 Nano 映像(一次做好,反复复用)

我把以下脚本固化在 C:\NanoBuild\build.ps1,每次改变量即可再生。

# 1) 准备
$Media = "D:\WS2016ISO"      # 挂载后的 ISO 根目录
$Out   = "C:\NanoOut"        # 输出目录
$Drv   = "C:\DriverPkgs"     # 驱动目录(含子目录)
$Name  = "hk-edge-nano01"
$Pass  = (ConvertTo-SecureString "S3cure!Passw0rd" -AsPlainText -Force)

# 2) 模块(离线或在线导入均可)
Import-Module "$Media\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator.psd1"

# 3) 生成 VHDX(作为 Hyper-V 来宾)
New-NanoServerImage `
  -Edition Datacenter `
  -DeploymentType Guest `
  -MediaPath $Media `
  -TargetPath "$Out\$Name.vhdx" `
  -ComputerName $Name `
  -AdministratorPassword $Pass `
  -InterfaceNameOrIndex "Ethernet" `
  -Ipv4Address "10.201.12.31" -Ipv4SubnetMask "255.255.255.0" -Ipv4Gateway "10.201.12.1" `
  -Ipv4Dns "10.200.0.53,10.200.0.54" `
  -Packages Microsoft-NanoServer-Compute-Package, `
            Microsoft-NanoServer-IIS-Package, `
            Microsoft-NanoServer-Defender-Package, `
            Microsoft-NanoServer-Guest-Package, `
            Microsoft-NanoServer-Containers-Package `
  -EnableRemoteManagementPort

要点

  • Guest-Package 让 Nano 在 Hyper-V 上更顺滑(时间同步/驱动增强)。
  • IIS-Package 直接把 IIS 极简组件塞进镜像,后面不用在线启。
  • Containers-Package 支持 Windows 容器(版本匹配详见“坑位 ③”)。
  • 一次把 IP 与 DNS 固定好,避免首启还要碰碰运气进恢复控制台设网络。

5.1 注入驱动(没有网卡是最常见“翻车”)

如果你像我一样踩过 “X722 在某些固件组合下首启不识别” 的坑,直接把驱动离线注入进 VHDX:

# 挂载 VHDX 为盘符
Mount-VHD -Path "$Out\$Name.vhdx" -PassThru | Get-Disk | Get-Partition | Get-Volume
# 假设挂载到 E:
dism /Image:E:\ /Add-Driver /Driver:$Drv /Recurse /ForceUnsigned
Dismount-VHD -Path "$Out\$Name.vhdx"

6)创建 Hyper-V VM 与 VLAN 绑定(批量化才省心)
$VMName = "hk-edge-nano01"
$Vhdx   = "C:\NanoOut\hk-edge-nano01.vhdx"
$VMSW   = "vSwitch-Prod10G"      # 已桥接到 10G 上联
$VlanId = 201

New-VM -Name $VMName -Generation 2 -MemoryStartupBytes 512MB -VHDPath $Vhdx -SwitchName $VMSW
Set-VMProcessor -VMName $VMName -Count 2
Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $true -MinimumBytes 256MB -MaximumBytes 4GB
# UEFI 安全启动模板
Set-VMFirmware -VMName $VMName -SecureBootTemplate "MicrosoftWindows"
# 绑定 VLAN
Set-VMNetworkAdapterVlan -VMName $VMName -Access -VlanId $VlanId
# 首启
Start-VM $VMName

我把上述动作封装成 New-EdgeNanoVM.ps1,参数化 $VMName/$IP/$VlanId,一晚上能上 10 台不费劲。

7)首启与远程管理(没有 GUI,心要稳)

恢复控制台(串口样式 UI)能看 IP/DNS/主机名等;若网络不通,用方向键改 IP。

跳板机上配置 WinRM 信任并建立会话:

# 跳板机:把 Nano IP 加入 TrustedHosts
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "10.201.12.31" -Force
# 首次登录
Enter-PSSession -ComputerName 10.201.12.31 -Credential (Get-Credential)
# 验证基础服务
Get-ComputerInfo | Select-Object OsName,OsVersion,WindowsInstallDateFromRegistry
Get-Service Winmgmt,w32time,mpssvc | Format-Table -Auto

8)IIS 极简站点上线(静态分发最快落地)

我们用 Nano 的 IIS 做纯静态内容与健康探测端点,不做复杂 URL Rewrite/ARR(见坑位 ②)。

# 进入远程会话
Enter-PSSession -ComputerName 10.201.12.31 -Credential (Get-Credential)

# 8.1 确认 IIS 组件存在(包含于映像包)
Get-WindowsFeature | ? Name -like 'Web-*'

# 8.2 站点目录与权限
New-Item -Path 'D:\sites\edge-static' -ItemType Directory -Force
# 日志目录
New-Item -Path 'D:\logs\iis' -ItemType Directory -Force

# 8.3 配置默认站点指向新目录,启用压缩
Import-Module IISAdministration
Start-IISCommitDelay
Set-IISConfigAttributeValue -ConfigElement (Get-IISConfigSection "system.applicationHost/sites").GetCollection()[0] `
  -AttributeName "id" -AttributeValue 1
Set-ItemProperty 'IIS:\Sites\Default Web Site' -Name physicalPath -Value 'D:\sites\edge-static'

# 压缩(静态)
New-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
  -filter "system.webServer/httpCompression" -name "scheme" -AtElement @{name='gzip';dynamicCompressionLevel=0;staticCompressionLevel=7}

# 8.4 静态文件部署(从跳板机复制)
# 最简单:SMB 只读共享或 PowerShell Copy-Item over PSSession
Copy-Item -Path \\jumpbox\release\static\* -Destination 'D:\sites\edge-static' -Recurse

# 8.5 开放 80/443(Nano 防火墙)
New-NetFirewallRule -DisplayName "IIS HTTP"  -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow
New-NetFirewallRule -DisplayName "IIS HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow

# 8.6 证书导入(PFX)
$pwd = ConvertTo-SecureString "PfxStrong#2024" -AsPlainText -Force
Import-PfxCertificate -FilePath 'D:\certs\edge01.pfx' -CertStoreLocation Cert:\LocalMachine\My -Password $pwd | 
  New-Item IIS:\SslBindings\0.0.0.0!443
Stop-IISCommitDelay

压测速记(1 个 vCPU / 动态内存 256MB–4GB):

指标 Server Core(参考) Nano(IIS)
100 并发静态 64KB 9.4–10.2K RPS 10.6–11.8K RPS
P95 响应(ms) 9.1–11.3 7.8–9.5
IIS 进程常驻内存 120–180 MB 80–120 MB

注:受 CPU 睿频、磁盘缓存影响较大;Nano 主要收益在系统开销更小。

9)把容器当“边车”(日志/探活/小工具)

Nano 带上 Containers-Package 后可作为 Windows 容器宿主(注意版本匹配)。我用容器跑一些独立更新的小工具,像 Filebeat/边车探活(自研 exe 放容器里)。

# 在 Nano 上安装 Docker(Windows 2016)
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
Restart-Computer

# 拉取 nanoserver 基础镜像(版本需与主机 OS Build 对齐)
docker pull mcr.microsoft.com/windows/nanoserver:sac2016
# 运行一个健康探测容器(示例)
docker run --name edge-health -d -p 127.0.0.1:9000:9000 `
  mcr.microsoft.com/windows/nanoserver:sac2016 cmd /c "ping -t 127.0.0.1"

版本坑见下文 ③:OS Build 不一致会导致容器拉起就退出或各种奇怪错误。

10)安全与最少权限(JEA + 防火墙 + 补丁策略)

JEA(Just Enough Administration):只给一线值守一个受限的 PowerShell 端点

# 在跳板机生成 JEA 配置并下发到 Nano
New-PSRoleCapabilityFile -Path C:\JEA\EdgeOps\EdgeOpsRole.psrc -VisibleCmdlets @('Get-Service','Restart-Service','Get-EventLog')
New-PSSessionConfigurationFile -Path C:\JEA\EdgeOps\EdgeOpsEndpoint.pssc `
   -SessionType RestrictedRemoteServer `
   -RoleDefinitions @{ 'HK\edgeops' = @{ RoleCapabilities = 'EdgeOpsRole' } }

# 复制并在 Nano 注册
Copy-Item C:\JEA\EdgeOps\* \\10.201.12.31\C$\ProgramData\JEA\EdgeOps -Recurse
Invoke-Command 10.201.12.31 { Register-PSSessionConfiguration -Name 'EdgeOps' -Path 'C:\ProgramData\JEA\EdgeOps\EdgeOpsEndpoint.pssc' -Force }

防火墙白名单:只开 80/443/WinRM(5985/5986),容器端口尽量 127.0.0.1 映射由 IIS 反代(若需要)。

补丁策略:镜像再生代替在线打补丁。月度做一次新镜像,批量“替换 VM 系统盘”(下节有应急做法)。

11)监控与告警(Nano 一样能看得很细)

# 即时看 CPU/内存/网络
Get-Counter -Counter '\Processor(_Total)\% Processor Time','\Memory\Available MBytes','\Network Interface(*)\Bytes Total/sec' -SampleInterval 2 -MaxSamples 5

# 性能日志(5 分钟采样,落盘 7 天)
logman create counter NanoBaseline -cf counters.txt -si 00:00:10 -o D:\logs\perf\baseline.blg -v mmddhhmm -max 200 -cnf 0:05:00
logman start NanoBaseline

# IIS 日志轮转(计划任务)
schtasks /Create /SC DAILY /TN "RotateIIS" /TR "PowerShell.exe -File D:\scripts\Rotate-IISLogs.ps1" /ST 03:00

12)真实收益(上线 2 周的复盘记录)

维度 变更前(Server Core × 6) 变更后(Nano × 16) 结论
单 VM 空载内存 0.9–1.2 GB 0.23–0.32 GB 平均节省 ~0.8 GB/台
单 VM 冷启动 45–70 s 18–28 s 缩短近一半
同机并发 VM(64GB 宿主) ~40–44 个(受内存水位限制,不建议这么挤) >100 个(实验值) 实际只跑到 16 台,余量充足
发布回滚窗口 10–15 分钟 < 5 分钟(换盘即回滚) 变更风险更可控
故障恢复 RDP 介入(常被占线) 全 PowerShell + 映像再生 夜班负担小了

13)我踩过的坑 & 现场解决过程

网卡驱动缺失(X722)

现象:首启看不到 IP,WinRM 也不通。

解决:把驱动 .inf 用 dism /Add-Driver 离线注入到 VHDX(见 5.1),再启动一次。

经验:凡是机房里装过“特色固件”的主机,一律先注驱动,别赌运气。

IIS 扩展能力的误判(不要把 Nano 当万能 IIS)

现象:同事准备在 Nano 上装 ARR/URL Rewrite 做反代,装不上。

原因:Nano 上可用的 IIS 组件是极简版,很多 GUI 时代的扩展不可用。

解决:反代统一放在上游 Nginx(Linux)或 F5,Nano 仅做静态/探活端点。

经验:在镜像阶段就把“功能边界”画死,上线时少扯皮。

容器版本不匹配(Win 容器“同代”原则)

现象:docker run 一直退出,日志里提示不兼容。

原因:Windows Server 主机 Build 与容器镜像 Tag 不匹配。

解决:锁定 mcr.microsoft.com/windows/nanoserver:sac2016 与宿主一致;制作私有镜像避免漂移。

经验:镜像仓库打“不可变标签”,CI/CD 里强校验。

时间漂移导致 Kerberos 失败

现象:间歇性远程命令报 401/WinRM 身份验证异常。

解决:NTP 明确指向机房 GPS/NTP 服务器,组策略把 MaxPosPhaseCorrection 调大。

经验:边缘节点不在域控附近时,时间就是“血条”。

日志把系统盘慢慢填满

现象:IIS 日志、Docker 日志长时间不清理,C: 预留太小。

解决:日志一律落到 D:,计划任务轮转 + 压缩,C: 只放系统。

经验:Nano 不是免维护,只是避开了 GUI 的那些麻烦。

补丁策略走错路(在线更 vs 镜像再生)

现象:在线打补丁偶发依赖断裂,窗口还得卡流量低谷。

解决:月度再生镜像 + 蓝绿切换(关机、换 VHDX、启动验活)。

经验:把服务器当“一次性工件”,不是“慢慢雕花”的宠物。

14)可复制的发布/回滚脚本(以 VHDX 换盘为核心)

param(
  [Parameter(Mandatory=$true)] [string]$VMName,
  [Parameter(Mandatory=$true)] [string]$NewVhdxPath,
  [switch]$Rollback
)

# 备份现有系统盘
$vm = Get-VM -Name $VMName
$old = (Get-VMHardDiskDrive -VMName $VMName | ? ControllerLocation -eq 0).Path
Stop-VM $VMName -TurnOff -Force

if (-not $Rollback) {
  Rename-Item $old "$old.bak_$(Get-Date -f yyyyMMddHHmmss)"
  Set-VMHardDiskDrive -VMName $VMName -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 0 -Path $NewVhdxPath
} else {
  $bak = Get-ChildItem "$old.bak_*" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
  if (-not $bak) { throw "No backup found." }
  Set-VMHardDiskDrive -VMName $VMName -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 0 -Path $bak.FullName
}

Start-VM $VMName
# 健康检查(HTTP 200)
Start-Sleep 10
$ok = (Invoke-WebRequest -Uri "http://$(($vm.NetworkAdapters | Select -Expand IPAddresses | ? {$_ -match '^\d+\.'})[0])/" -UseBasicParsing -TimeoutSec 5).StatusCode -eq 200
if (-not $ok) { Write-Error "Health check failed."; exit 1 }

15)常见问答(我在群里回答过的)

Q:Nano 没有本地登录,出事了怎么救?
A:有“恢复控制台”可设网络;更稳的是保留旧 VHDX,回滚就是换盘。

Q:能不能把 .NET 应用跑在 Nano 上?
A:.NET Core 可以(要对齐版本);传统 .NET Framework + GUI 组件不行,把它容器化或放 Server Core。

Q:性能一定更好吗?
A:CPU 吞吐有时差距不大,但系统开销更小,意味着密度更高、弹性更好,对边缘节点很关键。

16)结尾:从“胖子”到“轻骑兵”的那一夜

凌晨 5:10,我把最后一台 Nano 节点挂到了 LVS 后面,NOC 大屏上的转发曲线像心电图一样稳。我揉了揉酸胀的脖子,回想起过去几年我们对服务器的认知:总想加功能、开组件、备 GUI,结果越滚越重。Nano Server 逼着我把服务器当工件做——镜像、不可变、可替换。它不是万能药,但在空间、电力、窗口都受限的香港边缘节点,它让我在不加一台主机的情况下,把节点数翻了近三倍,也把夜班的焦虑值拉了下去。

等太阳升起来,地铁首班车载来新一天的工单。我合上笔记本,给白班的小伙发了条消息:

“别怕 Nano 没 GUI,脚本给你备好了。像换灯泡一样换盘,就行。”

——以上,就是我在香港机房把 Windows Server 2016“瘦”成 Nano 的一夜实录。希望你看完之后,能更笃定地把精简当成一种能力,而不是一种奢侈。

附录 A:我实际用到的文件清单(便于你打包)

C:\NanoBuild\
  build.ps1
  \drivers\intel-x722\*.inf|*.sys|*.cat
  \release\static\...
  \scripts\Rotate-IISLogs.ps1
  counters.txt

counters.txt 示例:

\Processor(_Total)\% Processor Time
\Memory\Available MBytes
\TCPv4\Connections Established
\Web Service(_Total)\Current Connections
\Network Interface(*)\Bytes Total/sec

附录 B:Rotate-IISLogs.ps1(7 天保留 + 压缩)

$logPath = "D:\logs\iis"
Get-ChildItem $logPath -Recurse -File -Include *.log |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } |
  ForEach-Object {
    $zip = "$($_.FullName).zip"
    Compress-Archive -Path $_.FullName -DestinationPath $zip -Force
    Remove-Item $_.FullName -Force
  }

如果你准备把这套方案落到你的香港节点上,直接照着章节 5→6→8→10→12走一遍,把脚本参数换成你的环境变量,基本就能一次过。遇到异构硬件、旧固件、多 VLAN 的复杂现场,先离线注驱动、再做网络分层,这是我这几年总结的“保命两步”。祝你在机房里,也能轻装上阵

目录结构
全文