Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比
Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比
在 Windows 服务器集群中批量更新 Node.js 代码,核心诉求是“停服务 → 覆盖代码 → 起服务”,同时保留远程
node_modules不动。本文提供两种落地方案:方案一用win_unzip直接解压覆盖,方案二借助共享目录中转分发。两者各有适用场景,方案仅供参考,请根据自己的实际情况使用合适的方案和调整合适的playbook。
背景与前提
假设 zip 包中已确认不包含node_modules,因此无论哪种方案,远程服务器上已有的依赖目录都不会被触碰。服务通过 Windows 服务(NSSM 或其他方式)托管,服务名假设为MyNodeApp,目标部署目录为C:\App\my-node-project。
两种方案共享相同的“停止/启动服务”逻辑,差异在于代码文件如何到达目标机器并覆盖。
方案一:ZIP 直解覆盖(逐机传输)
适用场景
服务器数量较少(10 台以内)、zip 包体积不大(几十 MB 级别)、各机器网络互通性一般、不想额外搭建共享存储。
核心逻辑
Ansible 通过win_copy将 zip 包逐个上传到每台目标机,再用community.windows.win_unzip解压到部署目录。win_unzip原生支持 zip 格式,解压时覆盖同名文件,不会删除目标目录中 zip 里不存在的旧文件。
Playbook 示例
----name:方案一-ZIP直解部署 Node.js 服务hosts:windows_serversvars:service_name:"MyNodeApp"app_dir:"C:\\App\\my-node-project"zip_source:"files/node-app.zip"remote_zip:"C:\\Temp\\node-app.zip"tasks:-name:停止 Node.js 服务ansible.windows.win_service:name:"{{ service_name }}"state:stopped-name:上传代码包到目标机ansible.windows.win_copy:src:"{{ zip_source }}"dest:"{{ remote_zip }}"-name:解压代码包到应用目录(覆盖同名文件)community.windows.win_unzip:src:"{{ remote_zip }}"dest:"{{ app_dir }}"rm:true-name:启动 Node.js 服务ansible.windows.win_service:name:"{{ service_name }}"state:started关键注意点
win_unzip不是幂等模块。官方文档明确说明它“not really idempotent”,每次执行都会解压并报告 changed。这意味着即使代码没变,Playbook 也会显示变更。如果你用--check做预检,结果会始终显示“将发生变更”,这是预期行为,不影响实际正确性。
覆盖不等于同步。win_unzip只覆盖 zip 中存在的文件,不会删除目标目录里 zip 已移除的旧文件。如果你的版本迭代涉及删除文件(比如废弃了某个legacy.js),旧文件会残留在部署目录中。如果需要严格对齐目录内容,方案一不适用,或者需要在解压前额外执行清理逻辑(但保留node_modules)。
大文件传输效率。win_copy通过 WinRM 传输,官方文档指出它“not a very efficient transfer mechanism”,大文件场景下 base64 编码开销明显。如果 zip 包超过 50MB 且节点较多,方案一的耗时会显著上升。
方案二:共享目录分发(一次上传,多次拉取)
适用场景
服务器数量较多(几十台以上)、zip 包较大、所有目标机都能访问同一个 SMB 共享路径(如\\fileserver\deploy$\)。核心思路是控制机只上传一次到共享目录,各目标机从共享目录拉取,避免 WinRM 逐机传输的重复开销。
核心逻辑
先用win_copy将 zip 包上传到共享目录(如果控制机可以直接写入共享路径,也可以用ansible.builtin.copy或win_copy的remote_src变体)。然后在每台目标机上,用 PowerShell 的Copy-Item从共享路径拉取 zip 到本地临时目录,再解压覆盖。
这里需要用到win_copy的remote_src: true参数,让 Ansible 在远程主机上执行复制,源路径和目标路径都在远程端,不经过控制机中转。
Playbook 示例
----name:方案二-共享目录分发部署 Node.js 服务hosts:windows_serversvars:service_name:"MyNodeApp"app_dir:"C:\\App\\my-node-project"share_zip:"\\\\fileserver\\deploy$\\node-app.zip"local_zip:"C:\\Temp\\node-app.zip"tasks:-name:停止 Node.js 服务ansible.windows.win_service:name:"{{ service_name }}"state:stopped-name:从共享目录拉取代码包到本地ansible.windows.win_copy:src:"{{ share_zip }}"dest:"{{ local_zip }}"remote_src:true-name:解压代码包到应用目录(覆盖同名文件)community.windows.win_unzip:src:"{{ local_zip }}"dest:"{{ app_dir }}"rm:true-name:启动 Node.js 服务ansible.windows.win_service:name:"{{ service_name }}"state:started前置条件:上传 zip 到共享目录
方案二需要一个独立的准备步骤:先把 zip 包放到共享路径。这个步骤只执行一次,不依赖目标机组:
-name:上传代码包到共享目录hosts:localhosttasks:-name:复制 zip 到 SMB 共享ansible.builtin.copy:src:"files/node-app.zip"dest:"/mnt/deploy_share/node-app.zip"# 假设控制机已挂载 SMB 共享到 /mnt/deploy_share如果控制机是 Linux 且没有挂载 SMB,也可以用win_copy先传到一台“跳板”Windows 机器,再从那台机器复制到共享目录。具体方式取决于你的环境拓扑。
关键注意点
共享目录的访问权限。执行 Ansible 任务的账户必须对共享路径有读取权限。如果使用域账户运行 WinRM 连接,通常可以直接访问域内共享;如果是本地账户,需要在共享服务器上为该账户单独授权。
remote_src: true的行为。当src和dest都在远程主机上时,win_copy不会经过控制机,直接在目标 Windows 机器内部完成文件复制。但注意src如果是 UNC 路径(\\server\share\file),实际上涉及网络 IO,速度取决于目标机到文件服务器的网络质量。
共享目录方案的“一次上传”优势只有在节点数足够多时才明显。如果只有 3 台机器,方案一的逐机上传和方案二的“上传一次 + 三次拉取”差别不大,反而方案二多了一个共享目录的维护成本。
选型建议
| 维度 | 方案一:ZIP 直解 | 方案二:共享目录 |
|---|---|---|
| 节点数量 | 少(≤10) | 多(>10) |
| ZIP 体积 | 小(<50MB) | 大(>50MB) |
| 网络要求 | 控制机到各节点 WinRM 可达 | 各节点到 SMB 共享可达 |
| 额外基础设施 | 无 | 需要 SMB 共享(或类似文件服务) |
| 传输效率 | 逐机重复传输 | 一次上传,N 次拉取 |
| Playbook 复杂度 | 低 | 中(多一个共享目录准备步骤) |
| 覆盖语义 | 同方案二 | 同方案一 |
推荐决策路径:先数节点。≤10 台且 zip 不大,直接用方案一,简单直接。>10 台或 zip 较大,评估共享目录是否现成可用。如果有现成的部署共享盘(很多企业环境都有),方案二能显著减少部署窗口时间。如果没有,搭建共享目录的运维成本可能抵消收益,此时方案一配合serial分批执行仍是务实选择。
作者简介
Moshow,一名热爱技术与分享的 Technical Lead & 数据科学家 & SpringBoot 专家。CSDN 博客之星 2025 年度 TOP100,Github 4K stars。
持有多项权威认证,包括:GCA 谷歌云架构师认证、Neo4j 数据科学家认证、阿里云 ACP 认证、金蝶高级 CRM 供应链工程师认证、PMP 项目管理认证、CSPM 项目管理专业人员能力评价认证、广东省人工智能训练师认证。