Rundeck 是我日常做跨环境作业调度离不开的一个工具它的核心思路很简单把目标机器抽象成“节点”再用 Job 在这些节点上跑命令、传文件。刚上手的时候大家都习惯只接 Linux 节点SSH 一把梭。但公司里 Windows Server 一点不少AD、IIS、文件服务器、数据库还有很多业务系统只跑在 Windows 上。把 Windows 节点纳入 Rundeck 是迟早的事而且真的做起来比想象中要细碎。我接第一批 Windows 节点时先后踩过认证失败、超时、证书信任、PowerShell 脚本执行环境不一致这些坑。这篇文章就把从 Windows 主机配置、Rundeck 节点定义、跑通第一条命令到高频故障排查的完整过程记录下来给要纳管 Windows 节点的同学一份可以直接照着做的方案。如果你手头正好有一批 Windows Server 想统一做配置变更、补丁巡检、远程脚本执行或者你想让 Rundeck 同时管理 Linux 和 Windows 两套环境这篇文章基本能覆盖你需要处理的核心环节。1. 为什么 Windows 节点要用 WinRM 而不是再装一个 Agent1.1 先理解 Rundeck 的节点模型Rundeck 里的“节点”和 IT 里的主机是两个概念。节点是一个抽象对象它至少包含 nodename、hostname、username还可以带上标签、系统类型、环境属性。真正让“节点”能被执行的关键是它绑定的执行器和文件复制器。Node Executor决定 Rundeck 用哪种协议去连远程节点并在上面执行命令。File Copier决定 Rundeck 怎么把本地文件或脚本传到远程节点上。对 Linux 节点来说默认是 SSH。对 Windows 节点来说最合适的原生协议是 WinRM。WinRM 是 Windows 远程管理的标准协议建在 HTTP/HTTPS 之上Windows 系统本身就带这个功能不需要在目标机上再装一个常驻 Agent。在 Rundeck 里配置 Windows 节点本质就是两件事把节点信息写进资源模型然后给它指定 WinRM 执行器和文件复制器。1.2 为什么优先选 WinRM 而不是 SSH 或 Agent先说 Agent 方案。很多自动化平台要求目标机器装一个 Agent 去接收指令这样做确实灵活但在 Windows 环境里推广起来很痛苦。Windows 上的软件安装、杀毒软件拦截、端口开放、Agent 版本升级都是额外成本而且每一台都要人工维护。Rundeck 本身就是无代理模式如果能用 WinRM 直接打通就不用动目标机的软件环境对运维和合规都很友好。再对比 SSH。Windows Server 从 2019 开始支持 OpenSSH Server配置也不复杂Rundeck 的 SSH 执行器可以直接在上面跑命令。如果你的安全基线已经把 SSH 作为唯一远程管理协议那用 SSH 也可以。但在大多数 Windows 环境中WinRM 更原生不需要额外装组件而且和 PowerShell Remoting 共用同一套底层机制对域环境、本地账号、证书认证的支持也更成熟。我实际用下来的结论是除非你所在环境已经有非常成熟的 SSH 堡垒机链路否则在 Rundeck 里管 Windows 节点优先走 WinRM。它的坑虽然也有但剩下的坑基本都是可预期的配置一次之后非常稳定。2. 第一步把 Windows 主机调成能被 WinRM 访问的状态2.1 用管理员 PowerShell 完成 WinRM 基础配置在 Windows 目标机上先以管理员身份打开 PowerShell确认并启动 WinRM 服务。Start-Service WinRM Set-Service WinRM -StartupType Automatic Get-Service WinRM如果这是一台全新机器WinRM 服务默认可能没有启动。不要直接依赖winrm quickconfig虽然它能开服务、开防火墙但它默认只配置 HTTP 5985 监听器很多场景下还不够。如果你是第一次配置把下面这段代码贴进去可以快速把基础认证和服务状态调整好Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $false Restart-Service WinRM这里有一个极其关键的取舍Basic 认证最适合 Rundeck 这种非 Windows 端的客户端但它要求在 HTTPS 传输层上使用。AllowUnencrypted一定要保持$false否则账号密码会以明文方式走 HTTP这在生产环境就是事故。2.2 配置 HTTPS 监听器和自签名证书Rundeck 官方 WinRM 插件在 Basic 认证下强烈建议走 HTTPS 端口 5986。如果目标机已经有域环境且证书体系完善直接用内部 CA 签发的证书。如果只是测试环境也可以临时用自签名证书把链路跑通。在目标机上用管理员 PowerShell 执行$name $env:COMPUTERNAME $cert New-SelfSignedCertificate -DnsName $name -CertStoreLocation Cert:\LocalMachine\My -FriendlyName RundeckWinRM New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $cert.Thumbprint -Force New-NetFirewallRule -DisplayName WinRM HTTPS -Direction Inbound -LocalPort 5986 -Protocol TCP -Action Allow winrm enumerate winrm/config/listener最后一步会列出机器上当前的 WinRM 监听器。如果能看到 Transport 为 HTTPS、端口 5986 的条目说明监听器已经正常。这里提醒一句自签名证书只是让你能把链路打通生产环境不要这么糊弄。真实机房环境建议用公司内部 CA 给每台 Windows 主机签证书然后在 Rundeck 侧的 Java 信任证书库或插件配置里把内网 CA 加进去。2.3 创建专用的 Rundeck 服务账号不要用普通运维人员的交互式账号去接 Rundeck风险太大了。单独建一个服务账号只给这个账号开通远程执行需要的权限平时也没人拿它登服务器。如果这台机器没有加入域本地账号就能用。比较省事的做法是建一个本地管理员账号因为很多巡检命令和 WMI 查询需要管理员权限$pw Read-Host -AsSecureString -Prompt 输入服务账号密码 New-LocalUser -Name rundeck_svc -Password $pw -FullName Rundeck Service Account -AccountNeverExpires Add-LocalGroupMember -Group Administrators -Member rundeck_svc如果是域环境建议在 AD 里建一个专用账号加入目标机器的本地Administrators组。虽然安全上更严格的做法是只加入Remote Management Users组但实际执行中很多脚本会用到本地管理员权限我一般还是把账号加进 Administrators同时严格控制这个账号的使用范围。还有一个容易忽略的点PowerShell 执行策略。如果后面要通过 WinRM 执行.ps1脚本最好在目标机上把执行策略设为RemoteSignedSet-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force不设置的话Rundeck 把脚本传上去之后PowerShell 可能因为策略限制直接拒绝执行报一堆看不懂的错误。2.4 在客户端验证 WinRM 通不通在另一台 Windows 机器上用 PowerShell 测试目标机的 WinRM 服务是否正常Test-WSMan -ComputerName win-app-01 -UseSSL -Credential (Get-Credential)如果有响应说明 WinRM service 活着。再用Enter-PSSession测试凭据能否真正建立会话Enter-PSSession -ComputerName win-app-01 -Credential (Get-Credential) -UseSSL如果自签名证书没有导入信任库Enter-PSSession会直接报证书不受信任。测试环境可以在参数里加一点“信任所有证书”的味道但生产环境千万别这么干。这一步验证完成后Windows 目标机侧的准备就算结束了。3. 在 Rundeck 里定义 Windows 节点3.1 配置节点资源源Rundeck 的节点不是凭空出来的通常由一个“资源模型源”提供。最简单的方式是在项目里配置一个本地 YAML 文件比如resources.yaml。进入项目设置找到 Node Sources添加一个 Local File 源指向这个文件。下面是一份可用的 Windows 节点定义win-app-01: nodename: win-app-01 hostname: 10.10.30.21 username: rundeck_svc osFamily: windows osName: Windows Server 2022 tags: windows,app,prod node-executor: winrm file-copier: winrm winrm-auth-type: basic winrm-use-https: true winrm-trust-self-signed: true winrm-password-storage-path: keys/project/winrm/rundeck-svc注意最后一行。winrm-password-storage-path指向的是 Rundeck Key Storage 里的密钥路径。Rundeck 有内置的凭据存储建议把密码放进 Key Storage不要再 YAML 里写明文密码。具体操作是先在 Key Storage 里创建一个 password 类型的密钥记录比如记录路径keys/project/winrm/rundeck-svc然后把路径填到这里。如果你的 Windows 节点比较多也不要每台都写一份完整的 YAML。可以把公共属性抽出来每台只保留 node-name、hostname 和真正不同的属性。Rundeck 的节点属性是叠加式的项目默认配置和节点自身的属性会合并。3.2 配置项目默认执行器和文件复制器在项目配置里找 Default Node Executor 和 Default Node File Copier把两者都改成 WinRM。Node ExecutorWinRM Node ExecutorFile CopierWinRM Node File Copier然后在项目配置里填 WinRM 的公共参数URLhttps://10.10.30.21:5986/wsman这种形式Auth TypebasicUse HTTPS勾选Shell根据你的习惯选cmd还是powershell如果你一个项目里同时管 Linux SSH 节点和 Windows WinRM 节点不要简单粗暴地在项目级设置 WinRM 默认执行器否则 Linux 节点全都跑不通。这种情况下更稳妥的做法是让项目级保持默认 SSH然后在 Windows 节点自己的 YAML 属性里指定node-executor: winrm和file-copier: winrm。Rundeck 会按节点属性去选择执行器这样可以做到在同一项目里 Linux 和 Windows 共存。这里有一个我踩过的小坑YAML 里winrm-use-https: true这样的布尔值写法有些版本解析严格会要求true不带引号而winrm-password-storage-path的值是字符串不带引号也可以。建议 YAML 文件用实际加载验证一遍避免因为缩进和引号问题刷屏报错。3.3 节点过滤和标签设计给 Windows 节点打标签非常重要。我习惯至少打三类标签系统类型、应用角色、环境。win-app-01: tags: windows,app,prod win-db-01: tags: windows,db,prod win-test-01: tags: windows,app,test这样在 Job 里面就可以非常方便地用节点过滤器选择一批目标比如tags: windows选择所有 Windows 节点tags: windows,db选择 Windows 数据库节点tags: windows,test选择测试环境 Windows 节点这个设计看似简单但后面做批量巡检时省了非常多事。你别想着在 Job 里一台台填主机名那是没理解 Rundeck 的节点模型。4. 第三步用 Job 跑通命令、脚本和文件分发4.1 跑第一条 WinRM 命令新建一个 Job节点选择直接指定win-app-01然后添加一个命令执行步骤。命令内容先写最简单的whoami运行之后输出里会看到 Rundeck 返回了rundeck_svc或者你配置的账号名。这说明 Rundeck 已经通过 WinRM 连上了 Windows 主机而且认证是通过的。如果这一步就报错不用急着往下面走先按第五章的排查表把问题解决。第一条命令跑通是后面所有工作的基础哪怕只是输出一个用户名的成功也代表整条链路已经完整。4.2 在 Job 里切换 PowerShell 执行环境WinRM 的默认 shell 可以是cmd也可以是powershell。很多人第一步跑通了第二步在命令里写Get-Date这种 PowerShell 命令却直接报“不是内部或外部命令”这就是 shell 还是 cmd 的原因。在 WinRM Node Executor 的配置里把 Shell 改成powershell然后命令就可以直接写成Get-Date Get-PSDrive C | Select-Object Used, Free这里要注意一个细节WinRM 会话不是交互式终端你执行的命令是被一次性送给远程主机的。如果你想运行的是一大段 PowerShell 逻辑不要硬塞进一条命令最好用执行脚本的方式可维护性和排错体验都会好很多。4.3 传脚本过去再执行Rundeck 的 Job 步骤里有一个 Script File 步骤可以把 Rundeck 服务端本地的脚本文件上传到远程节点再通过指定的执行器执行。这个步骤非常实用。比如我在 Rundeck 服务端放了一个脚本/opt/rundeck/scripts/check-path.ps1param( [string]$Path ) $ErrorActionPreference Stop $item Get-Item -Path $Path -ErrorAction Stop if ($item.PSIsContainer) { $size (Get-ChildItem $Path -Recurse -File | Measure-Object -Property Length -Sum).Sum } else { $size $item.Length } Write-Output (OK path{0} size{1} -f $item.FullName, $size)在 Job 里添加 Script File 步骤Script File/opt/rundeck/scripts/check-path.ps1Script Args-Path D:\logs\app节点执行器WinRMShell 选择 PowerShell运行后Rundeck 会把check-path.ps1通过 WinRM File Copier 复制到目标机的临时目录然后调用 PowerShell 执行。这样你就不需要担心命令里转义、引号、换行符这些蛋疼问题。脚本本身最好在文件开头就设置$ErrorActionPreference Stop。否则某些命令执行失败时PowerShell 不会中断Rundeck 拿到的退出码可能还是 0Job 页面看起来是绿的实际事没干成。这个习惯一定要养成。4.4 文件复制从 Windows 路径谈到行尾符号如果只是传一个文件没有执行需求可以直接用 Copy File 步骤。它同样依赖 WinRM File Copier在目标机上指定一个绝对路径比如C:\Windows\Temp\package\installer.msi上传完成后再单独用一个命令步骤执行安装。这种方式适合把安装包、配置文件分发到多台 Windows 节点。另外一个很容易踩的坑是脚本行尾符。Linux 上写的脚本通常是 LF 行尾Windows 的 cmd 解析bat/cmd文件时遇到 LF 可能会有诡异行为PowerShell 对 LF 则宽容得多。我的建议是凡是给 Windows 用的脚本优先写成 PowerShell 脚本如果是.bat或.cmd确保以 CRLF 行尾保存。现在很多编辑器右下角都能直接切行尾符批量转一遍也很简单。5. 高频故障与排查实录WinRM 加 Rundeck 这套组合一旦跑通就很稳但第一次搭的时候报错种类还是挺多的。我把最常遇到的问题整理成了一张排查表。现象可能原因处理办法HTTP 401 Unauthorized用户名或密码不对或密钥存储路径没指对检查 Key Storage 里路径是否真的存在重新保存密码连接被拒绝目标机防火墙没开 5986或监听器没起在目标机执行winrm enumerate winrm/config/listener再检查防火墙规则证书不受信任Rundeck 服务端不认自签名证书测试环境打开插件里 Trust Self-Signed 选项生产环境导入 CA报错要求加密传输Basic 认证配在了 HTTP 而不是 HTTPS 上改走 HTTPS或者改用 Kerberos/NTLM 认证Get-Date不是内部或外部命令WinRM shell 还停留在 cmd在 Node Executor 里把 Shell 切换到 PowerShell脚本执行了但没效果PowerShell 错误没抛终止异常脚本开头加$ErrorActionPreference StopYAML 解析失败密码或主机名里有特殊符号缩进错误密码放到 Key StorageYAML 用工具校验一遍我记忆里最深的一个真实案例是某次接入一个 Windows 集群花了一个下午解决“401 但密码明明是对的”问题。后来发现是 Rundeck 从 YAML 里读的不是我预期的那条 Key Storage 路径而是旧版本节点属性里另一个password字段。这个问题说明了一个原则不要在节点 YAML 里直接写密码。只要密码暴露在 YAML 里就会出现“YAML 里的密码改了这个值Key Storage 里却没同步”的撕裂状态。把所有密码都收口到 Key Storage用winrm-password-storage-path引用问题就消失了。还有一个常见问题是 WinRM 并发超时。如果你用标签一次性选择几十台 Windows 节点跑 Job同一时间建立的大量 WinRM 会话可能把目标机的 WSMan 配额打满。Windows 端可以适当调高 WSMan 配额Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB -Value 1024 Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell -Value 64 Set-Item WSMan:\localhost\Shell\MaxShellsPerUser -Value 64调完之后重启 WinRM 服务使之生效。但也要注意配额调太高单台机器容易被不可控的会话拖垮适度就好。6. 从几台到上百台批量纳管 Windows 节点的扩展思路6.1 用脚本批量生成 resources.yaml当你只有三四台 Windows 节点时手工在 YAML 里写一遍问题不大。当你有二三十台、上百台时再手工维护就是给自己挖坑。最简单的方式是在 Rundeck 服务器上写一个小的 shell 脚本按 IP 网段或主机列表批量生成 YAML#!/bin/bash for i in 21 22 23 24; do cat /path/to/resources.yaml EOF win-app-$i: nodename: win-app-$i hostname: 10.10.30.$i username: rundeck_svc osFamily: windows osName: Windows Server 2022 tags: windows,app,prod node-executor: winrm file-copier: winrm winrm-auth-type: basic winrm-use-https: true winrm-trust-self-signed: true winrm-password-storage-path: keys/project/winrm/rundeck-svc EOF done这个脚本的好处是结构统一后续扩容直接加一行 IP 或循环范围就行。注意同一个密钥存储路径是“所有节点共用同一服务账号密码”的假设如果你的环境是一台一个密码那就要在生成时把路径改成按节点区分的keys/project/winrm/nodename。6.2 动态节点源接 AD 或 CMDB如果环境已经有一个机器台账比如 CMDB 能提供 API或者你希望 Windows 节点自动跟着 AD 电脑对象增减就不要再用静态 YAML 文件。Rundeck 支持配置 URL 类型节点源只要你的 API 返回 Rundeck 认识的 JSON 资源模型它就能动态刷新节点列表。这种方式的好处是Rundeck 可以建一个定期抓取外部数据源的节点源Windows 主机从 AD 或 CMDB 里新增、下线后Rundeck 的节点列表自动同步不需要你手动改文件。适合规模大了以后做机器生命周期管理。6.3 把 Windows 节点纳入统一 Job 体系后能做什么Windows 节点纳管完成后收益是全方位的。你可以建一个“Windows 巡检” Job用标签选所有生产 Windows 节点统一跑磁盘空间、服务状态、Windows Update 待安装补丁数检查。也可以建一个“IIS 站点状态” Job批量获取所有 IIS 服务器的站点状态。更重要的是Rundeck 的 ACL 和审计机制可以把所有 Windows 远程操作记录留痕满足安全合规要求。我最喜欢的一套用法是把 Windows 和 Linux 节点塞进同一个工作流先用 SSH 节点跑一个脚本更新配置然后用 WinRM 节点跑 Windows 上关联的部署脚本最后用“等待”步骤做面向全环境的健康检查。跨平台、跨部门、跨环境的作业统一到一个调度平面上这种维护体验和过去一台台登服务器不可同日而语。根据我个人经验第一次把单个 Windows 节点接进 Rundeck 可能确实会花掉一下午但只要把前面那几条 Windows 主机侧的配置和 Rundeck 节点属性摸顺了第二批第三批就是复制粘贴的活。后面再遇到报错也基本都能从“认证”“证书”“端口”“Shell 选择”这四个方向里快速找到答案。最后多啰嗦一句Windows 节点接入不是越复杂越好能用 WinRM 原生能力解决就别想着加 Agent能走 HTTPS 就千万别让明文密码过网络。