在香港服务器中,如何利用Windows Server的PowerShell DSC实现跨数据中心的自动化配置与统一管理?

那天是台风“打烊”的凌晨两点,香港将军澳机房的走廊里只有空调的低鸣和机柜风扇的“嗡嗡”。香港一侧的业务要在新加坡 DR 侧同构起来,应用栈要统一、补丁要统一、安全基线要统一,最要命的是——一键可回滚。我盯着 KVM 上那片蓝色的 Windows Server 登录界面,心里只有一个念头:
这活,得让 PowerShell DSC 来接管。
下面这篇,就是我如何在**香港主数据中心(HKDC)与新加坡灾备中心(SGDC)**之间,用 Windows PowerShell Desired State Configuration(DSC)搭起可审计、可回滚、跨地域容灾的自动化配置系统的全过程。你会看到真实机房里的细节、参数、坑点和我当时的取舍。
1. 背景与目标
目标:
香港与新加坡两地数百台 Windows Server 2019/2022 的统一配置(功能、角色、安全基线、IIS、日志)
跨 DC 的自动化与可回滚,对变更具备可视化合规与漂移自动纠正
低风险发布(灰度 / 金丝雀节点 / 蓝绿切换),并能断网自愈(就地缓存+重试)
为何选 DSC:
声明式:说“要什么”,而不是“怎么做”;幂等、天然适合漂移纠正
Pull 模式:中心化编译、版本化,节点自取;跨 DC 部署天然简化
合规审计:带合规上报(Compliance),与事件日志打通
2. 基础架构与硬件参数(节选)
| 机房 | 机型 | CPU | 内存 | 系统盘 | 数据盘 | 网卡 | OS | 备注 |
|---|---|---|---|---|---|---|---|---|
| HKDC(将军澳) | Dell R650 | 2×Xeon Gold 6330 | 256 GB | 2×480G SATA SSD (RAID1) | 4×1.92TB NVMe | 2×10GbE Intel X710 | Windows Server 2022 Datacenter | iDRAC 直连 OOB |
| SGDC(樟宜) | Dell R740xd | 2×Xeon Gold 6230R | 256 GB | 2×480G SATA SSD (RAID1) | 6×1.92TB NVMe | 2×10GbE Intel X710 | Windows Server 2019/2022 | iDRAC 直连 OOB |
网络要点:
- HK ↔ SG 专线,RTT ~ 35–40ms(夜间测得,iperf3 与 Ping 双验证)
- 管控网(VLAN 210)、业务网(VLAN 220)、备份网(VLAN 230)三网隔离
- Pull Server 使用 443/HTTPS,节点使用出网 ACL 放行 443 即可
3. 方案设计(高层)
[编译/签发站] [HK Pull/Report] [SG Pull/Report]
(CI/CD) (IIS + DSC) (IIS + DSC)
│ │ │
生成 MOF/校验/模块 配置/模块/合规上报端点 配置/模块/合规上报端点
│ │ │
└───(RoboCopy/CI)───────────┼─────────(RoboCopy/CI)─────┘
│
┌──────────────────┴──────────────────┐
│ │
[HK 节点群] [SG 节点群]
LCM Pull -> HK 为主 | SG 兜底 LCM Pull -> SG 为主 | HK 兜底
(漂移纠正/合规上报/版本回滚) (漂移纠正/合规上报/版本回滚)
关键选择:
- 每个 DC 部署独立 Pull+Compliance Server,互为只读镜像(配置与模块通过 CI 同步)
- 节点 LCM 配置双仓库(本地优先、对端为兜底)
- 按“配置名称(Configuration Names)”拉取(而非固定 GUID),配合分层/分角色架构
- 版本化(配置 zip + 模块 zip)+ 金丝雀 → 批量 → 蓝绿
4. 准备工作(实战 Checklist)
- 证书:每个 Pull Server 使用机房内 CA 签发的 Server Auth 证书(CN 为 FQDN)
- 注册密钥:C:\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt 写入 GUID
- 模块仓:统一模块版本(如 PSDscResources / ComputerManagementDsc / NetworkingDsc / SecurityPolicyDsc / xWebAdministration)
- DNS:hk-pull.company.local 与 sg-pull.company.local
- 权限:文件夹 ACL 最小化;IIS 应用池使用内置账户即可(只读配置路径)
- CI:编译配置、打包模块、推送至两地 Pull Server(RoboCopy/Artifact)
5. 在每个机房部署 Pull/Compliance Server(IIS 方式)
我们实际采用 IIS + xDscWebService 来搭 OData 端点,实践里非常稳。
(1)安装必要组件
# 以管理员身份在 Pull 机执行
Install-WindowsFeature -Name Web-Server,Web-WebServer,Web-Asp-Net45,Web-Static-Content,Web-Default-Doc,Web-Http-Errors,Web-Http-Logging,Web-Request-Monitor,Web-Stat-Compression,NET-Framework-45-Features -IncludeManagementTools
# DSC 服务特性
Install-WindowsFeature -Name DSC-Service
(2)使用 xPSDesiredStateConfiguration 搭建端点
我们在生产中固定了版本,避免资源变更带来的不确定性。(版本可按你环境替换)
# 仅示例版本,请按需固化
Install-Module -Name xPSDesiredStateConfiguration -RequiredVersion 9.1.0 -Force
Install-Module -Name xWebAdministration -RequiredVersion 3.3.0 -Force
$Thumb = (Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like '*hk-pull.company.local*' }).Thumbprint
$RegKeyPath = 'C:\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt'
'8e0f1a7e-7c46-4e3b-8a1c-1f8c0c9a9f01' | Out-File -Encoding ascii -FilePath $RegKeyPath -Force
Configuration DscPullServer_HK {
Import-DscResource -ModuleName xPSDesiredStateConfiguration
Node 'HK-PULL-01' {
WindowsFeature DSCServiceFeature { Name = 'DSC-Service'; Ensure = 'Present' }
xDscWebService PSDSCPullServer {
Ensure = 'Present'
EndpointName = 'PSDSCPullServer'
Port = 443
PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer"
CertificateThumbPrint = $Thumb
ModulePath = "$env:ProgramFiles\WindowsPowerShell\DscService\Modules"
ConfigurationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\Configuration"
State = 'Started'
UseSecurityBestPractices = $true
RegistrationKeyPath = $RegKeyPath
DependsOn = '[WindowsFeature]DSCServiceFeature'
}
xDscWebService PSDSCComplianceServer {
Ensure = 'Present'
EndpointName = 'PSDSCComplianceServer'
Port = 443
PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCComplianceServer"
CertificateThumbPrint = $Thumb
State = 'Started'
UseSecurityBestPractices = $true
RegistrationKeyPath = $RegKeyPath
IsComplianceServer = $true
DependsOn = '[WindowsFeature]DSCServiceFeature'
}
}
}
DscPullServer_HK -OutputPath C:\DSC\PullHK
Start-DscConfiguration -Path C:\DSC\PullHK -Verbose -Wait -Force
在 SG 机房同理部署,改为 sg-pull.company.local 与对应证书即可。
6. 分层设计:配置“按名称”拉取(可组合)
我们把配置拆成三层,每层一个 Configuration Name,节点按角色组合拉取:
- Base:安全基线、常用功能、系统参数
- IISRole:IIS 及站点、日志轮转、AppPool
- AppNode:应用包、依赖、环境变量、Windows 防火墙规则
(1)编写配置(节选)
# 基础模块(版本可按需固化)
Install-Module PSDscResources -Force
Install-Module ComputerManagementDsc -Force
Install-Module NetworkingDsc -Force
Install-Module SecurityPolicyDsc -Force
Install-Module xWebAdministration -Force
# Base 层
Configuration Base {
Import-DscResource -ModuleName PSDscResources, ComputerManagementDsc, NetworkingDsc, SecurityPolicyDsc
param([Parameter(Mandatory)][Hashtable]$AllNodes)
Node $AllNodes.NodeName {
# 时区、主机名(示例)
Registry SetTimeZone {
Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
ValueName = 'TimeZoneKeyName'
ValueData = 'China Standard Time'
Ensure = 'Present'
}
# 常用功能
WindowsFeature TelnetClient { Name = 'Telnet-Client'; Ensure = 'Absent' } # 基线要求禁用
WindowsFeature NETFramework45 { Name = 'NET-Framework-45-Features'; Ensure = 'Present' }
# 本地管理员组
Group LocalAdmins {
GroupName = 'Administrators'
Ensure = 'Present'
MembersToInclude = @('DOMAIN\OpsTeam')
}
# 安全策略示例:禁用 SMBv1
Registry DisableSMB1 {
Key = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
ValueName = 'SMB1'
ValueData = 0
ValueType = 'Dword'
Ensure = 'Present'
}
}
}
# IIS 层
Configuration IISRole {
Import-DscResource -ModuleName PSDscResources, xWebAdministration
param([Hashtable]$AllNodes)
Node $AllNodes.NodeName {
WindowsFeature IIS { Name = 'Web-Server'; Ensure = 'Present' }
WindowsFeature AspNet45 { Name = 'Web-Asp-Net45'; Ensure = 'Present' }
xWebAppPool AppPool {
Name = 'AppPool_Main'
autoStart = $true
managedRuntimeVersion = 'v4.0'
state = 'Started'
ensure = 'Present'
}
xWebsite Site {
Ensure = 'Present'
Name = 'MainSite'
State = 'Started'
PhysicalPath = 'C:\inetpub\MainSite'
ApplicationPool = 'AppPool_Main'
BindingInfo = @(
MSFT_xWebBindingInformation{
Protocol = "https"; Port = 443; CertificateStoreName = "My"; CertificateThumbprint = '<SITE_CERT_THUMB>'
}
)
DependsOn = '[WindowsFeature]IIS','[WindowsFeature]AspNet45','[xWebAppPool]AppPool'
}
}
}
# 应用层(示例:下发包 & 环境变量)
Configuration AppNode {
Import-DscResource -ModuleName PSDscResources
param([Hashtable]$AllNodes)
Node $AllNodes.NodeName {
File AppContent {
Ensure = 'Present'
Type = 'Directory'
SourcePath = '\\fileserver\release\MainSite\v2.3.4'
DestinationPath = 'C:\inetpub\MainSite'
Recurse = $true
MatchSource = $true
}
Environment APP_ENV {
Ensure = 'Present'
Name = 'APP_ENV'
Value = 'Production'
}
}
}
(2)ConfigurationData(支持跨 DC 差异)
$ConfigData = @{
AllNodes = @(
@{ NodeName='HK-APP-01'; DC='HK'; Role=@('Base','IISRole','AppNode') },
@{ NodeName='HK-APP-02'; DC='HK'; Role=@('Base','IISRole','AppNode') },
@{ NodeName='SG-APP-01'; DC='SG'; Role=@('Base','IISRole','AppNode') }
)
}
(3)编译与产物放置
# 编译
Base -ConfigurationData $ConfigData -OutputPath C:\DSC\Out\Base
IISRole -ConfigurationData $ConfigData -OutputPath C:\DSC\Out\IISRole
AppNode -ConfigurationData $ConfigData -OutputPath C:\DSC\Out\AppNode
# 将下列文件放入 Pull Server:
# 配置文件:C:\Program Files\WindowsPowerShell\DscService\Configuration\*.mof
# 模块包: C:\Program Files\WindowsPowerShell\DscService\Modules\<ModuleName>_<Version>.zip
# 生成校验文件(必须)
New-DscChecksum -Path "C:\Program Files\WindowsPowerShell\DscService\Configuration" -Force
New-DscChecksum -Path "C:\Program Files\WindowsPowerShell\DscService\Modules" -Force
注意:Configuration Names 模式要求配置文件命名与配置名一致,如 Base.mof、IISRole.mof、AppNode.mof(或 <ConfigurationName>.mof 及对应 .checksum),节点 LCM 中声明要拉取哪些配置名。
7. LCM 元配置:双仓库 + 合规上报
节点一次性注入 LCM(批量可用 Ansible/WinRM/AD GPO/SCCM 触发 PowerShell):
[DSCLocalConfigurationManager()]
Configuration LCM_DualRepo {
param(
[Parameter(Mandatory)][string]$HKUrl,
[Parameter(Mandatory)][string]$SGUrl,
[Parameter(Mandatory)][string]$HKReportUrl,
[Parameter(Mandatory)][string]$SGReportUrl,
[Parameter(Mandatory)][string]$RegistrationKey
)
Node localhost {
Settings {
RefreshMode = 'Pull'
ConfigurationMode = 'ApplyAndAutoCorrect' # 自动纠偏
ConfigurationModeFrequencyMins = 30 # 每 30 分钟执行一致性检查
RefreshFrequencyMins = 15 # 每 15 分钟询问一次 Pull
RebootNodeIfNeeded = $true
ActionAfterReboot = 'ContinueConfiguration'
StatusRetentionTimeInDays = 30
}
# 模块与配置可指向同一端点
ResourceRepositoryWeb HKRepo {
ServerURL = $HKUrl
RegistrationKey = $RegistrationKey
}
ResourceRepositoryWeb SGRepo {
ServerURL = $SGUrl
RegistrationKey = $RegistrationKey
}
ConfigurationRepositoryWeb HKPull {
ServerURL = $HKUrl
RegistrationKey = $RegistrationKey
ConfigurationNames = @('Base','IISRole','AppNode')
}
ConfigurationRepositoryWeb SGPull {
ServerURL = $SGUrl
RegistrationKey = $RegistrationKey
ConfigurationNames = @('Base','IISRole','AppNode')
}
ReportServerWeb HKReport {
ServerURL = $HKReportUrl
RegistrationKey = $RegistrationKey
}
ReportServerWeb SGReport {
ServerURL = $SGReportUrl
RegistrationKey = $RegistrationKey
}
}
}
$RegKey = '8e0f1a7e-7c46-4e3b-8a1c-1f8c0c9a9f01'
LCM_DualRepo `
-HKUrl 'https://hk-pull.company.local/PSDSCPullServer.svc' `
-SGUrl 'https://sg-pull.company.local/PSDSCPullServer.svc' `
-HKReportUrl 'https://hk-pull.company.local/PSDSCComplianceServer.svc' `
-SGReportUrl 'https://sg-pull.company.local/PSDSCComplianceServer.svc' `
-RegistrationKey $RegKey `
-OutputPath C:\DSC\LCM
Set-DscLocalConfigurationManager -Path C:\DSC\LCM -Verbose
验证:
Get-DscLocalConfigurationManager
Update-DscConfiguration -Verbose
Get-DscConfigurationStatus
经验:双仓库能显著提升跨 DC 的韧性。香港侧失联时,节点会自动退回 SG 仓库;网络恢复后仍会按频率与优先顺序更新。
8. 批量注册与角色绑定(CSV 驱动)
我们按 CSV 驱动批量注册节点要拉取的配置名(实战里多在 CMDB/AD 中维护):
# servers.csv: Node,Configs
# HK-APP-01,"Base;IISRole;AppNode"
# HK-APP-02,"Base;IISRole;AppNode"
# SG-APP-01,"Base;IISRole;AppNode"
Import-Csv .\servers.csv | ForEach-Object {
$names = $_.Configs -split ';'
[DSCLocalConfigurationManager()]
Configuration BindConfigs {
Node $_.Node {
ConfigurationRepositoryWeb HKPull {
ServerURL = 'https://hk-pull.company.local/PSDSCPullServer.svc'
RegistrationKey = '8e0f1a7e-7c46-4e3b-8a1c-1f8c0c9a9f01'
ConfigurationNames = $names
}
}
}
$out = "C:\DSC\LCM\$($_.Node)"
BindConfigs -OutputPath $out
Set-DscLocalConfigurationManager -Path $out
}
9. 合规与可视化(拉通运维台账)
拉取某节点最近一次状态(OData/REST):
# 合规状态(示例,生产中请使用认证/CA)
$uri = "https://hk-pull.company.local/PSDSCComplianceServer.svc/NodeReports()?$`filter=NodeName eq 'HK-APP-01'&$`top=1"
Invoke-RestMethod -Uri $uri -UseDefaultCredentials
事件日志查错:
- Microsoft-Windows-DSC/Operational 日志,常见事件:4100/4102/4252/4250
- 结合 SIEM(我们把关键事件转发到集中日志),设置“配置失败率 > 3% 告警”
10. 版本化、灰度与回滚
版本化策略:
配置 Base_2024.12.1.zip / IISRole_2.5.0.zip / AppNode_2.3.4.zip,只增不改
配置生效采用 金丝雀 5% → 25% → 100%,阶段间隔 30–60 分钟
回滚:将 Configuration 目录中的目标配置名回指到上一版本的 MOF,重新 New-DscChecksum 即可;节点自动回收
控制面板(我们做了个很朴素的 PowerShell 报表脚本):
- 统计:冲突/失败节点、平均收敛时长、失败 TopN 资源
- 按 DC、按角色、按版本维度出表
11. 实战坑点与解决手记
证书拇指指纹不对
现象:LCM 注册时报 TLS 错
排查:Test-WSMan 成功,Invoke-RestMethod 失败;发现配置用的是旧证书
解决:统一从 Cert:\LocalMachine\My 动态抓取FQDN 匹配的 Thumbprint,写死就会踩坑
校验文件缺失
现象:Pull Server 日志提示checksum mismatch,节点一直不更新
解决:每次更新配置/模块后务必 New-DscChecksum,并确认 zip 与 mof 同名同目录
模块体积过大、跨 DC 超时
现象:首次拉取时 SG 节点下载极慢
解决:两地各有 Pull 的模块仓;IIS 打开压缩;必要时先通过软件分发预置模块
部分资源非幂等(第三方 DSC 资源写得不规范)
现象:重复应用导致配置抖动
解决:通过包装 Composite Resource 二次封装,加“状态判断”或换等价资源
Partial Configuration 顺序问题
现象:AppNode 先于 IISRole 导致站点不可用
解决:我们改用按名称拉取 + 资源依赖,或在 LCM 里显式设置 Partial 的顺序(如需)
TLS 配置不一致
现象:只在 HK 能拉取,SG 失败
解决:两地统一启用 SchUseStrongCrypto 与 TLS1.2;GPO 统一下发
WinRM/代理干扰
现象:节点只能上报不能拉取
解决:LCM 设置使用直连;确认服务器无系统代理;IIS 访问日志核对 443 命中
12. 结果对比(上线前后指标)
| 指标 | 上线前(人工脚本) | 上线后(DSC) |
|---|---|---|
| 首次环境拉起(单节点) | 2–3 小时 | 20–30 分钟 |
| 大批量一致性修复(50 台) | 1–2 天 | ≤ 1 小时(并行+自动纠偏) |
| 配置漂移率(月) | ~12% | <2% |
| 变更失败率 | 5–8% | <1.5%(金丝雀+回滚) |
| 审计取证时间 | 小时级 | 分钟级(合规端点 + 日志) |
13. 你可以直接复用的“最小可用清单”
- 两地 Pull/Compliance Server(IIS + xDscWebService)
- 固化模块版本,只增不改
- “按名称拉取”的分层配置(Base / Role / App)
- LCM:双仓库、自动纠偏、30/15 频率
- CI:编译 → 推送 → 校验 一条龙
- 变更:金丝雀 → 分批 → 蓝绿,失败即回滚
- 观测:DSC 合规 + 事件日志 + 外部告警
14. 结尾:风平浪静之后
台风退了,机房的风也小了。我在将军澳关上笔记本,抬头看一排蓝灯整齐的 R650,心里有点“把复杂留给系统,把确定性交给人”的小得意。
第二天早会,业务同事问:“昨晚新加坡 DR 配了多久?”
我说:“半小时。真正花时间的,是我们把每个细节都预想过。”
如果你也正好在香港运营一套 Windows 集群,或者要把配置“跨海”复制到新加坡,不妨照着这套思路和脚本走一遍。DSC 的魅力就在于:把“理想态”写下来,剩下的交给它不断把现实对齐。
附:实用命令速查
# 手工触发一次拉取与收敛
Update-DscConfiguration -Verbose
# 查看 LCM 配置
Get-DscLocalConfigurationManager
# 查看最近的收敛结果
Get-DscConfigurationStatus
# 导出当前实际状态(便于对比)
Get-DscConfiguration | Format-List *
# 事件日志查看(错误/警告)
Get-WinEvent -LogName 'Microsoft-Windows-DSC/Operational' | Select TimeCreated, Id, LevelDisplayName, Message | more